UNPKG

min-dom

Version:

A minimal dom utility toolbelt

565 lines (467 loc) 11.6 kB
function _mergeNamespaces(n, m) { m.forEach(function (e) { e && typeof e !== 'string' && !Array.isArray(e) && Object.keys(e).forEach(function (k) { if (k !== 'default' && !(k in n)) { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); }); return Object.freeze(n); } /** * Flatten array, one level deep. * * @template T * * @param {T[][] | T[] | null} [arr] * * @return {T[]} */ const nativeToString = Object.prototype.toString; const nativeHasOwnProperty = Object.prototype.hasOwnProperty; function isUndefined(obj) { return obj === undefined; } function isArray(obj) { return nativeToString.call(obj) === '[object Array]'; } /** * Return true, if target owns a property with the given key. * * @param {Object} target * @param {String} key * * @return {Boolean} */ function has(target, key) { return nativeHasOwnProperty.call(target, key); } /** * Iterate over collection; returning something * (non-undefined) will stop iteration. * * @template T * @param {Collection<T>} collection * @param { ((item: T, idx: number) => (boolean|void)) | ((item: T, key: string) => (boolean|void)) } iterator * * @return {T} return result that stopped the iteration */ function forEach(collection, iterator) { let val, result; if (isUndefined(collection)) { return; } const convertKey = isArray(collection) ? toNum : identity; for (let key in collection) { if (has(collection, key)) { val = collection[key]; result = iterator(val, convertKey(key)); if (result === false) { return val; } } } } function identity(arg) { return arg; } function toNum(arg) { return Number(arg); } /** * Assigns style attributes in a style-src compliant way. * * @param {Element} element * @param {...Object} styleSources * * @return {Element} the element */ function assign(element, ...styleSources) { const target = element.style; forEach(styleSources, function(style) { if (!style) { return; } forEach(style, function(value, key) { target[key] = value; }); }); return element; } /** * Set attribute `name` to `val`, or get attr `name`. * * @param {Element} el * @param {String} name * @param {String} [val] * @api public */ function attr(el, name, val) { // get if (arguments.length == 2) { return el.getAttribute(name); } // remove if (val === null) { return el.removeAttribute(name); } // set el.setAttribute(name, val); return el; } /** * Taken from https://github.com/component/classes * * Without the component bits. */ /** * toString reference. */ const toString = Object.prototype.toString; /** * Wrap `el` in a `ClassList`. * * @param {Element} el * @return {ClassList} * @api public */ function classes(el) { return new ClassList(el); } /** * Initialize a new ClassList for `el`. * * @param {Element} el * @api private */ function ClassList(el) { if (!el || !el.nodeType) { throw new Error('A DOM element reference is required'); } this.el = el; this.list = el.classList; } /** * Add class `name` if not already present. * * @param {String} name * @return {ClassList} * @api public */ ClassList.prototype.add = function(name) { this.list.add(name); return this; }; /** * Remove class `name` when present, or * pass a regular expression to remove * any which match. * * @param {String|RegExp} name * @return {ClassList} * @api public */ ClassList.prototype.remove = function(name) { if ('[object RegExp]' == toString.call(name)) { return this.removeMatching(name); } this.list.remove(name); return this; }; /** * Remove all classes matching `re`. * * @param {RegExp} re * @return {ClassList} * @api private */ ClassList.prototype.removeMatching = function(re) { const arr = this.array(); for (let i = 0; i < arr.length; i++) { if (re.test(arr[i])) { this.remove(arr[i]); } } return this; }; /** * Toggle class `name`, can force state via `force`. * * For browsers that support classList, but do not support `force` yet, * the mistake will be detected and corrected. * * @param {String} name * @param {Boolean} force * @return {ClassList} * @api public */ ClassList.prototype.toggle = function(name, force) { if ('undefined' !== typeof force) { if (force !== this.list.toggle(name, force)) { this.list.toggle(name); // toggle again to correct } } else { this.list.toggle(name); } return this; }; /** * Return an array of classes. * * @return {Array} * @api public */ ClassList.prototype.array = function() { return Array.from(this.list); }; /** * Check if class `name` is present. * * @param {String} name * @return {ClassList} * @api public */ ClassList.prototype.has = ClassList.prototype.contains = function(name) { return this.list.contains(name); }; /** * Clear utility */ /** * Removes all children from the given element * * @param {Element} element * * @return {Element} the element (for chaining) */ function clear(element) { var child; while ((child = element.firstChild)) { element.removeChild(child); } return element; } /** * Closest * * @param {Element} el * @param {string} selector * @param {boolean} checkYourSelf (optional) */ function closest(element, selector, checkYourSelf) { var actualElement = checkYourSelf ? element : element.parentNode; return actualElement && typeof actualElement.closest === 'function' && actualElement.closest(selector) || null; } var componentEvent = {}; var bind$1, unbind$1, prefix; function detect () { bind$1 = window.addEventListener ? 'addEventListener' : 'attachEvent'; unbind$1 = window.removeEventListener ? 'removeEventListener' : 'detachEvent'; prefix = bind$1 !== 'addEventListener' ? 'on' : ''; } /** * Bind `el` event `type` to `fn`. * * @param {Element} el * @param {String} type * @param {Function} fn * @param {Boolean} capture * @return {Function} * @api public */ var bind_1 = componentEvent.bind = function(el, type, fn, capture){ if (!bind$1) detect(); el[bind$1](prefix + type, fn, capture || false); return fn; }; /** * Unbind `el` event `type`'s callback `fn`. * * @param {Element} el * @param {String} type * @param {Function} fn * @param {Boolean} capture * @return {Function} * @api public */ var unbind_1 = componentEvent.unbind = function(el, type, fn, capture){ if (!unbind$1) detect(); el[unbind$1](prefix + type, fn, capture || false); return fn; }; var event = /*#__PURE__*/_mergeNamespaces({ __proto__: null, bind: bind_1, unbind: unbind_1, 'default': componentEvent }, [componentEvent]); /** * Module dependencies. */ /** * Delegate event `type` to `selector` * and invoke `fn(e)`. A callback function * is returned which may be passed to `.unbind()`. * * @param {Element} el * @param {String} selector * @param {String} type * @param {Function} fn * @param {Boolean} capture * @return {Function} * @api public */ // Some events don't bubble, so we want to bind to the capture phase instead // when delegating. var forceCaptureEvents = [ 'focus', 'blur' ]; function bind(el, selector, type, fn, capture) { if (forceCaptureEvents.indexOf(type) !== -1) { capture = true; } return event.bind(el, type, function(e) { var target = e.target || e.srcElement; e.delegateTarget = closest(target, selector, true); if (e.delegateTarget) { fn.call(el, e); } }, capture); } /** * Unbind event `type`'s callback `fn`. * * @param {Element} el * @param {String} type * @param {Function} fn * @param {Boolean} capture * @api public */ function unbind(el, type, fn, capture) { if (forceCaptureEvents.indexOf(type) !== -1) { capture = true; } return event.unbind(el, type, fn, capture); } var delegate = { bind, unbind }; /** * Expose `parse`. */ var domify = parse; /** * Tests for browser support. */ var innerHTMLBug = false; var bugTestDiv; if (typeof document !== 'undefined') { bugTestDiv = document.createElement('div'); // Setup bugTestDiv.innerHTML = ' <link/><table></table><a href="/a">a</a><input type="checkbox"/>'; // Make sure that link elements get serialized correctly by innerHTML // This requires a wrapper element in IE innerHTMLBug = !bugTestDiv.getElementsByTagName('link').length; bugTestDiv = undefined; } /** * Wrap map from jquery. */ var map = { legend: [1, '<fieldset>', '</fieldset>'], tr: [2, '<table><tbody>', '</tbody></table>'], col: [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'], // for script/link/style tags to work in IE6-8, you have to wrap // in a div with a non-whitespace character in front, ha! _default: innerHTMLBug ? [1, 'X<div>', '</div>'] : [0, '', ''] }; map.td = map.th = [3, '<table><tbody><tr>', '</tr></tbody></table>']; map.option = map.optgroup = [1, '<select multiple="multiple">', '</select>']; map.thead = map.tbody = map.colgroup = map.caption = map.tfoot = [1, '<table>', '</table>']; map.polyline = map.ellipse = map.polygon = map.circle = map.text = map.line = map.path = map.rect = map.g = [1, '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">','</svg>']; /** * Parse `html` and return a DOM Node instance, which could be a TextNode, * HTML DOM Node of some kind (<div> for example), or a DocumentFragment * instance, depending on the contents of the `html` string. * * @param {String} html - HTML string to "domify" * @param {Document} doc - The `document` instance to create the Node for * @return {DOMNode} the TextNode, DOM Node, or DocumentFragment instance * @api private */ function parse(html, doc) { if ('string' != typeof html) throw new TypeError('String expected'); // default to the global `document` object if (!doc) doc = document; // tag name var m = /<([\w:]+)/.exec(html); if (!m) return doc.createTextNode(html); html = html.replace(/^\s+|\s+$/g, ''); // Remove leading/trailing whitespace var tag = m[1]; // body support if (tag == 'body') { var el = doc.createElement('html'); el.innerHTML = html; return el.removeChild(el.lastChild); } // wrap map var wrap = Object.prototype.hasOwnProperty.call(map, tag) ? map[tag] : map._default; var depth = wrap[0]; var prefix = wrap[1]; var suffix = wrap[2]; var el = doc.createElement('div'); el.innerHTML = prefix + html + suffix; while (depth--) el = el.lastChild; // one element if (el.firstChild == el.lastChild) { return el.removeChild(el.firstChild); } // several elements var fragment = doc.createDocumentFragment(); while (el.firstChild) { fragment.appendChild(el.removeChild(el.firstChild)); } return fragment; } var domify$1 = domify; /** * @param { HTMLElement } element * @param { String } selector * * @return { boolean } */ function matches(element, selector) { return element && typeof element.matches === 'function' && element.matches(selector) || false; } function query(selector, el) { el = el || document; return el.querySelector(selector); } function all(selector, el) { el = el || document; return el.querySelectorAll(selector); } function remove(el) { el.parentNode && el.parentNode.removeChild(el); } export { assign as assignStyle, attr, classes, clear, closest, delegate, domify$1 as domify, event, matches, query, all as queryAll, remove }; //# sourceMappingURL=index.esm.js.map