Get Z-index of each element on the page - javascript
I am trying to find the highest z-index on a page. I am using this
var getStyleProp = function(el, prop){
return window.getComputedStyle(el, null).getPropertyValue(prop);
}
var getHighestZIndex = function(){
var highestZIndex = 0,
HTMLElems = ["a","abbr","acronym","address","applet","area","article","audio","b","base","basefont","bdi","bdo","big","blink","blockquote","body","br","button","canvas","caption","center","cite","code","col","colgroup","content","data","datalist","dd","decorator","del","details","dfn","dialog","dir","div","dl","dt","element","em","embed","fieldset","figcaption","figure","footer","form","frame","frameset","h1, h2, h3, h4, h5, h6","head","header","hgroup","hr","html","i","iframe","img","input","ins","isindex","kbd","keygen","label","legend","li","link","listing","main","map","mark","menu","menuitem","meta","meter","nav","noembed","noscript","object","ol","optgroup","option","output","p","param","plaintext","pre","progress","q","rp","rt","rtc","ruby","s","samp","script","section","select","shadow","small","source","spacer","span","strike","strong","style","sub","summary","sup","table","tbody","td","template","textarea","tfoot","th","thead","time","title","tr","track","tt","u","ul","var","video","wbr","xmp"],
tags,
zIndex;
for (var i = 0; i < HTMLElems.length; i++){
tags = document.getElementsByTagName(HTMLElems[i]);
if (tags){
for (var c = 0; c < tags.length; c++){
zIndex =getStyleProp(tags[c], "z-index");
console.log(tags[c], "zIndex=", zIndex);
if (zIndex > highestZIndex){
highestZIndex = zIndex;
}
}
}
}
return highestZIndex;
}
console.log(getHighestZIndex());
But everything is coming back as "auto". This ancient article explains how a "bug" is causing this behavior. I've tried to make clones of each node, set the position to relative, and then get the z-index,
cl.style.display = "none";
cl.style.position = "absolute";
zIndex = (getStyleProp(cl, "z-index"));
but that is not working either. What is wrong here? Is there a cheaper way to do this than recreating everything on the page?
JSBIN
The node's clone does not seem to get the z-index, while the node itself returns the right value. You could try using it instead (not sure how it might react on a page with lots of content):
var getHighestZIndex = function () {
var highestZIndex = 0,
zIndex,
pos,
tags = document.body.getElementsByTagName('*');
for (var c = 0; c < tags.length; c++) {
// Get the original 'position' property
pos = getComputedStyle(tags[c]).position;
// Set it temporarily to 'relative'
tags[c].style.position = "relative";
// Grab the z-index
zIndex = getComputedStyle(tags[c]).zIndex;
// Reset the 'position'
tags[c].style.position = pos;
console.log(tags[c], "zIndex=", zIndex);
if (zIndex > highestZIndex) { highestZIndex = zIndex; }
}
return highestZIndex;
};
console.log(getHighestZIndex());
JS Fiddle Demo
Changing the element's position temporarily might produce a glitch. You'll need to test that on a page with lots of contents and elements that are position:absolute; or position:fixed;.
If this doesn't fit your use-case, just let me know, and I'll remove it. However, as a thought.
Can you loop through all the tags, and if the value is "auto" assume it's 999. If it's >= 1000, take that as your "highest" value. Then, increment your zIndex up from your highest number that way. This way, the first tag you place will be 1000, the next will be 1001.
var elements = document.getElementsByTagName("*"),
maxZIndex = 999;
for( var i=0; i < elements.length; i++ ) {
var zIndex = parseInt(elements[0].style.zIndex) || 999;
maxZIndex = zIndex > maxZIndex ? zIndex : maxZIndex;
}
return maxZIndex;
This would fit a use case where you are just trying to make sure that the tag you are placing is greater than anything on the page...such as placing a modal.
999 is overkill...somewhat of a "just in case" I missed something because anything with z-index:auto is equivalent to zero. See the following "proof" where even though my z-index is only "1" it overlaps boxes that are 3-deep of "auto".
<div style='position:absolute;background-color:white;z-index:1;width:94px;height:94px;'>
</div>
<div style='position:absolute;background-color:red;width:100px;height:100px;'>
<div style='position:absolute;background-color:blue;width:98px;height:98px;'>
<div style='position:absolute;background-color:green;width:96px;height:96px;'>
</div>
</div>
</div>
Related
Different/Increasing CSS Value for Many DIVs With Same Class
I want to use javascript to change the left-margin value of many separate DIVs. The catch is that: I want to use only one className and I want the margin to increase, for example, 100px for each instance of the class. This way, instead of having all the DIVs land on top of each other, each DIV will be space out: the first at margin-left:0px, the second at margin-left:100px, the third at margin-left:200px, and so on. Here is the code that I have which simply applies the same margin-left to all DIVs. <script> b = document.getElementsByClassName('spacing'); for (i = 0; i < b.length; i++) { b[i].style.marginLeft = "100px"; } </script> Is there a way to get the javascript to find each instance of the class sequentially and instead of simply applying margin-left:100px to all, it does something like (margin applied to last instance of class + X) so each of 100 DIVs with the same className end up with a unique marginLeft value?
Yes there is a way You can simply multiply the amount of margin by iteration number like this i*100+'px' instead of this "100px" var b = document.getElementsByClassName('spacing'); for (i = 0; i < b.length; i++) { b[i].style.marginLeft = i*5+'px'; } Here is the working example
What you want to do is keeping track of your increasing margin by every iteration of the loop: b = document.getElementsByClassName('spacing'); var margin = 0; for (i = 0; i < b.length; i++) { margin += 100; b[i].style.marginLeft = margin + "px"; } That should do the trick. Check out a working example here: https://jsfiddle.net/c4p9ry46/
How do i get the link of the topmost positioned image in a website?
I'm writing a script which needs the src of the image which is positioned topmost on any website, NOT the FIRST image in the source, but the one positioned the highest. i tried something really basic, which is, retrieving the first image tag, however this wont work for images positioned by css/jquery/javascript. so any idea how i can accomplish this? |----------------- | .....title.... | |------| | |image | <==== link needed | |------| |//text content |Lorem dolor ipsum
I'm not certain about the jQuery reply but I believe that will still only give you relative image coordinate. Following this earlier post showing a method to get the absolute x, y coordinates of a html element on a page, and stealing the same method from PrototypeJS, the following code should do what you need, Caveats, I think that the 0 top check is safe to use to determine if an image is invisible or not, but it might be problematic. Also, this will only get images inside img tags, not image links or anything set with css. // cumulative offset function stolen from PrototypeJS var cumulativeOffset = function(element) { var top = 0, left = 0; do { top += element.offsetTop || 0; left += element.offsetLeft || 0; element = element.offsetParent; } while(element); return { top: top, left: left }; }; // get all images var results = document.getElementsByTagName("img"); var images = []; for (result in results) { if (results.hasOwnProperty(result)) { images.push(results[result]); } } // map our offset function across the images var offsets = images.map(cumulativeOffset); // pull out the highest image by checking for min offset // offset of 0 means that the image is invisible (I think...) var highest = images[0]; var minOffset = offsets[0]; for (i in offsets) { if (minOffset.top === 0 || (offsets[i].top > 0 && offsets[i].top < minOffset.top)) { minOffset = offsets[i]; highest = images[i]; } } // highest is your image element console.log(highest);
You need to compare the .y property of each element. function getTopMostImage() { //Get all images var imgs = document.querySelectorAll("img"); //Define a variable that marks the topmost image var topmost = imgs[0]; //Check all image positions and mark the topmost for (i = 1; i < imgs.length; i++) { if (topmost.y > imgs[i].y) topmost = imgs[i]; } return topmost.src; }
After load you can check the first img element present in Dom by first() Docs are here Hope it helps
// Use the most appropriate selector for your images such as "body img" or bla bla... var images = jQuery('img'); var topMostImageIndex = 0; for (var i = 0; i < images.length; i++) { if (jQuery(images[i]).position().top < jQuery(images[topMostImageIndex]).position().top) { topMostImageIndex = i; } } // It's the top-most image images[topMostImageIndex];
How to characterize z-indexing for the DOM? (2)
I've been a bit care-less with choosing z-indexes in my CSS. I'd like to traverse the DOM and report all the z-indexes with their respective ID's. For example: id z-index header 10 main 0 menu 20 The end result would be an object whose keys are the element id and the value is the z-index. One might call it z_obj // pseudo code var z_obj = {el_id: el_zindex}; Getting the z-index for each element ( el ) should be easy using something like filter and the code below: // attr is attribute data = _.filter(el.attributes, function (attr) { return (/^z-index/).test(atttr.name); }); But how would I traverse the DOM to get all z-indexes and store that in an object? I'd like to do this w/ out libraries, and using the code above if possible. This would be a good debug tool in general.
You can get all elements with getElementsByTagName("*"), iterate over the collection with a for loop, and use window.getComputedStyle(Node). From there, you can retrieve the z-index. Here's an example: var zObj, allEls, i, j, cur, style, zIndex; zObj = {}; allEls = document.body.getElementsByTagName("*"); for (i = 0, j = allEls.length; i < j; i++) { cur = allEls[i]; style = getComputedStyle(cur); zIndex = style.getPropertyValue("z-index"); zObj[cur.id] = zIndex; } DEMO: http://jsfiddle.net/mj3cR/1/ Where zObj is an Object, keys represented by the id attributes, and values represented by the z-index style. Note that getComputedStyle is not supported in IE < 9, but of course there are many polyfills :)
reportZ = function () { var z_arr = {}, all_el = document.body.getElementsByTagName("*"), i, len, cur, style, z_index; for (i = 0, len = all_el.length; i < len; i++) { cur = all_el[i]; style = win.getComputedStyle(cur); z_index = style.getPropertyValue("z-index"); if (z_index !== "auto") { z_arr[i] = [cur.id, cur.tagName, cur.className, z_index]; } } return z_arr; };
How to count the number of line boxes in a DIV or P
<div><span>aaaaaa</span> ... (many other span here) ... <span>zzzzzz</span></div> In that case, the boxes span are placed on few line-boxes inside the div. (The span elements can use different font-size.) 1) How can we know the number of the line-boxes ? 2) Can we know on which line-boxe an element span is placed ? 3) Can we know on which line-boxe the caret is placed (contenteditable) ? Thank you
I'll suppose the DOM in your example is an effective example of the actual complexity of your DOM, and that a "line-boxe" is just a line of text. 1-2) For every <span> inside the <div>, you can count the number of lines they span with something like this: var spans = div.getElementsByTagName("span"), spandata = []; for (var i = 0; i < spans.length; i++) { var rects = spans[i].getClientRects(); if (i > 0) if (rects[0].bottom > obj.rects[obj.rects - 1].bottom) var inirow = obj.lastRow + 1; else var inirow = obj.lastRow; var obj = { element: spans[i], rects: rects, iniRow: inirow, lastRow: inirow + rects.length - 1 }; spandata.push(obj); } Now spandata is a list of all the data you want about the <span> elements. I'm also supposing that each one of them may span through more than one line. Keep in mind that getClientRects has some issues in IE<8. 3) In modern browsers, the getSelection method can help you: var sel = window.getSelection(); if (sel.type === "Caret") var span = sel.anchorNode.parentNode; About the line position, I must say it's not an easy task. You can't easily get the page position of the caret. The simplest thing you can do is to place a dummy inline element in the place of the caret: var text = sel.anchorNode.nodeValue; sel.anchorNode.nodeValue = text.substring(0, sel.anchorOffset); var dummy = document.createElement("i"); span.appendChild(dummy); var pos = dummy.getBoundingClientRect(); sel.anchorNode.nodeValue = text; span.removeChild(dummy); pos contains the info of the position of the caret. Now you have to compare them with the rect infos about the span: var rects = span.getClientRects(); for (var i = 0; i < rects.length; i++) if (rects[i]].bottom === pos.bottom) break; if (i < rects.length) { for (var i = 0; i < spandata.length; i++) { if (spandata[i].element === span) { var line = spandata[i].iniRow + i; break; } } } In the end, if line != null, it contains the line of the caret. Man, that was complicated...
Let's say your div is in the el variable: el.children.length; // Number of direct children // You have one of the children in the "child" variable, to know its index: [].indexOf.call( el.children, child ); // Index of child in el.children I'm not mentioning the cross-browser issues there, but Array.prototype.indexOf is only available starting IE9 (so it works in all modern browsers).
split html content into fixed height and width divs with jQuery
I'm trying to split a large html page into smaller fixed height and width chunks (pages). Its pretty easy if I know the pages count - I can generate required number of pages first and then fill them with source content children: children = $('#source').children(); width = 600; height = 600; // already generated 'pages' divs = $('.page'); $(divs).width(width); pages = divs.length; i = 0; for (var c = 0; c < pages; ++c) { var div = $(divs).eq(c); while (i < children.length && div.height() < height) { $(children[i++]).clone().appendTo(div); } if(div.height() > height) { div.contents().last().remove(); i--; } } But how can I do the same thing if I don't know the pages count? How to wrap content with $('div.page') and keep adding pages until I reach the end of content? thanks
You would want to use a loop to keep creating pages / filling them with content until you hit some endpoint, where you'd break out of the loop. The key here is dynamically creating the page divs in the loop. You can use document.createElement or simply $("") (or other jQuery ways). Something like this: var i = 0; while(true) { if (content /* have more content */) { var page = document.createElement("div"); $(page).addClass('page'); var children = $(content).children(); while (i < children.length && div.height() < height) { $(children[i++]).clone().appendTo(page); } $(body).append(page); } else { break; } } You may also want to use jQuery's each method if you have defined blocks of content to add to each 'page'. Comment if you need more help.