This library adds a few convenient niceties on top of the DOM. Think jQuery, but less runtime wrapping and with Haxe in mind. It also offer integration with tink.core.Signal
, but feel free to ignore it if that approach does not appeal to you.
The extra functionality is obtained by wrapping dom nodes in the xdom.Wrapped<T>
abstract (which exists at compile time only) where T
is the concrete node type, e.g. js.html.Node
or js.html.SelectElement
.
There are two easy entrypoints into using xDOM
, both of which become available with import xdom.XDom.*
:
X
: called with any node, you will obtain a wrapped versiondocument
: this is a wrapped document, which when queried for descendants returns wrapped versions again.
The following examples tend to use document
, but all of them would work with any wrapped nodes.
-
type safe querying, i.e.
document.find('form input:checked')[0]
is known to be an input element and so accessing itsvalue
property is done in a type safe manner - with auto completion and code navigation:Also, selectors are validated at compile time:
The resulting collection allows for array access, iteration or more jQuery-esque chaining:
//Ordinary loop: for (e in document.find('form input:checked')) e.checked = false; //With function: document.find('form input:checked').each(e -> e.checked = false); //Or the slightly shorter version document.each('form input:checked', e -> e.checked = false);
-
type safe event handling
The standard
onevent
properties are shadowed in xDOM in favor of a different API:- Call with a
Callback
and get back aCallbackLink
, e.g.document.onclick(function (e) {})
- Call
once
with aCallback
and get back aCallbackLink
, e.g.document.onclick.once (function (e) {})
- Convert to
Signal
, e.g.document.onclick.signal
. Note that this conversion is implicit. Alsomap
,join
,filter
,select
andnextTime
can be called directly, e.g.minusButton.onclick.map(e -> -1)
.
The main "twist" is that the event type is known and that the
target
is anElement
and thecurrentTarget
is of the type of the element you registered your handler with, so e.g.button.onclick(e -> e.currentTarget.disabled = true)
will work properly. - Call with a
-
type safe event delegation
Every of the above events also has a
within
macro that you may call in two different ways:-
With a selector and a callback:
document.onclick.within('[data-tooltip]', function (e) { renderTooltip(e.clientX, e.clientY, e.target.dataset.tooltip); });
The result of this is again a
CallbackLink
-
With just a selector, which produces a
Signal
:document.onchange.within('input[type="radio"][name="font-size"]').map(e -> e.target.value);
In this case we're mapping it to produce a signal of font sizes.
-
With just a selector, chaining with
once
document.onclick.within('button.big.red').once(launchMissiles);
The result of this is again a
CallbackLink
Note that the
currentTarget
of the event will point to the element that was matched (the big red button in this case), not the one where the handler was registered (the document in this case). -
And really, that's it. On top of that, you can just use the plain DOM APIs.
As a rule, this library tries not to abstract away the DOM, just add a bit of type safety and convenience. That said, using element.onclick = function () {...}
is largely discouraged in favor of the more recent element.addEventListener('click', function () {...})
for various reasons. Arguably no harm is done in shadowing it. And doing so results in straight forward autocompletion and short notation for events.