Last active
March 5, 2017 00:47
-
-
Save davemcdermid/ff71709b114959f2e213 to your computer and use it in GitHub Desktop.
A minimalist helper library for simple DOM functions. jQuery style method chaining. Only supports modern browsers, appropriate for mobile.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Q is a lightweight wrapper around basic DOM functions | |
| // It is similar to jQuery, and follows a similar pattern | |
| // for initialisation and chaining functions. | |
| (function () { | |
| var root, | |
| Q, | |
| fn, | |
| simpleRex = /^(#?[\w-]+|\.[\w-.]+)$/, | |
| periodRex = /\./g, | |
| emptyArray = []; | |
| //Defines a global function as an entry point | |
| window.Q = function (selector) { | |
| return new Q(selector, root); | |
| }; | |
| //Copies array values to an object and sets the length property | |
| var copyArray = function (obj, arr) { | |
| obj.length = arr.length; | |
| for (var i = 0; i < arr.length; i++) { | |
| obj[i] = arr[i]; | |
| } | |
| }; | |
| //Constructor for a Q object. Wraps an array of DOM elements in an object to provide helper methods | |
| //Selector parameter can be a single DOM element, an array of DOM elements, a string representing a CSS selector or an HTML string | |
| //Context parameter is a Q object as context for queries | |
| Q = function (selector, context) { | |
| this.context = context; | |
| if (typeof (selector) == 'object') { | |
| //if it doesn't look like an array, put it in an array | |
| if (selector.length === undefined) | |
| selector = [selector]; | |
| copyArray(this, selector); | |
| } | |
| else if (typeof (selector) == 'string') { | |
| selector = selector.trim(); //in case HTML has whitespace | |
| if (selector[0] === '<' && selector[selector.length - 1] === '>' && selector.length >= 3) { | |
| //looks like HTML. Stick the HTML inside a div and get the child element back out, then we have a DOM element | |
| var newElement = document.createElement('div'); | |
| newElement.innerHTML = selector; | |
| copyArray(this, [newElement.firstElementChild]); | |
| return; | |
| } | |
| copyArray(this, this.context.find(selector)); | |
| } else { | |
| copyArray(this, emptyArray); | |
| } | |
| }; | |
| fn = Q.fn = Q.prototype; | |
| //Finds elements matching a CSS selector as decendents of current context | |
| fn.find = function (selector) { | |
| //performance issues of queryselector all bypassed, implemetnation based on | |
| //http://ryanmorr.com/abstract-away-the-performance-faults-of-queryselectorall/ | |
| var _this = this[0]; | |
| if (_this == undefined) | |
| return new Q(emptyArray, this); | |
| if (simpleRex.test(selector)) { | |
| switch (selector[0]) { | |
| case '#': | |
| //only document root can find by ID, won't work for fragments | |
| var element = (_this.getElementById) ? _this.getElementById(selector.substr(1)) : _this.querySelector(selector); | |
| return new Q(element ? [element] : emptyArray, this); | |
| case '.': | |
| return new Q(_this.getElementsByClassName(selector.substr(1).replace(periodRex, ' ')), this); | |
| default: | |
| return new Q(_this.getElementsByTagName(selector), this); | |
| } | |
| } | |
| return new Q(_this.querySelectorAll(selector), this); | |
| }; | |
| //Executes the given callback for each element, with 'this' set as the element | |
| fn.each = function (callback) { | |
| emptyArray.forEach.call(this, function (element) { callback.call(element); }); | |
| return this; | |
| }; | |
| fn.where = function (callback) { | |
| var passed = []; | |
| this.each(function () { | |
| if (callback(this)) { | |
| passed.push(this); | |
| } | |
| }); | |
| return Q(passed); | |
| } | |
| //binds the given callback to the event for all elements | |
| fn.bind = function (event, callback) { | |
| return this.each(function () { | |
| this.addEventListener(event, callback, false); | |
| }); | |
| }; | |
| //Toggles the given className on or off for all elements | |
| //add parameter can be used to force class on (true) or off (false) | |
| fn.toggleClass = function (className, add) { | |
| return this.each(function () { | |
| var classes = this.className.split(' '); | |
| var index = classes.indexOf(className); | |
| if (index >= 0 && add !== true) { | |
| classes.splice(index, 1); | |
| } else if (index === -1 && add !== false) { | |
| classes.push(className); | |
| } | |
| this.className = classes.join(' '); | |
| }); | |
| }; | |
| //Adds the given class to each element | |
| fn.addClass = function (className) { | |
| return this.toggleClass(className, true); | |
| } | |
| //Removes the given class from each element | |
| fn.removeClass = function (className) { | |
| return this.toggleClass(className, false); | |
| } | |
| //Returns true if the first element has the given className | |
| fn.hasClass = function (className) { | |
| var element = this[0]; | |
| if (!element || !element.className) | |
| return false; | |
| var classes = element.className.split(' '); | |
| return classes.indexOf(className) >= 0; | |
| }; | |
| root = new Q(document); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment