Skip to content

Instantly share code, notes, and snippets.

@romainl
Forked from plugnburn/README.md
Created March 10, 2016 20:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save romainl/be716f5a7fc963d82f79 to your computer and use it in GitHub Desktop.
Save romainl/be716f5a7fc963d82f79 to your computer and use it in GitHub Desktop.
Q.js - DOM nano-framework in 50 lines of JS

Q.js

Q.js is a very simple, tiny and elegant DOM manipulation library that provides the essentials in an original and minimalistic way.

How to obtain

Just download the minified version here or include it directly in your HTML:

<script src="//cdn.rawgit.com/plugnburn/daed387b8c9046f19572/raw/6446b08ca7a78a10242b952b41eaae453d1f32c4/q.min.js"></script>

Usage

Let's look at the general syntax:

Q(selector)([iterationCallback])[.method([args])|([iterationCallback])][.method([args])|([iterationCallback])]...

The single global function, Q(selector), is bound to window and Element.prototype, so you can run it either on all elements or on a single DOM element.

After it, you can start building the call chain. The first call must always be an iteration call that runs the specified iteration callback on each selected element. If you don't need any iteration, you can just omit it (Q(selector)()) and continue building the chain.

Each of the further chain links can be either another iteration call, or one of two predefined methods:

  • .val([newVal]) - if newVal is passed, set a value (see the notes below) on each element matching the selector, otherwise (without parameters) get the first found element value;
  • .on(events, cb) - bind a live event handler to the elements matched by selector (found elements are passed as this to the callback, and the standard Event object - as the only parameter).

Let's check out some examples:

  • Q('h2')(function(el){el.style.borderBottom='2px solid red'}) - underlines each found h2 element with a red 2px-thick line;
  • Q('h1')().val('My cool header') - just sets h1 value to "My Cool header";
  • Q('input[data-field="phone"]')(function(el){el.disabled = true}).val() - disables all found inputs with data-field attribute set to "phone" and returns the first found value from them;
  • Q('.popupBtn')().on('click touchstart', function(e){e.preventDefault();this.style.visibility='hidden';showPopup()}) - hides any .popupBtn element on click or touchstart events and calls some external function;
  • Q('#someId')()()()()()()()()() - effectively does nothing but is still valid.

.val() notes

You can set and get values on any possible DOM element. If it's an <input>, <select> or <textarea> control, its value DOM property will be processed (additionally, if setting on a <select> element, appropriate <option> will be auto-selected). For any other elements that don't have value property, their innerHTML will be read/modified.

Caching

Thanks to the meta-construction nature, Q.js allows you to logically reference your DOM element collections. For example, instead of caching the selector like this:

var headersSel = 'h1, h2, h3, .ownHdr'

and then calling it like this:

Q(headersSel)(...)

you can cache the Q.js constructor like this:

var Headers = Q('h1, h2, h3, .ownHdr')()

and then call it like this:

Headers(...)

Much more convenient. Just a side note: if you intend to use live(constantly changing) element collections with this approach, it's important not to include final empty parentheses in the above example (as they will cause Q.js to cache the node list persistently) but instead use them when actually calling the collection for some operation. So in this case you must cache it like this:

var Headers = Q('h1, h2, h3, .ownHdr') // note the final parentheses are omitted...

and then call it like this:

Headers()(...) //... because they must be here

Simple as that.

!function(w, d, query){
query = function(sel) {
return (function(target) {
function retF(cb, e, l) {
retF.l=[].slice.call(target.querySelectorAll(sel))
if(cb) for(l=retF.l.slice();el=l.shift();) cb(el)
return retF
}
retF.val = function(val, el, l) {
for(l=retF.l.slice();el=l.shift();) {
if(val==null)
return 'value' in el ? el.value : el.innerHTML
else {
if('value' in el) {
el.value = val
if (el.tagName.toLowerCase()==='select') {
el.Q('option')(function(opt){opt.selected = false})
el.Q('option[value="'+val+'"]')(function(opt){opt.selected = true})
}
}
else
el.innerHTML = val
}
}
return retF
}
retF.on = function(evNames, evCb, ev){
for(evNames = evNames.split(' ');ev=evNames.shift();)
w.addEventListener(ev, function(e) {
e.target.closest(sel) && evCb.call(e.target, e)
}, false)
return retF
}
return retF
})(this)
}
w.Q = query.bind(d)
!function(EP){
EP.Q = query
EP.matches || (EP.matches = EP.matchesSelector || EP.webkitMatchesSelector || EP.mozMatchesSelector || EP.msMatchesSelector || function(sel, el) {
el = this;
return [].some.call(d.querySelectorAll(sel), function(e){return e === el})
})
EP.closest = EP.closest || function closest(sel) {
return this.parentNode ?
this.matches(sel) ? this : closest.call(this.parentNode, sel)
: null
}
}(w.Element.prototype)
}(window, document)
!function(f,g,e){e=function(b){return function(e){function c(a,d,h){c.l=[].slice.call(e.querySelectorAll(b));if(a)for(h=c.l.slice();el=h.shift();)a(el);return c}c.val=function(a,d,b){for(b=c.l.slice();d=b.shift();){if(null==a)return"value"in d?d.value:d.innerHTML;"value"in d?(d.value=a,"select"===d.tagName.toLowerCase()&&(d.Q("option")(function(a){a.selected=!1}),d.Q('option[value="'+a+'"]')(function(a){a.selected=!0}))):d.innerHTML=a}return c};c.on=function(a,d,e){for(a=a.split(" ");e=a.shift();)f.addEventListener(e,function(a){a.target.closest(b)&&d.call(a.target,a)},!1);return c};return c}(this)};f.Q=e.bind(g);!function(b){b.Q=e;b.matches||(b.matches=b.matchesSelector||b.webkitMatchesSelector||b.mozMatchesSelector||b.msMatchesSelector||function(b,c){c=this;return[].some.call(g.querySelectorAll(b),function(a){return a===c})});b.closest=b.closest||function c(a){return this.parentNode?this.matches(a)?this:c.call(this.parentNode,a):null}}(f.Element.prototype)}(window,document)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment