Simple as $uck DOM API shorthands. Fast as hell. Mainly to use with latest Chromium (24+). All other browsers compatibility notes in code are JUST FOR REFERENCE, and not all features have such notes. It is not intended to be totally cross-browser, although tries to use only general, well-known and compatible calls.
API IS ABOUT TO CHANGE in future releases, so use at your own risk, or use fixed version
I'm using it with my nw.js projects to get totally rid of jQuery (which is VERY SLOW and bloated for such use), while having convinient tool for querying CSS selectors, DOM traversal and manipulations.
It does not have tests (yet?) and had been published only for demonstration purposes. Can have some dirty comments and untested code and does not ready for production use.
About tests: having the code not being very complicated, it still requires some unit testing. If you can/want to help writing them (and for any other contribution or sugestions) -- use [email protected] to contact me.
P.S. I know that extending core objects' prototypes is far not a 'best practice' and have some side effects, but hey. Accept it or leave it. It's a specific thing for a specific case. And bear in mind that unlike other browsers, Chromium is immune to performance impact introduced by such approach.
Just
npm install safdom
or
git clone https://github.com/offshore/safdom.git
SAFDOM default behavior depends on include method.
The detection based on variable module
tested to be in surrounding context.
See below.
That's a shallow compatibility table for major features; some of browsers' lower versions are not mentioned.
Browser | Version | Feature | Dependency |
---|---|---|---|
Chrome | 24+ | .$class* full |
Element.classList : several arguments in the add/remove/toggle |
Chrome | 8+ | .$class* basic |
Element.classList |
FF | 26+ | .$class* full |
Element.classList : several arguments in the add/remove/toggle |
FF | 13+ | .$clone(true) |
Node.cloneNode(true) |
FF | 9+ | .$has |
Node.contains |
FF | 3.6+ | .$class* basic |
Element.classList |
IE | 10+ | .$class* full |
Element.classList : several arguments in the add/remove/toggle |
IE | 9+ | Too many things became available only with IE 9+ | |
Opera | 11.5+, 15.0+ | .$is |
Element.matches |
Safari | 5.0+ | .$is |
Element.matches |
<script type="text/javascript" src="path/to/dist/safdom.min.js"></script>
or CDN:
<script type="text/javascript" src="//unpkg.com/[email protected]/dist/safdom.min.js"></script>
This just loads SAFDOM and do prototype extension automatically.
If your nw.js project is simple single-window application, you can use the browser method already described above. But there is one more interesting option available, especially if you open (possibly many) windows dynamically. In background context you can do something like this:
var $SAF = require('safdom');
// when you open new window, do magic:
nw.Window.open('someWindow.html', {}, function(someWindow) {
$SAF(someWindow.window);
});
And then, someWindow
's DOM objects got extended.
The only unprefixed thing in SAFDOM is a forEach
method for NodeList and HTMLCollection;
however its done to achieve the same behavior and interface as seen on (Array|Map|Set|...).forEach()
;
the only difference is that it returns list
on which it was called -- for chaining purposes.
list.forEach(callback, thisArg)
: wrapper toArray.prototype.forEach.call(list, callback, thisArg || list)
, returns list;callback
'sthis
is pointing tothisArg
; and arguments are:v
: (currentValue), the current thing being processed in the listk
: (index), the index of the current element being processed in the listlist
: (array), The array thatforEach
is being applied to
Another method for NodeList and HTMLCollection is $slice
, which acts exactly as Array.slice
and can be used to flatten LIVE lists.
list.$slice(begin, end)
: same asArray.prototype.slice.call(list, begin, end)
, returns shallow copied list transformed to plain array
Maybe it's worth switching from forEach
to map
, because latter is more suitable for chaining.
Anyway, I think NodeList and HTMLCollection deserve their own map
and reduce
methods.
x.$has(y)
: same asx.contains(y)
x.$eq(y)
: checkx
identity (===) toy
x.$lt(y)
andx.$gt(y)
: determine thatx
is somewhere before or aftery
in DOM tree, respectively. UsesNode.compareDocumentPosition
, returns0
ornon-0
(seeNode.DOCUMENT_POSITION_*
).
All of those methods do the same things as their jQuery cousins;
except that x
and y
can only be Node
instances (no strings, selectors, arrays or other magic handling).
However you may be interested in $(html|text)(Before|Prepend|Append|After)
-- see below.
They all return x
(except $clone
, which returns (optionally deep) detached copy of x
), so can be easily chained with each other or other manipulation shorthands.
x.$after(y)
* x must have parentx.$append(y)
x.$appendTo(y)
x.$before(y)
* x must have parentx.$clone(deep)
: same asx.cloneNode(deep)
; returns detached copy of xx.$detach()
x.$insertAfter(y)
* y must have parentx.$insertBefore(y)
* y must have parentx.$prepend(y)
x.$prependTo(y)
x.$replace(y)
* x must have parent; remember thatreplaceChild
is destructivex.$clear()
: same as jQuery.empty() -- wipes children
x.$attrH(k)
: same asx.hasAttribute(k)
x.$attrG(k)
: same asx.getAttribute(k)
, pay attention to behavior notes in MDN related to return value and lowercasingx.$attrS(k, v)
: same asx.setAttribute(k, v)
, but returns x for chainingx.$attrD(k, k, ...)
: same asx.removeAttribute(k)
for each argument, but returns x for chainingx.$attrU(k, v)
: $attrD when v == null, $attrS otherwisex.$attrU(kv)
: $attrU for each k and v in kv
x.$propG(k)
: just return x[k]x.$propS(k, v)
: set x[k] to v, return x for chaining
Remember that IE10- does not support the second parameter for .toggle()
and multiple params for .add
or .remove
;
however it may be shimmed later. See docs for Element.classList / DOMTokenList.
x.$classA(v, v, ...)
: add each v toElement.classList
x.$classD(v, v, ...)
: remove each v fromElement.classList
x.$classH(v)
: check thatElement.classList
contains vx.$classT(v, state)
: toggle v inElement.classList
when state == null, otherwise add/remove it for state is true/false, respectively
x.$htmlG()
: just return x.innerHTMLx.$htmlS(v)
: set x.innerHTML to v, return x for chaining
Those are crafty. Allows you to update HTML content inside and around element without side effects (losing events or performance impact);
so they act like x.$before
, x.$prepend
, x.$append
and x.$after
versions with html argument.
See Element.insertAdjacentHTML
for details.
x.$htmlBefore(v)
* x must have parentx.$htmlPrepend(v)
: likex.innerHTML = v + x.innerHTML
x.$htmlAppend(v)
: likex.innerHTML = x.innerHTML + v
x.$htmlAfter(v)
* x must have parent
Note compatibility issues for *G and *S: see reference for Node.textContent
.
x.$textG(k)
: just return x.textContentx.$textS(k, v)
: set x.textContent to v, return x for chaining
Acts like x.$before
, x.$prepend
, x.$append
and x.$after
versions with text argument, automatically creating text Node with supplied
argument and attaching it appropriately.
x.$textBefore(v)
: * x must have parentx.$textPrepend(v)
x.$textAppend(v)
x.$textAfter(v)
: * x must have parent
Note that results of methods for querying multiple things can return not only HTMLCollection, but NodeList as well, and both LIVE and NON-live versions; it can be tricky sometimes, so be sure to check documented behavior for appropriate shorthands.
x.$f(selector)
: same asx.querySelectorAll(selector)
, returns NON-live listx.$s(selector)
: same asx.querySelector(selector)
, returns first match ornull
x.$t(tagName)
: same asx.getElementsByTagName(tagName)
, returns LIVE listx.$c(className)
: same asx.getElementsByClassName(className)
, returns LIVE listx.$is(selector)
: same asx.matches(selector)
, returns boolx.$closest(selector)
: same asx.matches(selector)
, returns closest matching ancestor (including x) or null
x.$idx()
: obtain x index relative to its parent, or -1 if not found or detachedx.$idxOf(y)
: obtain y index relative to x, which should be it's parent, returns -1 if not found, detached, or y is not direct child of xx.$up()
: just returnx.parentElement
x.$fc()
: just returnx.firstElementClild
x.$lc()
: just returnx.lastElementClild
x.$ps()
: just returnx.previousElementSibling
x.$ns()
: just returnx.nextElementSibling
Deprecated for now due to naming problem and tricky nature (uses css4 :scope
and index detection, not mentioned in compatibility table).
Most likely be renamed to $psa
and $nsa
, or even be completely cut:
x.$pss()
: find all siblings between (and including if not null)x.parentElement.firstElementChild
andx.previousElementSibling
, returns NONlive listx.$nss()
: find all siblings between (and including if not null)x.nextElementSibling
andx.parentElement.lastElementChild
, returns NONlive list
x.$cs(pseudoElt)
: same aswindow.getComputedStyle(x, pseudoElt)
x.$rect()
: same asx.getBoundingClientRect()
x.$rects()
: same asx.getClientRects()
See also $meta.px()
/$meta.py()
.
Only minimal set of utilities for now
Note that EventTarget.prototype is not consistently available, so fo browsers like IE, SAFDOM extends only window
, window.document
and Node.prototype
with related methods,
leaving other thing like XMLHttpRequest unmodified.
x.$on(event, handler)
: same asx.addEventListener(event, handler, false)
, but returns x for chainingx.$onc(event, handler)
: capturing version for$on
x.$off(event, handler)
: same asx.removeEventListener(event, handler, false)
, but returns x for chainingx.$offc(event, handler)
: capturing version for$off
Selectors:
$id(id)
: same asdocument.getElementById(id)
, returns thing ornull
$f(selector)
: same asdocument.querySelectorAll(selector)
, returns NON-live list$s(selector)
: same asdocument.querySelector(selector)
, returns first match ornull
$t(tagName)
: same asdocument.getElementsByTagName(tagName)
, returns LIVE list$c(className)
: same asdocument.getElementsByClassName(className)
, returns LIVE list
Minimal (for now) construction shorthands:
$mk(tagName)
: same asdocument.createElement(tagName)
$mkT(textContent)
: same asdocument.createTextNode(textContent)
$mkF()
: same asdocument.createDocumentFragment()
Utility:
It really hurts when you try to determine, which element is actually the main scrolling thing, html
or body
.
Here, take this painkiller.
$meta.se()
: utility to getdocument.scrollingElement
. Includes cross-browser shim via https://github.com/mathiasbynens/document.scrollingElement, which is partially rewritten and optimized to be fully transparent in Chromium. This method caches it's return value, so the steps to detect scrollingElement are not performed oncedocument.readyState
is no moreloading
. I guess it's useful for weird setups only, but if you need to flush this cache, call$meta.seReread()
.$meta.px()
,$meta.py()
: viewport pageXOffset/pageXOffset to use withElement.$rect
/Element.$rects
(in general, this is not the same as scrollLeft/scrollTop, but workaround included; see also https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollY). Note that this functions are affected by scrollingElement cache (see$meta.se()
).
The dictionary of method names is fairly simple, short and easy to remember; screw those who prefer WaitForMultipleObjects
over poll
.
- attr: Attribute-related stuff
- class: Element class names
- html: HTML content manipulation
- prop: Properties
- text: Text content manipulation
- f: Find all elements matching query
- s: Single element that meets query
- t: Search by tag name
- c: Search by class name
- A: Add
- D: Delete
- G: Get
- H: Has
- S: Set
- T: Toggle
- U: Update