JS : How to get notified after insertBefore()? - javascript

Is there a way to get notified, after inserting an element into the DOM with insertBefore(), when this element becomes actually visible/available to user ? Especially to start applying CSS transforms on it ?
Complete problem
Forgive me if this question is recurrent, I didn't find a suitable answer so far. I'm trying to implement a custom popup dialog system on a website of my own, similar to SweetAlert or some other products.
I would like to apply some special effects when this popup shows up, such as a progressive darkening of the background, as well as a slow vertical motion on the box itself.
To achieve all of this, I spawn one big, fixed div element covering the whole screen (the background) and containing the popup box. When I need it, I first insert this element as body's first child, tagging it with a special invisible class. Once inserted, I remove this invisible class from the element and let the CSS rules do the magic.
The problem is that even if removed the class after having inserted this element, this one will be rendered only when the Javascript function leaves, hence directly in its final state.
When doing this on a complete initial page, the load event helps. I now would like to do the same on an existing page.
As always, I'm interested on both solutions to this (potentially XY) problem: if there's a better way to do it, I'll be happy to discover it, but I'm still interested in solving this particular situation anyway.
Thanks in advance to everyone.
EDIT: currently performing tests on Firefox 82.0.2

Thanks to comments above, here's a valid solution to both exposed problems:
"Mutation Observer", as well as former "Mutation Events" (now deprecated) are the best way to get notified when something is inserted. It won't help with animations issues, though, because it's still not guaranteed to be rendered yet at this time ;
Rather than applying a class then another to perform a transition, it's better to define a regular animation using #keyframes that plays only once. It's guaranteed to be played when the object appears, by definition.
Many thanks to "Pomax", F4st3r and epascarello for their help.

Related

Continue modifying DOM as user scrolls

I have a chrome extension that modifies the DOM based on keywords. The problem is, for websites like twitter that have an infinite scroll, I need a way for my function to keep firing as the user scrolls through the page.
Is .livequery() the only way to do this or is there a better way?
Right now all of the logic is plain JavaScript/Jquery, but I'm open to using a framework like Angular if that's the best way to do it.
I have several functions that interact -
1) a hide() function that adds a class to divs containing words I want hidden
2) a walk() function that walks the DOM and identifies divs to call hide() on
3) walkWithFilter() function that gets words to filter from localstorage and calls walk() function
The last function walkWithFilter() is called in a window.onload() event
It seems like the onScroll event would be a natural match for this. The trick would be that you'd need to keep track of what's already been processed to avoid reprocessing old content. If you're assuming that the user is always exposing new content below the existing content, that could be as simple as keeping a pointer to the last processed item and restarting the walkWithFilter method from there. That doesn't seem like an entirely safe assumption to me, though.
If you want to be more robust in that regard, you could try a virtual DOM approach: you maintain a copy of the DOM as you last saw it, compare it to the DOM as it currently exists, and take a diff. I know there are a bunch of premade libraries for this kind of thing, but I haven't used any and can't recommend a specific one (the link just goes to the first example that showed up in Google). It also doesn't appear to be overly burdensome to roll your own, if you're so inclined.

KnockOut binding breaks after moving DOM element

I have a web app built on KO and for the most part it has been a god-sent. However, I have one very frustrating problem.
When I move an element with jQuery from one spot in the DOM to another, the bindings seem to randomly break. Sometimes they survive the move, sometimes they don't. Anyone know what might be causing this? I wish I could give a specific example, but I can't seem to re-create it in a simple case (for a fiddle) and it truly is random (3 in 10 tries).
Is there a way to refresh the bindings in an element?
Cheers,
Had a similar issue. It was happening for me when I was moving the DOM element before applying bindings.
Make sure that all the applyBinding calls are made before you move the DOM element.
That is about all the help I can give without a code sample.
Perhaps try using ko.cleanNode to clear the bindings from the moved element, then ko.applyBindings( model, element ) to rebind them?
See also How to clear/remove observable bindings in Knockout.js?.

jquery event for on render an element

I am trying to do all dom manipulations off screen and then make it visible. Which works, except now I have the situation where I am trying to do it with a form which I want to focus on the first input text upon rendering it on the browser.
Something like: myForm.prependTo(myDiv).show().find('input:first').focus();
Problem is that the focus is being called before the form has finished rendering which is causing the lovely error 'Can't move focus to the control because it is invisible, not enabled, or of a type that does not accept the focus'
How do other web developers handle the similiar situation of manipulating elements off screen and then making it visible? I wish jQuery had something like myForm.prependTo(myDiv, function() { /* on render code here */ })
I know one way of doing it is setting a timeout and when it fires I put focus on the input, but I feel like that's not really the cleanest way to do things. I know the iframe has an onload event, so I'm curious if people usually draw their elements in some hidden iframe and listen for its load event to know when the element has finished rendering? If so could you point me to an example of doing this?
myForm.prependTo(myDiv).show(function(e){
$(this).find('input:first').focus();
});​
I know I'm 7 years late, but I had a similar problem, which I solved by putting the stuff I needed to happen after the render in a ready handler.
I had a restore function that worked, but there was zero or near zero visual feedback that the element had been restored.
I tried emptying the element first. It still worked, but still had zero visual feedback.
$("#someSelector").empty();
restore();
Then I discovered ready() happens after the rendering; so I changed it to something like....
$("#someSelector").empty().ready(function() {
restore();
});
Now the restore() doesn't happen until after the empty() action RENDERS. This means my element APPEARS to empty out and then refill (it always did, but now the user can see it happen).
I found this solution somehow a few days ago for a different problem with some vague search that I can't remember. Then I needed it again but couldn't exactly remember what I did. Now my searches included the word "jquery" and "render" and lead me here.
I ended up going thru my code to find out what I did, and I thought it might be a good idea to post it here in case other people stumble on this post and actually need to execute something AFTER rendering happens.
Cheers.

Using jquery to add/remove class but elements are not redrawn to reflect

I have two elements (think of two buttons side by side). I dynamically toggle the class "focusd" to change the highlighted effect. However, there's a quirk it doesn't always get redrawn and/or inserted in the DOM. For example, if in chrome I do console.log, I see the class changes (I'm using removeClass/addClass in jquery). But if I go to the Elements tab in the inspector, it shows the classes from before (and in fact, I'm not seeing the redrawing reflecting the toggling of the classes.)
I tried setting the parent div to display none then back to block but that didn't work. It's a "one off" modale screen, so efficiency doesn't matter so I've resorted to this hack where I essentially copy the parent's innerhtml, remove and reinsert the element. Horrible!
// Not sure why I need this hack. But if I don't, the buttons don't seem to get redrawn
var htm = jQuery(".rdata_container").html(); // copy the innerhtml
jQuery(".rdata_container").empty(); // empty and then append back
jQuery(".rdata_container").append(htm);
This seems like a specific quirk that someone must have ran into (I hope). If so, I'd love to know why my changes aren't reflected.
EDIT
Code posted here:
http://jsfiddle.net/roblevintennis/JCZnf/
you can use setTimeout when you are doing the other operation on the element, so for example:
$elm.addClass('hide')
setTimeout(function(){
$elm.removeCalss('hide')
},0);
Or you could force a repaint like so:
$elm.addClass('hide')
$elm.scrollTop; // forces a repaint (might be expensive for large amount of items)
$elm.removeCalss('hide');
These tricks will force the browser to re-draw the change, because there are two things happening here and the browser logic just combines them into one, which isn't the desired behavior.
Not directly an answer to your question, but you can use jQuery's toggleClass function to simplify your code.
Here's an updated version that uses toggleClass() and jQuery 1.6 and AFAICT works fine.
http://jsfiddle.net/JCZnf/7/

Javascript removeChild() and appendChild() VS display=none and display=block|inline

I'm developing a web application that shows some controls and descriptions dynamically (I don't want to use jQuery or other libraries).
At this moment i make appear and disappear controls using:
element.setAttribute("style", "display : inline");
and
element.setAttribute("style", "display : none");
but i'm thinking about using:
element.appendChild(childRef);
and
element.removeChild(childRef);
So, which one is the best solution in terms of system speed and elegance of the code?
Or is there a better way to go about this?
element.appendChild(childRef); and element.removeChild(childRef); both make the browser to manipulate the DOM tree while changing CSS just changes one of the attributes.
So, changing CSS is faster.
Dev Opera says
When an element has its display style set to none, it will not need to repaint, even if its contents are changed, since it is not being displayed. This can be used as an advantage. If several changes need to be made to an element or its contents, and it is not possible to combine these changes into a single repaint, the element can be set to display:none, the changes can be made, then the element can be set back to its normal display.
This will trigger two extra reflows, once when the element is hidden, and once when it is made to appear again, but the overall effect can be much faster
Another relevant article explains about reflow and repaint
Definitely go on using display properties.
They are much faster than removing/appending children, but most important they work very well in every browsers.
I know appendChild/removeChild are supposed to be well supported in IE, but sometimes on my IE7 a still get things moving around after a new element is appended (this is only my personal experience).
Regarding the way you change the display properties I would simply do a more easy and cross-browser (setAttribute is not well supported by IE6/7 an also IE8 when in IE7 mode):
element.style.display = 'none'; //for hiding an element
element.style.display = ''; //for showing that element again using its default way of displaying
Showing an element by using display = 'inline' is wrong because the element might have by default a display block way of showing like DIV tags and you are changing its way of showing to inline wich is not correct and might lead to elements in your page moving out from the places you expect them to be.
I doubt there's much in it one way or the other, and even if there is, I bet it varies by implementation (IE vs. Chrome vs. Firefox vs. ...). Both will cause reflow events in the tree.
Showing and hiding is simple and straightforward. Adding and removing can have its uses, but for the most part is probably overkill. For one thing, you have to keep a reference to it so you can add it back later, and you have to remember where you need to add it back.
But (and this is a bit off-topic) your mechanism for showing and hiding has some issues:
With your example code, things can only be inline, not inline-block or block, which would be somewhat...limiting.
Your code also completely replaces the style information on the element (and may not work on IE; see David's comment on your question), so all other styles directly applied to the element will get blown away; consider using the style property of the element instead: childRef.style.display = 'none"; and childRef.style.display = "inline"; (or block, or...). That way, you don't bludgeon any other styles on the element.
Completely removing a child from the tree (while keeping a reference to it so you can put it back later) definitely has its uses, though. For instance, when it's not in the tree, it won't be found when you're walking the tree with selectors for doing this, that, and the other; which may be helpful.
A small aside: if you happen to be dealing with user editable elements in IE (i.e. inside a document with designMode "on" or inside an element with contenteditable true), setting the CSS display property to none won't have any effect, and you'll need to remove the elements from the DOM to hide them.
The first approach is better i think which just hides and displayes but does not remove from the DOM and add again.
I would think that performance is better just updating the style.
The answer will depend on what you are using the elements for. If it is very dynamic whether they should be shown or not, it might be a solution to add/remove from DOM, but if there are a static set of elements, it might be easier to set the style. Setting the style, you also have more control of the position in the document. Adding/removing from DOM, you need to be sure it is added at the right place.
Note:
Instead of element.setAttribute("style", "display:none"); you can use element.style.display= "none"; If you use the first approach, you will remove any other style settings on the element.
I think editing the style of an element will be faster, but which is better hide the element or remove it from dom, this depends on your needs, if you need to just hide the element or remove it.
May be you don't need the user see the element but you need to do some js logic on it, so hide here will be the best.

Categories