5 ms·
Show HN: SentinelJS – Detect new DOM nodes using CSS selectors
- ry_ry 9y agoI 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?
- Arnt 9y agoYou used npm to pull in nine hundred dependencies. One of them made the new node, and you want to know. Or you're writing one of the nine hundred, and the user made the node.
- myhf 9y agoThere is some discussion about the motivation here: http://www.backalleycoder.com/2012/04/25/i-want-a-damnodeinserted/ http://www.backalleycoder.com/2012/04/25/i-want-a-damnodeins...
- ry_ry 9y agoOh nice, thanks. I'd actually completely forgotten about mutation observers too - they'd got mentally filed away under "browser vendors are why we can't have nice things".
- atmin 9y agoAnd implementation by the same author: https://github.com/csuwildcat/SelectorListener https://github.com/csuwildcat/SelectorListener
- daurnimator 9y agoFor fengari-web[1] we want to watch for <script type="text/lua"> tags inserted into the document. At the moment we use a MutationObserver[2], but I should compare the speed of things. I've created a new issue to track[3] [1] https://github.com/fengari-lua/fengari-web [2] https://github.com/fengari-lua/fengari-web/blob/4279f4b3aec063062eb8baf68a065ee5b0c1d7c6/src/fengari-web.js#L132 [3] https://github.com/fengari-lua/fengari-web/issues/15
- superasn 9y agoHow 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.
- cheeaun 9y agoFrom the code, it adds `animationstart` event that will be fired for any newly injected DOM https://github.com/muicss/sentineljs/blob/master/src/sentinel.js#L14 https://github.com/muicss/sentineljs/blob/master/src/sentine... It adds a faux `animation` keyframe for the specified CSS selector.
- ry_ry 9y agoLooks like it adds a keyframe animation to the elements, and when a child node is inserted that animation is triggered, and emits an event. Am on my phone tho, so may have misread.
- projectant 9y agoIt 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!
- superasn 9y agoWow that is just super clever and I'm guessing super fast also since it is triggered by the browser. I was thinking more on the lines of comparing nodes using setInterval which would of course never work because it would be slow and CPU intensive!
- gcb0 9y agoit 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.
- have_faith 9y agoDon't we have mutation observers for this exact purpose? or is there a benefit to this approach?
- moheeb 9y agoI have used mutation observers to do exactly this. They are very simple to use.
- spand 9y agoIf the author is watching this thread or anyone else. Would this work for elements not yet loaded by the browser before DOMContentLoaded?
- andres 9y agoYes. 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 9y agoThanks 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 9y agoYes, 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.
- yeasayer 9y agoWhat'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 9y agoMutation 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 9y ago> 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-changes-with-Mutation-Observers https://developers.google.com/web/updates/2012/02/Detect-DOM...
- nailer 9y agoI would have thought MOs were implemented in C and thus pretty fast. Either way, even if they're slow compared to this technique at present, I haven't found performance to be an issue when using them now, and no doubt they'll be optimsied further.
- rstacruz 9y agoNice 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
- skrebbel 9y agoWow, rsjs is really neat! Thanks! Totally going to use that on our dashboard.
- aoeusnth1 9y agoRsjs looks pretty great, thanks.
- Lord_Zero 9y agoIs 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 9y agoif 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).
- SimeVidas 9y agoI think, this is not a “shadow DOM-like,” but a “custom element-like experience.”
- andres 9y agoFixed. Thanks.
- robocat 9y agoDetailed write up of how it is implemented, and why mutation events are not suitable: http://www.backalleycoder.com/2012/04/25/i-want-a-damnodeinserted/ http://www.backalleycoder.com/2012/04/25/i-want-a-damnodeins...
- kccqzy 9y agoBut aren't mutation events deprecated since forever? Is the newer replacement mutation observer also unsuitable?
- mercer 9y agoI think the article might answer that, if I understand your question correctly.
- paulirish 9y agoMutationObserver 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/wait-by-observer.js https://github.com/azu/wait-for-element.js/blob/master/lib/w... (also rstacruz's referenced onmount lib also uses MutationObserver/matches()) (Mutation events deprecated since ~5 years ago, yes. )
- danhardman 9y agoon(), off() seems a bit unintuitive in my opinion. I'd opt for something like .watch(), .ignore()
- wolfgang42 9y agoon() 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.
- ryanb23 9y agoGreat work!
- c-smile 9y agoThe 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. }