Frontend environment evolves repaidly nowadays, modern browsers have already implemented a great deal of DOM/BOM APIs which are good enough. We don't have to learn jQuery from scratch for DOM manipulation or events. In the meantime, thanks to the prevail of frontend libraries like React, Angular and Vue, manipulating DOM directly becomes anti-pattern, jQuery has never been less important. This project summarized most of the jQuery method alternatives in native implementation, with IE 10+ support.
Common selectors like class, id or attribute we can use document.querySelector
or document.querySelectorAll
for substitution. The differences lie in:
document.querySelector
returns the first matched Elementdocument.querySelectorAll
returns all matched Elements as NodeList. It can be converted to Array using[].slice.call
- If no element matched, jQuery would return
[]
whereas these DOM API will returnnull
. Pay attention to Null Pointer Exception.
// query by class
$('.css')
document.querySelectorAll('.css')
// query by id
$('#id')
el.querySelectorAll('#id')
// query by attribute
$('a[target=_blank]')
document.querySelectorAll('a[target=_blank]')
// find
$el.find('li')
el.querySelectorAll('li')
// body
$('body')
document.body
// attr
$el.attr('foo')
e.getAttribute('foo')
// data attribute
$el.data('foo')
// using getAttribute
el.getAttribute('data-foo');
// you can also use `dataset` if only need to support IE 11+
el.dataset['foo']
// siblings
$el.siblings();
[].filter.call(el.parentNode.children, function(child) {
return child !== el;
})
// prev
$el.prev();
el.previousElementSibling
// next
$el.next();
el.nextElementSibling
return the first matched element by provided selector, traversing from current element to document.
jQuery
$el.closest(queryString)
Native
function closest(el, selector) {
const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector;
while (el) {
if (matchesSelector.call(el, selector)) {
return el;
} else {
el = el.parentElement;
}
}
return null;
}
Get the ancestors of each element in the current set of matched elements, up to but not including the element matched by the selector, DOM node, or jQuery object.
jQuery
$el.parentsUntil(selector, filter)
Native
function parentsUntil(el, selector, filter) {
const result = [];
const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector;
// match start from parent
el = el.parentElement;
while (el && !matchesSelector.call(el, selector)) {
if (!filter) {
result.push(el);
} else {
if (matchesSelector.call(el, filter)) {
result.push(el);
}
}
el = el.parentElement;
}
return result;
}
$('iframe').contents()
returns contentDocument
for this specific iframe
// contents
$iframe.contents()
iframe.contentDocument
// query
$iframe.contents().find('.css')
iframe.contentDocument.querySelectorAll('.css')
// set css
$el.css('border-width', 20)
el.style.borderWidth = '20px'
// addClass
$el.addClass(className)
el.classList.add(className);
// removeClass
$el.removeClass(className)
el.classList.remove(className);
// hasClass
$el.hasClass(className)
el.classList.contains(className);
// toggleClass
$el.toggleClass(className)
el.classList.toggle(className);
// get style
$el.css("color");
// NOTE: Known bug, will return 'auto' if style value is 'auto'
const win = el.ownerDocument.defaultView;
// null means not return presudo styles
win.getComputedStyle(el, null).color;
// set style
$el.css({ color: "#ff0011" });
el.style.color = '#ff0011';
Note that if you want to set multiple styles once, you could refer to setStyles method in oui-dom-utils package.
Width and Height are theoretically identical, take Height as example:
// window height
$(window).height();
// without scrollbar, behaves like jQuery
window.document.documentElement.clientHeight
// with scrollbar
window.innerHeight
// document height
$(document).height();
document.documentElement.scrollHeight
// el height
$el.height();
// behaves like jQuery
function getHeight(el) {
const styles = this.getComputedStyles(el);
const height = el.offsetHeight;
const borderTopWidth = parseFloat(styles.borderTopWidth);
const borderBottomWidth = parseFloat(styles.borderBottomWidth);
const paddingTop = parseFloat(styles.paddingTop);
const paddingBottom = parseFloat(styles.paddingBottom);
return height - borderBottomWidth - borderTopWidth - paddingTop - paddingBottom;
}
// accurate to integer(when `border-box`, it's `height`; when `content-box`, it's `height + padding + border`)
el.clientHeight
// accurate to decimal(when `border-box`, it's `height`; when `content-box`, it's `height + padding + border`)
el.getBoundingClientRect().height
// position
$el.position();
{ left: el.offsetLeft, top: el.offsetTop }
// offset
$el.offset();
function getOffset (el) {
let box = el.getBoundingClientRect();
return {
top: box.top + window.pageYOffset - document.documentElement.clientTop,
left: box.left + window.pageXOffset - document.documentElement.clientLeft
}
}
// remove
$el.remove()
el.parentNode.removeChild(el);
// get text
$el.text()
el.textContent
// set text
$el.text(string)
el.textContent = string
// get html
$el.html()
el.innerHTML
// set html
$el.html(htmlString)
el.innerHTML = htmlString
append child element after the last child of parent element
jQuery
$el.append("<div id='container'>hello</div>")
Native
let newEl = document.createElement('div');
newEl.setAttribute('id', 'container');
newEl.innerHTML = 'hello';
el.appendChild(newEl)
jQuery
$el.prepend("<div id='container'>hello</div>")
Native
let newEl = document.createElement('div')
newEl.setAttribute('id', 'container')
newEl.innerHTML = 'hello'
el.insertBefore(newEl, el.firstChild)
Insert a new node before the selected elements
jQuery
$newEl.insertBefore(queryString);
Native
newEl.insertBefore(document.querySelector(queryString))
Insert a new node after the selected elements
jQuery
$newEl.insertAfter(queryString);
Native
function insertAfter(newEl, queryString) {
var parent = document.querySelector(queryString).parentNode;
if (parent.lastChild === newEl) {
parent.appendChild(newEl);
} else {
parent.insertBefore(newEl, parent.nextSibling);
}
},
// scrollTop
$(window).scrollTop()
(document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop;
Replace with fetch and fetch-jsonp
For a complete replacement with namespace and delegation, refer to https://github.com/oneuijs/oui-dom-events
// bind an event with on
$el.on(eventName, eventHandler);
el.addEventListener(eventName, eventHandler);
// unbind an event with off
$el.off(eventName, eventHandler);
el.removeEventListener(eventName, eventHandler);
jQuery
$(el).trigger('custom-event', {key1: 'data'});
Native
if (window.CustomEvent) {
var event = new CustomEvent('custom-event', {detail: {key1: 'data'}});
} else {
var event = document.createEvent('CustomEvent');
event.initCustomEvent('custom-event', true, true, {key1: 'data'});
}
el.dispatchEvent(event);
$('#my-input').val()
document.querySelector('#my-input').value
// get index of e.currentTarget between `.radio`
$(e.currentTarget).index('.radio')
[].indexOf.call(document.querySelectAll('.radio'), e.currentTarget)
// isArray IE10+
$.isArray(range)
Array.isArray(range)
// trim
$.trim(string)
String.trim(string)
// extend, use object.assign polyfill https://github.com/ljharb/object.assign
$.extend({}, defaultOpts, opts)
Object.assign({}, defaultOpts, opts)
// contains
$.contains(el, child);
el !== child && el.contains(child);
Latest ✔ | Latest ✔ | 10+ ✔ | Latest ✔ | 6.1+ ✔ |
MIT