Skip to content

Instantly share code, notes, and snippets.

@davemcdermid
Last active March 5, 2017 00:47
Show Gist options
  • Select an option

  • Save davemcdermid/ff71709b114959f2e213 to your computer and use it in GitHub Desktop.

Select an option

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.
// 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