
Show HN: SentinelJS – Detect new DOM nodes using CSS selectors - andres
https://github.com/muicss/sentineljs
======
rstacruz
Nice work! I've also done a similar thing
([https://github.com/rstacruz/onmount](https://github.com/rstacruz/onmount)),
and it's really useful in managing behaviors in a page.

For those wondering what this is for, I've written a document with guidelines
in building scalable non-SPA apps leveraging this:
[http://ricostacruz.com/rsjs](http://ricostacruz.com/rsjs)

~~~
Lord_Zero
Is it just me or do these approaches seem hacky? If you were using something
like Angular or React wouldn't you plug into lifcycle events that are exposed
by the library like Abgular's OnInit?

~~~
rstacruz
if you were using something like Angular or React to manage your full page,
then by all means run with it! RSJS is an alternative to that that's more
suited for "traditional" sites rendered on the server (eg, traditional Rails
and Rails-like setups).

------
have_faith
Don't we have mutation observers for this exact purpose? or is there a benefit
to this approach?

~~~
moheeb
I have used mutation observers to do exactly this. They are very simple to
use.

~~~
andres
Can you give an example of how you would do this with mutation observers?

~~~
mikerybka
[https://developer.mozilla.org/en-
US/docs/Web/API/MutationObs...](https://developer.mozilla.org/en-
US/docs/Web/API/MutationObserver)

------
superasn
How does it accomplish it in 680 bytes? I tried reading the code but don't
understand it.. there is a lot of stuff about css and keyframes that i don't
get.

~~~
projectant
It adds a stylesheet to accept new stylerules. For every element set (CSS
selector) you want to watch it adds a new stylerule that makes a no-op
animation happen (transform:none -> transform:none). Every time such a node
that matches your selector is added to the document, this animation is
triggered. When this happens, Sentinel publishes an event to whatever handlers
you ask it to, by listening to the 'AnimationStart' event emitted when an
animation happens. Pretty clever, but simple, and I think this is not the
first source to suggest this approach for watching DOM modification. Note, the
unminified dist/sentinel.js is not 682 bytes!

~~~
gcb0
it will break when browser optimize a null animation, just like tons of code
used to detect if some ad was in view in the past broke.

edit: already happened once with this very project. they started animating
clip, but then ie optimised it and now they use outlineColor.

------
robocat
Detailed write up of how it is implemented, and why mutation events are not
suitable:

[http://www.backalleycoder.com/2012/04/25/i-want-a-
damnodeins...](http://www.backalleycoder.com/2012/04/25/i-want-a-
damnodeinserted/)

~~~
kccqzy
But aren't mutation events deprecated since forever? Is the newer replacement
mutation observer also unsuitable?

~~~
paulirish
MutationObserver could handle this case instead of the animationstart hack
yeah. Here's one impl I found: [https://github.com/azu/wait-for-
element.js/blob/master/lib/w...](https://github.com/azu/wait-for-
element.js/blob/master/lib/wait-by-observer.js) (also rstacruz's referenced
onmount lib also uses MutationObserver/matches())

(Mutation events deprecated since ~5 years ago, yes. )

------
c-smile
The DOM element arrival detection is based on animation events in Sentinel.
Sentinel adds global style rules that cause newly born elements to start
animation and so generate "animationstart" event that the library catches.

That appears if not hacky then a bit heavyweight - 2 passes of layout needed
at least.

But I do understand the need for this. In sciter I've added special CSS
property aspect that takes name of the function to execute when element gets
mounted into the DOM and before any layout on it:

    
    
        my-component { 
         aspect: MyComponent url(script/my-components.js); 
         color:blue;
        }  
    

and script:

    
    
        function MyComponent() {
          this // is the element just added to the DOM.
        }

~~~
andres
Do you have an example of this technique? I just tried using your technique
but I couldn't get it to work.

------
yeasayer
What's the difference between SentinelJS and DOM Mutation Observers?

I glanced at the source code, it seems this lib is using some kind of hack
based on CSS animations. Why not just use Mutation Observers, which are the
standard approved API to get the job done?

~~~
onion2k
Mutation observers are very, _very_ bad for performance. They're still useful
for some things as they give you a lot of detail about what happened, but if
you're just looking for a change on a specific set of elements this library
seems like a better bet.

~~~
paulirish
> Mutation observers are very, very bad for performance.

No, they are not. _Mutation events_ are tragic for performance and are
deprecated (Chrome will make that pretty obvious in the console). The Mutation
Observer was designed to have reasonable performance characteristics. It's
still a very active listener, but rarely is it a bottleneck.

more: [https://developers.google.com/web/updates/2012/02/Detect-
DOM...](https://developers.google.com/web/updates/2012/02/Detect-DOM-changes-
with-Mutation-Observers)

------
ry_ry
I might be missing a very obvious use-case here (3rd party scripts, perhaps?)
but surely if a new DOM node is inserted, you most likely actioned it yourself
- so why not set the innerHTML at that point?

~~~
myhf
There is some discussion about the motivation here:

[http://www.backalleycoder.com/2012/04/25/i-want-a-
damnodeins...](http://www.backalleycoder.com/2012/04/25/i-want-a-
damnodeinserted/)

~~~
atmin
And implementation by the same author:
[https://github.com/csuwildcat/SelectorListener](https://github.com/csuwildcat/SelectorListener)

------
spand
If the author is watching this thread or anyone else. Would this work for
elements not yet loaded by the browser before DOMContentLoaded?

~~~
andres
Yes. SentinelJS will trigger a watch for elements present when
DOMContentLoaded fires and for elements added dynamically afterwards. In the
Quickstart example, there's an element that is part of the initial HTML
payload and a button to add more dynamically:
[https://jsfiddle.net/muicss/rbqLbjzf/](https://jsfiddle.net/muicss/rbqLbjzf/)

~~~
spand
Thanks for the response and sorry. Maybe I was not quite clear. I was
attempting ask if it would trigger the event handler before DOMContentLoaded
(relevant on big document where DOMContentLoaded takes 10 seconds to fire)
Example on a timeline:

    
    
      0. Browser starts parsing the document
      1. Sentiel.js loads (ie. by blocking script tag)
      2. Handler is set for .my-component (ie. by inline script tag)
      3. Browser parses <div class="my-component" />
      4. Handler is fired for .my-component
      5. Browser parses rest of document
      6. DOMContentLoaded is fired.
    

Will the .my-component handler will always fire after DOMContentLoaded or have
you experienced that it might happen before? I guess it reduces to the
question if key-frame-handlers will fire before the document is fully
loaded/rendered.

~~~
andres
Yes, it comes down to when animation events fire which I believe is after
DOMContentLoaded.

In MUI we detect all elements present in the DOM when DOMContentLoaded fires
using document.querySelectorAll() and then use SentinelJS to detect elements
added after.

------
SimeVidas
I think, this is not a “shadow DOM-like,” but a “custom element-like
experience.”

~~~
andres
Fixed. Thanks.

------
ryanb23
Great work!

------
danhardman
on(), off() seems a bit unintuitive in my opinion. I'd opt for something like
.watch(), .ignore()

~~~
wolfgang42
on() and off() are the names that every JavaScript library uses for event
handlers; I don't think bikeshedding individual libraries is going to help
much.

