Skip to content

Instantly share code, notes, and snippets.

@kotarok
Last active February 17, 2017 13:31
Show Gist options
  • Save kotarok/ea2f2fc4f90a695972ae84a53c475e4c to your computer and use it in GitHub Desktop.
Save kotarok/ea2f2fc4f90a695972ae84a53c475e4c to your computer and use it in GitHub Desktop.
Array based tiny jQuery subset alternative with minimum number of methods + useful functionalities. Less than 1kb gzipped.
(function(window) {
'use strict';
var camelize = function(str) {
str = str.replace(/^\s+/, '');
return str.replace(/[\W]+([a-z0-9])(\w*)/ig, function(match, p1, p2) {
return p1.toUpperCase() + p2;
});
};
/**
* Main function to create NqObj object.
* @global
* @param {String} q [description]
* @param {Function} f Optional. If it's given, passed to forEach.
* @return {NqObj}
*/
var nq = function(q, f) {
if (typeof f === 'function') {
return new NqObj(q).forEach(function(el, i) {f(el, i);});
} else {
return new NqObj(q);
}
};
/**
* NqObj
* @global
* @constructor
* @param {String|Array|NodeList|HTMLElement} q Given elements are added to NqObj.
*/
var NqObj = function(q) {
return q? this.push(q): this;
};
var nqp = nq.fn = NqObj.prototype = [];
/**
* Returns new NqObj with specified element. Just like NodeList.item()
* @param {Number} i Index number.
* @return {Object} New NqObj only has specified element.
*/
nqp.item = function(i) {
return nq(this[i]);
};
/**
* Just like jQ.find(). Find matched elements from descendant of original collection.
* @param {String|Array|NodeList|HTMLElement} q CSS selector or Element or Elements
* @return {Object} New NqObj.
*/
nqp.find = function(q) {
var newNq = new NqObj();
this.forEach(function(el) {
newNq.push(el.querySelectorAll(q));
});
return newNq;
};
/**
* Add new elements to collection.
* @param {Array|NodeList|NqObj|HTMLElement|window|document} q CSS selector or Element or Elements
* @return {Object} self
*/
nqp.push = function(q) {
var r = [];
if (q instanceof HTMLElement || q === window || q === document) {
r = [q];
} else if (typeof q === 'string') {
[].forEach.call(document.querySelectorAll(q),function(el){
r.push(el);
});
} else {
r = q;
}
r.forEach(function(el) {
[].push.call(this, el);
}.bind(this));
return this;
};
/**
* delayEach is forEach with interval
* @param {Function} f function to iterate
* @param {Number} interval Interval in millisecond
* @return {Object} self
*/
nqp.delayEach = function(f, interval) {
var i = 0;
if (interval) {
var timer = setInterval(function() {
f(this[i], i);
i++;
if (i >= this.length) {
clearInterval(timer);
}
}.bind(this), interval);
} else {
this.forEach(function(el) {
f(el);
});
}
return this;
};
/**
* (methodName, className[, className...][, interval])
* @private
* @param {String} method Method Name
* @param {String | Number} args className[, className...][, interval]
* @return {Object} Self
*/
nqp.oprtClass_ = function(method, args){
args = [].slice.call(args);
var interval = (typeof args[args.length - 1] === 'number') ? args.pop() : 0;
this.delayEach(function(el) {
el.classList[method].apply(el.classList, args);
}, interval);
return this;
};
/**
* Shorthand for HTMLElement##classList#add, and interval
* @param {String} className[, className...][, interval]
* @return {Object} Self
*/
nqp.addClass = function() {
return this.oprtClass_('add', arguments);
};
/**
* Shorthand for HTMLElement##classList#remove, and interval
* @param {String} className[, className...][, interval]
* @return {Object} Self
*/
nqp.removeClass = function() {
return this.oprtClass_('remove', arguments);
};
/**
* Shorthand for cHTMLElement#lassList#toggle, and interval
* @param {String} className[, condition function...][, interval]
* @return {Object} Self
*/
nqp.toggleClass = function(className, condition, interval) {
if (typeof condition === 'number') {
interval = condition;
condition = undefined;
}
return this.oprtClass_('toggle', [className, condition, interval]);
};
/**
* Shorthand for HTMLElement#classList#contains
* @param {String} className className
* @return {Object} Self
*/
nqp.hasClass = function(className) {
return this[0].classList.contains(className);
};
var treatMultipleEventListner_ = function(el, eventType, listenerType, fn) {
eventType.split(/,\s*/).forEach(function(e) {
el[listenerType + 'EventListener'](e, fn);
});
};
/**
* Shorthand for Element#addEventListener
* @param {Event} e Event type
* @param {Function} f Event handler function
* @return {Object} Self
*/
nqp.on = function(eventType, f) {
this.forEach(function(el) {
treatMultipleEventListner_(el, eventType, 'add', f.bind(el));
});
return this;
};
/**
* Shorthand for Element#removeEventListener
* @param {Event} e Event type
* @param {Function} f Function to remove
* @return {Object} Self
*/
nqp.off = function(eventType, f) {
this.forEach(function(el) {
treatMultipleEventListner_(el, eventType, 'remove', f.bind(el));
});
return this;
};
nqp.one = function(eventType, f) {
this.forEach(function(el) {
var eventName;
var fnWrapper = function() {
f.apply(el);
treatMultipleEventListner_(el, eventType, 'remove', fnWrapper);
};
treatMultipleEventListner_(el, eventType, 'add', fnWrapper);
});
return this;
};
nqp.defineScrollObserver = function(eventName, condition) {
var offset = condition = condition || 0;
// var getScrollContainer_ = function(el) {
// var scrollContainer = $(el)[0].find(function(el) {
// return el.scrollHeight > el.offsetHeight || el.scrollWidth > el.offsetWidth;
// });
// return (scrollContainer instanceof HTMLBodyElement)? window: scrollContainer;
// };
if (typeof condition === 'number' && Math.abs(condition) > 1) {
condition = function(el, rect, vh) {
return vh > rect.top + offset;
};
} else if (typeof condition !== 'function') {
condition = function(el, rect, vh) {
return vh * offset > rect.top;
};
}
this.forEach(function(el) {
nq(window).on('load,scroll,resize', function() {
var rect = nq(el).rect(), vh = window.innerHeight;
if (condition(el, rect, vh)) {
var e = new Event(eventName);
e.rect = rect;
e.vh = vh;
el.dispatchEvent(e);
}
});
});
return this;
};
nqp.parents = function() {
var els = [], el = this[0];
while ((el = el.parentNode)) {
if(el instanceof HTMLElement){
els.push(el);
}
}
return nq(els);
};
nqp.parent = function() {
var el = this[0].parentNode;
return (el instanceof HTMLElement)? nq(el): undefined;
};
/**
* Porperty accessor
* @param {String} n Property name
* @param {String | Number} v Value to set
* @return {Object} Self
*/
nqp.prop = function(n, v) {
// this condition statement must check if it's string.
// Because there's a case passing '' to reset property v.
if (typeof v === 'string') {
this.forEach(function(el) {
el[n] = v;
});
return this;
} else {
return this[0][n];
}
};
/**
* Short hand for HTMLElement#innreHTML
* @param {String} v HTML source string
* @return {Object | String} Self | innerHTML value
*/
nqp.html = function(v) {
if (v !== undefined) {
this.forEach(function(el) {
el.innerHTML = v;
});
return this;
} else {
return this[0].innderHTML;
}
};
/**
* Short hand for HTMLElement#set/getAttribute
* @param {String} n Attribute name
* @param {String | Number} v Attribute value
* @return {Object | String} Self | Attribute value
*/
nqp.attr = function(n, v) {
if (v !== undefined) {
this.forEach(function(el) {
el.setAttribute(n, v);
});
return this;
} else {
return this[0].getAttribute(n);
}
};
/**
* Short hand for HTMLElement#dataset
* @param {String} n Data set name
* @param {String | Number} v Data content to set
* @return {Object | String} Self | Date content
*/
nqp.data = function(n, v) {
n = camelize(n);
if (v !== undefined) {
this.forEach(function(el) {
el.dataset[n] = v;
});
return this;
} else {
return this[0].dataset[n];
}
};
/**
* Form element value accessor
* @param {String | Number} v Form field value
* @return {Object | String} Self | Form field value
*/
nqp.val = function(v) {
if (v !== undefined) {
this.forEach(function(el) {
el.value = v;
});
return this;
} else {
if (this[0].value) {
return this[0].value;
}
}
};
/**
* Style attribute accessor
* @param {String} n CSS property name | Object of CSS rulesets
* @param {String | Number} v Form field value
* @return {Object | String} Self | Style property value
*/
nqp.style = function(n, v) {
if (v !== undefined) {
return this.forEach(function(el) {
el.style[camelize(n)] = v;
});
} else if (n instanceof Object) {
return this.forEach(function(el) {
Object.keys(n).forEach(function(prop) {
el.style[camelize(prop)] = n[prop];
});
});
} else {
// return getComputedStyle(this[0])[camelize(n)];
return this[0].style[camelize(n)];
}
};
/**
* Shorthand for .insertAdjacentHTML
* @param {String} position (beforebegin, afterbegin, beforeend, afterend)
* @param {HTMLElement | NqObj} target Target element
* @return {[type]} [description]
*/
nqp.insert = function(position, insertee) {
this.forEach(function(el) {
if (typeof insertee === 'string') {
el.insertAdjacentHTML(position, insertee);
} else if (insertee instanceof HTMLElement) {
el.insertAdjacentElement(position, insertee);
}
});
return this;
};
/**
* Remove elemet themselves
* @return {undefined}
*/
nqp.remove = function() {
this.forEach(function(el) {
el.parentNode.removeChild(el);
});
};
/**
* Get DOMRect
* https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIDOMClientRect
* @return {Object} DOMRect object
*/
nqp.rect = function() {
var rect = this[0].getBoundingClientRect();
rect.x = this[0].offsetLeft;
rect.y = this[0].offsetTop;
return rect;
};
/**
* Uniques collection.
* @return {Object} self
*/
nqp.unique = function() {
return this.filter(function(value, index, el) {
return el.indexOf(value) === index;
});
};
/**
* Reverse collection order
* @name reverse
* @method
* @public
* @return {Object} self
*/
/**
* Work as Array#slice
* @name slice
* @method
* @public
* @return {Object} self
*/
['reverse','slice', 'filter']
.forEach(function(m) {
nqp[m] = function() {
return nq([][m].apply(this, arguments));
};
});
window.nq = nq;
})(this);
!function(a){"use strict";var b=function(a){return a=a.replace(/^\s+/,""),a.replace(/[\W]+([a-z0-9])(\w*)/gi,function(a,b,c){return b.toUpperCase()+c})},c=function(a,b){return"function"==typeof b?new d(a).forEach(function(a,c){b(a,c)}):new d(a)},d=function(a){return a?this.push(a):this},e=c.fn=d.prototype=[];e.item=function(a){return c(this[a])},e.find=function(a){var b=new d;return this.forEach(function(c){b.push(c.querySelectorAll(a))}),b},e.push=function(b){var c=[];return b instanceof HTMLElement||b===a||b===document?c=[b]:"string"==typeof b?[].forEach.call(document.querySelectorAll(b),function(a){c.push(a)}):c=b,c.forEach(function(a){[].push.call(this,a)}.bind(this)),this},e.delayEach=function(a,b){var c=0;if(b)var d=setInterval(function(){a(this[c],c),c++,c>=this.length&&clearInterval(d)}.bind(this),b);else this.forEach(function(b){a(b)});return this},e.oprtClass_=function(a,b){b=[].slice.call(b);var c="number"==typeof b[b.length-1]?b.pop():0;return this.delayEach(function(c){c.classList[a].apply(c.classList,b)},c),this},e.addClass=function(){return this.oprtClass_("add",arguments)},e.removeClass=function(){return this.oprtClass_("remove",arguments)},e.toggleClass=function(a,b,c){return"number"==typeof b&&(c=b,b=void 0),this.oprtClass_("toggle",[a,b,c])},e.hasClass=function(a){return this[0].classList.contains(a)};var f=function(a,b,c,d){b.split(/,\s*/).forEach(function(b){a[c+"EventListener"](b,d)})};e.on=function(a,b){return this.forEach(function(c){f(c,a,"add",b.bind(c))}),this},e.off=function(a,b){return this.forEach(function(c){f(c,a,"remove",b.bind(c))}),this},e.one=function(a,b){return this.forEach(function(c){var e=function(){b.apply(c),f(c,a,"remove",e)};f(c,a,"add",e)}),this},e.defineScrollObserver=function(b,d){var e=d=d||0;return"number"==typeof d&&Math.abs(d)>1?d=function(a,b,c){return c>b.top+e}:"function"!=typeof d&&(d=function(a,b,c){return c*e>b.top}),this.forEach(function(e){c(a).on("load,scroll,resize",function(){var f=c(e).rect(),g=a.innerHeight;if(d(e,f,g)){var h=new Event(b);h.rect=f,h.vh=g,e.dispatchEvent(h)}})}),this},e.parents=function(){for(var a=[],b=this[0];b=b.parentNode;)b instanceof HTMLElement&&a.push(b);return c(a)},e.parent=function(){var a=this[0].parentNode;return a instanceof HTMLElement?c(a):void 0},e.prop=function(a,b){return"string"==typeof b?(this.forEach(function(c){c[a]=b}),this):this[0][a]},e.html=function(a){return void 0!==a?(this.forEach(function(b){b.innerHTML=a}),this):this[0].innderHTML},e.attr=function(a,b){return void 0!==b?(this.forEach(function(c){c.setAttribute(a,b)}),this):this[0].getAttribute(a)},e.data=function(a,c){return a=b(a),void 0!==c?(this.forEach(function(b){b.dataset[a]=c}),this):this[0].dataset[a]},e.val=function(a){return void 0!==a?(this.forEach(function(b){b.value=a}),this):this[0].value?this[0].value:void 0},e.style=function(a,c){return void 0!==c?this.forEach(function(d){d.style[b(a)]=c}):a instanceof Object?this.forEach(function(c){Object.keys(a).forEach(function(d){c.style[b(d)]=a[d]})}):this[0].style[b(a)]},e.insert=function(a,b){return this.forEach(function(c){"string"==typeof b?c.insertAdjacentHTML(a,b):b instanceof HTMLElement&&c.insertAdjacentElement(a,b)}),this},e.remove=function(){this.forEach(function(a){a.parentNode.removeChild(a)})},e.rect=function(){var a=this[0].getBoundingClientRect();return a.x=this[0].offsetLeft,a.y=this[0].offsetTop,a},e.unique=function(){return this.filter(function(a,b,c){return c.indexOf(a)===b})},["reverse","slice","filter"].forEach(function(a){e[a]=function(){return c([][a].apply(this,arguments))}}),a.nq=c}(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment