import isArray from 'lodash/isArray';
import isString from 'lodash/isString';
import includes from 'lodash/includes';
/**
* Utilities for working with the Document Object Model (DOM).
* @module DOMUtils
*/
const DOMUtils = {
/**
* Returns the tag name of the specified element.
* @static
* @function tag
* @param {Element} el - The element to check the tag of.
* @param {Boolean} [lowercase=true] - Whether to normalize the tag name for comparison as lowercase.
* @example
* DOMUtils.tag(document.querySelector('.foo'));
* @returns {String} - The tag of the specified element.
*/
tag(el, lowercase = true) {
return (lowercase) ? el.nodeName.toLowerCase() : el.nodeName;
},
/**
* Returns the element(s) matching the specified selector.
* @static
* @function el
* @param {String} selector - The selector of the element to query.
* @example
* DOMUtils.el('.foo');
* @returns {Element|Array} - The element or array of elements matching the selector.
*/
el(selector) {
const els = document.querySelectorAll(selector);
if (els.length > 1) {
return els;
} else {
return els[0];
}
},
/**
* Returns the element's parent or the closest ancestor matching the specified selector if provided.
* @static
* @function parent
* @param {String|Element} item - The selector or element to find the parent of.
* @param {String} [parentSelector] - The selector of the ancestor to find.
* @example
* DOMUtils.parent('.foo', '.bar');
* @returns {Element} - The parent of the item.
*/
parent(item, parentSelector) {
const el = (isString(item))
? DOMUtils.el(item)
: item;
if (!el) { return undefined; }
if (parentSelector) {
const allParents = document.querySelectorAll(parentSelector);
let currentParent = el.parentNode;
while (currentParent && !includes(allParents, currentParent)) {
currentParent = currentParent.parentNode;
}
return currentParent;
} else {
return el.parentNode;
}
},
/**
* Returns the attribute of the specified element.
* @static
* @function attr
* @param {String|Element} item - The selector or element to query.
* @param {String} attribute - The attribute to query.
* @example
* DOMUtils.attr('.foo', 'data-baz');
* @returns {String} - The value of the attribute.
*/
attr(item, attribute) {
const el = (isString(item))
? DOMUtils.el(item)
: item;
if (!el) { return; }
return el.getAttribute(attribute);
},
/**
* Adds a class to the specified element.
* @static
* @function addClass
* @param {String|Element} item - The selector or element to add the class to.
* @param {String} className - The class to add.
* @example
* DOMUtils.addClass(evt.currentTarget, 'foo');
* @returns {void}
*/
addClass(item, className) {
const el = (isString(item))
? DOMUtils.el(item)
: item;
if (!el) { return; }
if (el.classList) {
el.classList.add(className);
} else {
el.className += ` ${className}`;
}
},
/**
* Removes a class from the specified element.
* @static
* @function removeClass
* @param {String|Element} item - The selector or element to remove the class from.
* @param {String} className - The class to remove.
* @example
* DOMUtils.removeClass(evt.currentTarget, 'foo');
* @returns {void}
*/
removeClass(item, className) {
const el = (isString(item))
? DOMUtils.el(item)
: item;
if (!el) { return; }
if (el.classList) {
el.classList.remove(className);
} else {
el.className = el.className.replace(new RegExp(`(^|\\b)${className.split(' ').join('|')}(\\b|$)`, 'gi'), ' ');
}
},
/**
* Checks if an element has a specified class.
* @static
* @function hasClass
* @param {String|Element} item - The selector or element to check.
* @param {String} className - The class to check for.
* @example
* DOMUtils.hasClass(evt.currentTarget, 'foo');
* @returns {Boolean} - Whether the element has the class or not
*/
hasClass(item, className) {
const el = (isString(item))
? DOMUtils.el(item)
: item;
if (!el) { return false; }
if (el.classList) {
return el.classList.contains(className);
} else {
return includes(el.className.split(' '), className);
}
},
/**
* Get the height of the specified element.
* @static
* @function height
* @param {String|Element} item - The selector or element to find the height of.
* @param {Boolean} [includeMargin=true] - Whether to include the margins in the calculation effectively getting the outerHeight.
* @param {Boolean} [includePadding=true] - Whether to include the padding in the calculation.
* @example
* DOMUtils.height('.foo');
* @returns {Number} - The height of the element.
*/
height(item, includeMargin = true, includePadding = true) {
const el = (isString(item))
? DOMUtils.el(item)
: item;
if (!el) { return 0; }
const {
marginTop,
marginBottom,
paddingTop,
paddingBottom
} = getComputedStyle(el);
let height = el.offsetHeight;
if (includeMargin) {
height += (parseInt(marginTop, 10) + parseInt(marginBottom, 10));
}
if (!includePadding) {
height -= (parseInt(paddingTop, 10) + parseInt(paddingBottom, 10));
}
return height;
},
/**
* Get the width of the specified element.
* @static
* @function width
* @param {String|Element} item - The selector or element to find the width of.
* @param {Boolean} [includeMargin=true] - Whether to include the margins in the calculation effectively getting the outerWidth.
* @param {Boolean} [includePadding=true] - Whether to include the padding in the calculation.
* @example
* DOMUtils.width('.foo');
* @returns {Number} - The width of the element.
*/
width(item, includeMargin = true, includePadding = true) {
const el = (isString(item))
? DOMUtils.el(item)
: item;
if (!el) { return 0; }
const {
marginLeft,
marginRight,
paddingLeft,
paddingRight
} = getComputedStyle(el);
let width = el.offsetWidth;
if (includeMargin) {
width += (parseInt(marginLeft, 10) + parseInt(marginRight, 10));
}
if (!includePadding) {
width -= (parseInt(paddingLeft, 10) + parseInt(paddingRight, 10));
}
return width;
},
/**
* Removes element(s) from the DOM.
* @static
* @function remove
* @param {String|Element|Array} items - The selector or element(s) to remove from the DOM.
* @example
* DOMUtils.remove('.class-to-remove');
* @returns {void}
*/
remove(items) {
const el = (isString(items))
? DOMUtils.el(items)
: items;
if (!el) { return; }
if (isArray(el)) {
el.each(thisEl => {
thisEl.remove();
});
} else {
el.remove();
}
}
};
export default DOMUtils;