How to find HTML element(-s) with z-index = 10 for example?
You have to iterate over all elements and check their z-index:
$('*').filter(function() {
return $(this).css('z-index') == 10;
}).each(function() {
// do something with them
});
One possible [jQuery] solution:
$(".elementsToSearch").each(function()
{
if($(this).css('z-index') == 10)
{
//then it's a match
}
});
Just loops through elements searching for a match to the css rule.
You can get all elements and filter them by css property:
$('*').each(function(){
if($(this).css('z-index') == 10) {
//$(this) - is element what you need
}
});
In my testing in Chrome 43, I found #ThiefMaster's post to be helpful, but not 100%. The value of the z-index being pulled was a string.
I also made this only iterate over visible elements and handle auto.
Here's my update:
var topZ = $('.thing-in-front').css('z-index')
if (topZ != 'auto') {
topZ = parseInt(topZ);
$('*:visible').filter(function() {
var thisZ = $(this).css('z-index')
return thisZ != 'auto' && parseInt(thisZ) >= topZ;
}).each(function() {
$(this).css('z-index', topZ - 1);
})
}
Without JQuery:
Array.from(document.querySelectorAll('*'))
.filter(e=>getComputedStyle(e).zIndex >= 0)
Related
I have elements like below
<div class="one">send Message</div>
<div class="one">send Message</div>
<div class="one">send Message</div>
I have a web page where there is send Message buttons like above, in which only one button is visible at a time.Other two buttons are hidden via some javascript codes.So for example if 2nd button is visible , I should be able to get only that element.
So my code will be something like
document.querySelector(".one:visible");
In jquery the code is $(".one:visible"); , which works fine , But I need to know how to do this via pure javascript.
Here's something you can use, pure Javascript:
// Get all elements on the page (change this to another DOM element if you want)
var all = document.getElementsByTagName("*");
for (var i = 0, max = all.length; i < max; i++) {
if (isHidden(all[i]))
// hidden
else
// visible
}
function isHidden(el) {
var style = window.getComputedStyle(el);
return ((style.display === 'none') || (style.visibility === 'hidden'))
}
I have something shorter:
Array.from(document.querySelectorAll('.one')).filter(s =>
window.getComputedStyle(s).getPropertyValue('display') != 'none'
);
Returns all elements with attribute display block set.
Use getBoundingClientRect. It will return height and width of zero if the element is not in the DOM, or is not displayed.
Note that this cannot be used to determine if an element is not visible due to visibility: hidden or opacity: 0. AFAIK this behavior is identical to the jQuery :visible "selector". Apparently jQuery uses offsetHeight and offsetWidth of zero to check for non-visibility.
This solution will also not check if the item is not visible due to being off the screen (although you could check that easily enough), or if the element is hidden behind some other element.
See also Detect if an element is visible (without using jquery)
var $el = document.querySelectorAll('.one');
var visibleElements;
for (var i = 0; i < $el.length; i++) {
var currentElement = $el[i];
var $style = window.getComputedStyle(currentElement, null);
if (!currentElement) {
return false;
} else if (!$style) {
return false;
} else if ($style.display === 'none') {
return false;
} else {
visibleElements.push(currentElement);
}
}
First we get all the elements using document querySelectorAll. Then, we need to iterate over all the elements. To get the style, use getComputedStyle.
After that :visible check only for display and we do it the same way.
A more comprehensive approach:
function isVisible(el) {
while (el) {
if (el === document) {
return true;
}
var $style = window.getComputedStyle(el, null);
if (!el) {
return false;
} else if (!$style) {
return false;
} else if ($style.display === 'none') {
return false;
} else if ($style.visibility === 'hidden') {
return false;
} else if (+$style.opacity === 0) {
return false;
} else if (($style.display === 'block' || $style.display === 'inline-block') &&
$style.height === '0px' && $style.overflow === 'hidden') {
return false;
} else {
return $style.position === 'fixed' || isVisible(el.parentNode);
}
}
}
This would check for any possible way an element could be visible in the dom to my knowledge minus the z-index cases.
If you're using the hidden attribute :
document.querySelector(".one:not([hidden])");
So all jQuery's :visible selector does is check the display property.
If that's all you want, this is all you'd need.
(window.getComputedStyle(el).getPropertyValue('display') !== 'none')
However, this is lacking in many use cases. If you seek a more comprehensive solution, keep reading.
Both Element.getBoundingClientRect() and window.getComputedStyle() are useful for determining if the element is visible and in the viewport.
You can't use getBoundingRect() alone to determine the visibility, and while you could use getComputedStyle() solely, it's not the optimal solution in terms of performance.
Both of these functions used in conjunction with each other is the best option (around 22% faster than getComputedStyle() alone.
function inViewport(els) {
let matches = [],
elCt = els.length;
for (let i=0; i<elCt; ++i) {
let el = els[i],
b = el.getBoundingClientRect(), c;
if (b.width > 0 && b.height > 0 &&
b.left+b.width > 0 && b.right-b.width < window.outerWidth &&
b.top+b.height > 0 && b.bottom-b.width < window.outerHeight &&
(c = window.getComputedStyle(el)) &&
c.getPropertyValue('visibility') === 'visible' &&
c.getPropertyValue('opacity') !== 'none') {
matches.push(el);
}
}
return matches;
}
With a usage example of...
var els = document.querySelectorAll('.one'),
visibleEls = inViewport(els);
This ensures that the display is not set to "none", the visibility is "visible", the width and height are greater than 0, and the element is within the bounds of the viewport.
I want to add only one css style in JS. I don't want to include jQuery for only one thing.
My code:
document.addEventListener('DOMContentLoaded', function() {
if (navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1) {
var productAttr = document.getElementsByClassName('product-attributes');
productAttr.style.top = "-90px";
}
});
The error from console is:
TypeError: 'undefined' is not an object (evaluating 'productAttr.style.top = "-90px"')
If I want change other styles f.e. opacity or color, I get the same problem.
How can I fix this ?
Thanks in advance for help.
You need to loop through your results because getElementsByClassName() returns a collection of elements:
for(var i = 0; i < productAttr.length; i++)
{
productAttr[i].style.top = "-90px";
}
Maybe it's because you can not give negative values in CSS
top:"-90px" === bottom "90px"
Maybe now it would work
document.addEventListener('DOMContentLoaded', function() {
if (navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1) {
var productAttr = document.getElementsByClassName('product-attributes');
productAttr.style.bottom = "90px";
}
});
It's preferred to have an id assigned to the targeted element then target using getElementById() as there cannot be elements with the same id, hence getElementByClassName() will return an array if there are multiple elements with the same className. If you need to target multiple elements, you should for-loop through them while applying the change to the nth item being looped:
for(x = 0; x < array.length; x++){
array[x].style.top = '-90px';
}
Gentle reminder: also remember to have position: relative|absolute|fixed to ensure 'top' attribute works
edited with thanks to Sebastien Daniel
When selecting a class you have to indicate what number is as an Tag, it is not like the id that there is only one.
It would be something like this code depending on which of the classes you want to apply it:
document.addEventListener('DOMContentLoaded', function() {
if (navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1) {
var productAttr = document.getElementsByClassName('product-attributes');
productAttr[0].style.top = "-90px";
productAttr[1].style.top = "-90px";
productAttr[2].style.top = "-90px";
}
});
I'm looking for an easy way to locate elements on the page that have margin-left and margin-right set to auto.
I got this script, that helps me some of the time:
(function() {
var elementsList = [];
for (var i = 0; i < document.styleSheets.length; i++) {
var styleSheet = document.styleSheets[i];
if (styleSheet.rules) {
for (var j = 0; j < styleSheet.rules.length; j++) {
var rule = styleSheet.rules[j];
if (rule && rule.style && rule.style.marginLeft == 'auto' && rule.style.marginRight == 'auto') {
var smallList = document.querySelectorAll(rule.selectorText);
if (smallList.length)
elementsList = elementsList.concat(smallList);
}
}
}
}
return elementsList
})();
While this function gets some of the job done, it doesn't catch most cases of margin: auto I've seen in websites.
Can you show me a better way?
If you're OK to use JQuery
As said by Martin Ernst for yonatan's answer: 'This will select only elements with marginLeft/Right="auto".'
Besides, as described in the comments, elements must be hidden in order to work with FF and safari.
This should work using JQuery:
$(document).ready(function() {
var visibleElements = $('body *:visible');
$('body *').hide();
var elements = $('body *').filter(function() {
return $(this).css('margin-left') == 'auto' && $(this).css('margin-right') == 'auto';
})
// show only elements that were visible
visibleElements.show();
});
Tip: if for some reason, you need to not load external scripts, just copy the content of the minified jquery script at the begining of yours.
use jQuery:
$('*').filter(function(i, d){return d.style.marginLeft == "auto" && d.style.marginRight == 'auto';});
I hate to say this, but this one has less success then my own version.
This problem is not trivial. Even in the days of window.getComputedStyle() it's hard to get a crossbrowser reliable answer for marginLeft/Right when margins are set to auto. So this is shurely not a complete answer but will try helping to find one.
margin-left and margin-right are also auto when the margin-shorthand is used:
#elem {margin: auto;} // or:
#elem {margin: 100px auto;} // or:
#elem {margin: 100px auto 30px;} // or:
#elem {margin: 100px auto 30px auto;}
You have to find those notations too when you are searching in the stylesheets. Include this function just before var elementsList=[]; in your code:
function expand(margin) {
var parts = margin.split(' ');
for (var i = 3; i; i--) parts[i] = parts[i] || parts[i - 2] || parts[0];
return parts[1] == 'auto' && parts[3] == 'auto';
}
Then change your inner if-condition to:
if (rule && rule.style &&
(rule.style.marginLeft == 'auto' && rule.style.marginRight == 'auto' || expand(rule.style.margin))
) {
var smallList = document.querySelectorAll(rule.selectorText);
if (smallList.length) elementsList = elementsList.concat(smallList);
}
Now you get also the rules where margin is used. But some problems stay with your code:
Same elements may be listed multiple times when they match more than one rule
It's not shure that all listet elements are really rendered with marginLeft/Right = auto. Maybe that css becomes overridden by another more specific rule.
As dfsq mentioned in his comment there can be inline-styles you can't find this way.
I have a really nice little function that reduces the text size inside some div's when they are displayed on the screen if they start overflowing.
$(function(){
$('div.Body').each(function (index) {
if ($('div.Body')[index].scrollHeight > 150) {
$('div.Body')[index].style.fontSize = 'small';
if ($('div.Body')[index].scrollHeight > 150) {
$('div.Body')[index].style.fontSize = 'x-small';
if ($('div.Body')[index].scrollHeight > 150) {
$('div.Body')[index].style.fontSize = 'xx-small';
}
}
}
});
})
I wanted to use the same/similar function to do the same while the users are typing the text into a textArea when they are submitting the text, but the textArea doesn't seem to have a function for scrollHeight:
$(function() {
window.status = $('.TextAreaClass').scrollHeight;
});
This function just returns undefined.
How can I accomplish this in a textArea?
Simply use this, instead of getting $('div.Body')[index] for each iteration of the loop:
$('div.Body').each(function () { // Remove the redundant parameter
if (this.scrollHeight > 150) {
this.style.fontSize = 'small';
if (this.scrollHeight > 150) {
this.style.fontSize = 'x-small';
if (this.scrollHeight > 150) {
this.style.fontSize = 'xx-small';
}
}
}
});
In the .each, this refers to $('div.Body')[index].
And, like Rory said, $('.TextAreaClass') returns a jQuery object. You'll probably want to use $('.TextAreaClass')[0]; to access the first DOM element in that object.
scrollHeight is a native javascript method, not a jQuery one so you will need to do the following to get the scrollHeight of the textarea:
window.status = $('.TextAreaClass')[0].scrollHeight;
Note the [0] which returns the first element in the jQuery object, which is the native DOM element. Also, you should really be caching the selected element in your current code to make it perform better. Try this:
$('div.Body').each(function (index) {
var div = $('div.Body')[index];
if (div.scrollHeight > 150) {
div.style.fontSize = 'small';
if (div.scrollHeight > 150) {
div.style.fontSize = 'x-small';
if (div.scrollHeight > 150) {
div.style.fontSize = 'xx-small';
}
}
}
});
Finally, the logic of that code seems flawed, as all the conditions are checking > 150?
I think I'm close, but I'm having trouble trying to finalize this. Basically when you scroll down to each image, the div containing that image's offset from the top of the window (-500 as a buffer) would add a .selected class to the list element on the left.
http://jsfiddle.net/H6RTs/ is my example
Here is the code where basically the magic happens (sort of):
$(window).bind('scroll', function() {
// What the current px is from vertical scroll
var scrollY = window.pageYOffset;
// They scrolled more than the original offset
if (scrollY > menuTop) {
if (menuClone.parent().length === 0) {
$('#js-current-products').each(function(index, value) {
var newIndex = index + 1;
var currentProduct = $('.js-current-product-' + newIndex).offset().top - 500;
if (scrollY > currentProduct) {
$('.js-product-' + newIndex).addClass('selected');
}
});
stickyDiv.addClass(posFixed).width(clonedWidth);
menuClone.after(stickyDiv.parent());
}
} else {
$('#js-product-menu li').first().removeClass('selected');
stickyDiv.removeClass(posFixed);
menuClone.remove();
}
});
It'll add the class to the first list item, but not the other ones (so I guess it keeps focusing on $('.js-current-product-1') instead of iterating through all of them (which would be 3).
What am I doing wrong?
2 things:
#EDIT - The way Zeta loop through is much cleaner :D#
1:
$('#js-current-products').each(function(index, value) {
var newIndex = index + 1;
var currentProduct = $('.js-current-product-' + newIndex).offset().top - 500;
if (scrollY > currentProduct) {
$('.js-product-' + newIndex).addClass('selected');
}
});
Your just looping through the container of the images, of which there is only one, so what you need to do is loop the the images themselves. One way would be to add the class prod to them when you loop through and give them an index, ie:
$('#js-current-products .js-current-product').each(function(index, value) {
var newIndex = index + 1;
$(this).removeClass('js-current-product').addClass('js-current-product-' + newIndex).addClass('prod');
});
#EDIT#
2:
You also need to remove the selected class when you scroll back up, this is easily done by adding an else to the test condition, ie:
if (scrollY > currentProduct) {
$('.js-product-' + newIndex).addClass('selected');
console.log(newIndex);
} else {
$('.js-product-' + newIndex).removeClass('selected');
}
I'm pretty sure that's all, take a look at the fiddle here: http://jsfiddle.net/H6RTs/3/
Cheers
Charlie
You only iterate over all elements which are identified by id="js-current-products". Only one element matches this selector. So
if (scrollY > menuTop) {
if (menuClone.parent().length === 0) {
$('#js-current-products').each(function(index, value) {
// matches only one product ....
should be
if (scrollY > menuTop) {
if (menuClone.parent().length === 0) {
$('#js-current-products div.border').each(function(index, value) {
// matches all products, but has to use div.border, since
// you changed your class names....
or something similar. Also you shouldn't change classnames to create unique ones for each element. They're class names, not identifiers.
$('#js-current-products .js-current-product').each(function(index, value) {
var newIndex = index + 1;
$(this).attr('id','js-current-product-' + newIndex);
});
If you don't abuse classes then you can also remove your .selected class very easily:
if (scrollY > currentProduct) {
$('#js-product-menu li').removeClass('selected');
$('#js-product-' + newIndex).addClass('selected');
}
Demonstration