Avoiding zIndex by appending dom elements - javascript

So I'm trying to control the stacking of equally-sized elements via z-index.
Now an idea that I came across recently to avoid going through z-indices and improve performance times by hopefully to avoiding browser reflows is instead order layers via the order I append things to the parent.
So if I have a container div that holds all the stacking divs, and linked list that mirrors the order, referencing the stacking divs, then I reorder the divs based on user input. Then instead of updating the z-indices, I would recreate the div element and just append everything in order. So something like this:
var from = nodeBeforeFrom; // Input
var target = nodeBeforeTarget; // Input
var linkedlist = input; // var linkedlist contains all the stacking divs
linkedlist.moveElement(div1, div2); //Move div1 to after div2
var container = document.createElement('div');
linkedlist.reorder; //
var cur = linkedlist.first;
while (cur.next) {
container.appendChild(cur)
cur = cur.next;
}
document.removeChild(oldContainer);
document.appendChild(container);
// This is meant as pseudocode so forgive an errors in regards to the specifics
So my questions are the following:
Would this reduce browser reflows from n reflows to just 1 or 2 (where n is the number of divs)? If I understand it right, changing the z-index of a single element should cause either a browser repaint or a reflow.
Will the second approach work and stack elements in the order you append them?
Is there a way to move childs around using the DOM's child node structure already so I don't have to create a separate linked list? I only see removeChild and appendChild functions that I can use at the moment.
And yes performance is an issue since I'm planning on using this for graphics and html5 stuff. So where I can save I would like to save.

Well great it seems I've answered my own question after playing around with things and some research thanks to the good people at Opera. Pretty much yeah it's faster to perform updates on hidden/unseen elements on the browser, then to add it to the DOM tree. I got my confirmation from here. The actual trick is to set the hidden CSS tag, perform all the operations that affect display, then set hidden back to true and that reduces your browser reflows from O(n) to just 2 total.
Also this method for avoiding z-index certainly works. I unfortunately still haven't found a way to access the childNodes linked list for DOM elements. However, taking a closer look at the specification, it turns out that childNodes for DOM nodes is read-only, which likely means it's not possible unless there's some vague hack around it.

Related

Remove child nodes (or elements) or set innerHTML=""?

When clearing HTML elements such as select boxes, tables, or lists, is it better/faster to remove the nodes (e.g., select.options.remove(i), table.deleteRow(i)) or just empty the innerHTML (e.g., select.innerHTML = "")? Or does it matter?
An example case would be reinitializing a table. A specific select field's value should load different values for a subsequent HTML table. When the select value changes, the table needs to be reinitialized.
In IE you cannot set the innerHTML of a select element. So for a cross-browser solution the only way is to add/remove child nodes.
I made a new test that isn't broken.
http://jsperf.com/innerhtml-vs-removechild/67
It's not perfect either since part of the sample setup is part of the test so this might skew the results.
This gives that innerHTML is faster but I don't know by how much.
Per this conversation here: What is the best way to empty an node in JavaScript
It appears that the while (elm.firstChild) {elm.removeChild(elm.firstChild);} approach got the the best results across browsers in this test.
(Would have put this as a comment instead of an answer, but the comments are coming in awful fast, so I didn't want it to get lost.)
Case 1:-
Run on 1000 Child DOM Element to Remove.
Which one is Faster?
Remove Child (5,676,264 ops/sec ±1.46%) --> Faster
innerHTML = "" (4,359,867 ops/sec ±1.46%) --> Slower
Case 2:-
Which one is able to remove event handler of child Nodes?
Remove Child --> Able to remove event handler
innerHTML = "" --> Not able to remove event handler
Result:-
Seems It's better to used Remove Child for removing any child nodes.
Ref:-
https://www.measurethat.net/Benchmarks/Show/6910/0/innerhtml-vs-removechild-vs-remove
https://rusingh.com/javascript-benchmark-removechild-vs-innerhtml/

createRange select all in parent js

I'm looking to select a range with javascript createRange function. I'm just now after so many years discovering this function, and it's one of those DOH! moments.
Is there a way to select all the elements in a given parent. Currently, I have a div full of content I'm trying to select. This content may be text nodes, elements, IDK.
var container = document.getElementById('mydiv');
var range = document.createRange();
range.setStart(container, 0);
range.setEnd(container, ???);
Is there an accurate, easy, efficient way to get the length of content in the parent (container)? Or is there a hidden method with range that does this already?
No libraries, please! Not terribly concerned with cross-browser fall-backs, but insights into them are welcome if you have any.
EDIT
For clarity, I'm NOT looking to get the innerHTML, but rather a range object. So PLEASE don't say "why not just use innerHTML".
selectNodeContents does the trick, getting the content of the specified element.
range.selectNodeContents(container);

removeClass of Mootools is very slow on IE

I am applying addClass and removeClass on same set of elements in mooTools.addClass works fine but removeClass takes too long approx 6-7sec.Please see the code undeneath.
$('usermailbox').getElements('input[class=emailmessages]').each(function(el){
el.getParents().addClass('unChecked');//works fine
});
$('usermailbox').getElements('input[class=emailmessages]').each(function(el){
el.getParents().removeClass('selected'); //Takes too long in IE
});
Do I have any hope ?
Right. Several issues here... Obviously, w/o seeing the DOM it is a little difficult to determine but:
you do the same operations twice. in the same instance you can addClass and removeClass
doing element.getElements("input[class=emailmessages]") vs element.getElements("input.emailmessages") is probably slower but may return inputs that have other classes as well that you may not want.
el.getParents() will return all parents. you then iterate them again. twice. are you sure you don't mean just .getParent(), singular? if so, or if its one parent only, you are applying a .each on a single element, which is an unnecessary hit.
if your logic needs to remain then consider this as a single iteration:
store and walk.
var parents = el.getParents();
parents.each(function(p) {
p.addClass("unchecked").removeClass("selected");
});
all in all:
$("usermail").getElements("input.emailmessages").each(function(el) {
var parents = el.getParents();
parents.each(function(p) {
p.addClass("unchecked").removeClass("selected");
});
});
of course, with Slick / mootools 1.3 you can do it a lot more simple:
on this dom:
<div id="usermailbox">
<div class="selected">
<input class="emailmessages" />
</div>
<div class="selected">
<input class="emailmessages" />
</div>
<div class="selected">
<input class="emailmessages" />
</div>
</div>
the following will return all the divs:
// gets parents via a reverse combinator. you can do !> div or whatever to be more specific
// document.getElements("#usermailbox input.emailmessages !") will return all parents...
// ... in a single statement, inclusive of body, html etc... same as getParents
var divs = document.id("usermailbox").getElements("input.emailmessages !> ");
// single iteration per parent:
divs.each(function(div) {
div.removeClass("selected").addClass("unChecked");
});
of course, you can just do $("useremail").getElements("div.selected,div.unChecked") to get to these divs at any time anyway.... so it all depends on your DOM and needs, there must be a reason why you are doing what you are doing.
bottom line for perf:
cache results of lookups into vars. if you call $("someid") twice, cache it in your scope. if you do $("someid").getElements() twice, that's more than twice as bad in performance... and add .getParents() twice, thats ... n-times as bad now...
avoid applying chaqined methods to collections like this: collection.addClass("foo").removeClass("bar") - it will iterate it twice or n-times, go for a single .each callback instead, it will be much faster.
try to avoid reverse combinators or parents lookups if possible, go direct. you can use nth-child etc - other ways to walk your DOM than to get to the input and walk back. Especially when you don't really need the input itself...
.getParents(selector) will limit the types of parents you want. .getParents() will return buckloads, all the way up the parent / anchor dom node, often including the same ones for siblings.
always create a local scope with anonymous functions so you don't pollute your global object or upper scopes - IE does not like that and makes accessing variables slow.
Hope some of this makes sense, at least :) good luck and remember, speed is only relative and is as good as your performance in the slowest browser you support.

What information about a DOM element would allow JavaScript to identify it (somewhat) uniquely? (e.g. when it doesn't have `id`)

Here's what I'm trying to do: I have a bookmarklet that is looking for elements in the current page (which can be any site) and dispatch a click event on the ones that match. I have that part working.
In some cases though, nothing matches automatically and I want to be able to show (by hovering it) what element should be activated and then save some info about it in localStorage. The next time I'm using the bookmarklet on that page, I want to retrieve that info to identify the element in the DOM and then dispatch a click event.
The question is: what information should I save to be able to identify it? (in most cases, since it will always be possible to create a case where it doesn't work)
In the best case, said-element will have an id value and I'm good to go. In some other cases, it won't and I'd like to see your suggestions as to what info and what method I should use to get it back.
So far my idea is to save some of the element's properties and traverse the DOM to find elements that match everything. Not all properties will work (e.g. clientWidth will depend on the size of the browser) and not all types of elements will have all properties (e.g. a div node won't have a src value), which means that on one hand, I can't blindly save all properties, but on the other, I need to either choose a limited list of properties that will work for any kinds of element (at the risk of losing some useful info) or have different cases for different elements (which doesn't sound super great).
Things I was thinking I could use:
id of course
className, tagName would help, though className is likely to not be a clear match in some cases
innerHTML should work in a lot of cases if the content is text
src should work in most cases if the content is an image
the hierarchy of ancestors (but that can get messy)
...?
So, my question is a bit "how would you go about this?", not necessarily code.
Thanks!
You could do what #brendan said. You can also make up a jQuery-style selector string for each element in the DOM by figuring out the element's "index" in terms of its place in its parent's list of child nodes, and then building that up by walking up the DOM to the body tag.
What you'd end up with is something that looks like
body > :nth-child(3) > :nth-child(0) > :nth-child(4)
Of course if the DOM changes that won't work so good. You could add class names etc, but as you said yourself things like this are inherently fragile if you don't have a good "id" to start with, one that's put there at page creation time by whatever logic knows what's supposed to be in the page in the first place.
an approach would be using name, tagName and className-combination. innerHTML could may be too big.
another approach would be to look for child elements of your choosen element which have an id.
check for id => check for childs with id => check for name, tagName and className-combination (if => tell user to choose a different item :-)
What about finding all elements without an ID and assigning them a unique id. Then you could always use id.
What about using the index (integer) of the element within the DOM? You could loop through every element on page load and set a custom attribute to the index...
var els = document.getElementsByTagName("*");
for(var i = 0, l = els.length; i < l; i++) {
els[i].customIndex = i;
}

Problem in using javascript childNodes.length property with elem

I have a treeview with drag and drop functionality. Upon completion of drag and drop I need to find the count of the child nodes of the new parent node. So for that I'm using the following
lines
var childnodelength=elem.parentNode.parentNode.childNodes.length;
alert(childnodelength);
But I always get the same value of 4 irrespective of the no. of childs, with the above alert.
I've also tried in the following way.
alert(elem.getElementsByTagName("A")[0].childNodes.length);
Above line always gives me 1 irrespective of the no. of childs. I am not sure if I am referring correctly in both the ways.
And hence I'm unable to find the no. of child nodes.
Please could someone help me with this?
Thanks
Keep in mind that childNodes only looks one level down, so the value will be the same so long as you have the same number of immediate children. Perhaps what you wanted was to count all the children all the way down the tree?
If you want all the child elements, you can use getElementsByTagName("*"). Note that the use of the "*" argument doesn't work in IE5.5, which could also pose trouble for more recent versions of IE when running in quirks mode.
Another good thing to know about childNodes.length is that it may return a different value in IE than other browsers because of different ways of counting text nodes . . . If you want to exclude text nodes and only count elements you can loop through the childNodes and check the value of nodeType (1 = ELEMENT_NODE, 2 = ATTRIBUTE_NODE, 3 = TEXT_NODE, etc.) You can also use children instead of childNodes if you only want element nodes, but in IE this incorrectly counts comment nodes, and in Firefox it wasn't supported until version 3.5.
Perhaps the child node is a DIV or Table that wraps up the nodes you need to count?

Categories