I want to get a top element (Element Object) from the viewport of a web page with JavaScript. (If any part of the top boundary of an html element is visible on the viewport that element is within the viewport)
The approach I came up was:
Find all the elements with document.body.getElementsByTagName("*")
Check if each element is in the viewport with How can I tell if a DOM element is visible in the current viewport?
Get the element with the lowest el.getBoundingClientRect().top value from the matching elements of the step 2
If there are many element at the same level (Eg: parents and children), matching any of them would be ok.
This seems to be too much code for a small requirement. Is there a shorter approach?
Related
I have something like a carousel with elements inside of a container with overflow: hidden
I can scroll left and right and I want to determine which elements are not visible at all or only half is visible (like on this picture) and when add to this invisible and half visible elements a class.
Width of each element is for example 100px but width of container depends on screen size. I can get number of elements which are visible (by dividing offsetWidth of container by width of one element)
Alse I know that there is such thing as getBoundingClientRect() but not sure how to use it in this case.
example
Here you can see how I try to implement getBoundingClientRect but I can't figure out which elements to target. I want to add class to the div which is partially seen (4th) and if on the first click part of the first div would be seen - to it too.
I'm creating an absolutely positioned element with Javascript. This element will be used in various different contexts. So the element in relation to which it is positioned could be its parent, or any other element including the body.
I need to set the element's position (e.g. left and top) before I place it in the DOM. Is there some way to find out which element my new element will be positioned relative to? I know which element will be its direct parent. But the closest element that has a position style that is not static — the element my new element will be positioned in — might be a different element.
I considered traveling up the DOM tree and checking each parent element's position style, but that seems like a really convoluted way to do it.
So, is there a better way to find out which element is the reference frame for my absolutely positioned element's position?
Sounds like you want .offsetParent(), which will grab the first positioned parent of the element (I assume in the most efficient way possible). As per the definition of absolute positioning,
absolute
Do not leave space for the element. Instead, position it at a
specified position relative to its closest positioned ancestor if any,
or otherwise relative to the containing block. Absolutely positioned
boxes can have margins, and they do not collapse with any other
margins.
It does exactly what you're looking for.
EDIT: For a pure JavaScript solution, iterating over every parent is the only way to go. Try:
function offsetParent(elem) {
var parent = elem.parentNode;
if (window.getComputedStyle(parent).position !== 'static' || parent.tagName === 'BODY') {
return parent;
} else {
return offsetParent(parent);
}
}
There are methods, but they will require iteration through all the DOM elements, which might not be quick.
Here are possible selection of elements for you in jQuery:
$('*').filter(function(){
var position = $(this).css('position');
return position === 'absolute';
});
If you have target selector, and need to figure out if it's nested to any of parent elements, with absolute positioning, you may traverse up among all the parents with parent():
// it will return all the parent selectors, with the absolute positioning
$(targetSelector).parents().filter(function() {
var position = $(this).css('position');
return position === 'absolute';
});
See also .offsetParent(), which might be useful at some degree, but not for your purposes, as you have a bit different task. And here's why:
.offsetParent() method allows us to search through the ancestors of these elements in the DOM tree and construct a new jQuery object wrapped around the closest positioned ancestor. An element is said to be positioned if it has a CSS position attribute of relative, absolute, or fixed.
That means, it will grab the first parent element, which has any of those CSS position attributes defined: relative, absolute, or fixed. As soon as you are in need to filter out only "position:absolute;" it's not working the way you expect, and you need to take the solution I proposed several paragraphs above.
I have a slider that contains N elements. Each element will by translated by N pixels when the user click on the next button. When the element is out of the wrapper div, it disappears because it is overflowed by another element.
My plugin does not use any margins, just the transform property.
I would like to know if there is a way to know if my element is out of the div. :visible does not work for my problem because the element is already visible but overflowed.
If I understand correctly, one way to do it would be to compare the position of this element to the size (width/height or both) of his parent.
With Jquery you could do it this way:
<script>
//This is the position of the right side of the element
//relative to his parent
var rightPos = $("#element").position().left + $("#element").width();
//And bottom side
var botPos = $("#element").position().top + $("#element").height();
if (rightPos > $("#element").parent().width()) {
//The element is outside the right limit of the the parent block
} else if (botPos > $("#element").parent.height()) {
//It's outside the bottom limit of the parent block
}
</script>
If it's not the parent you could then adapt this code to compare the position to the width of the correct div, preferably by using the jquery offset() method instead of position().
By determine parent width and get child width then use if condition
if($('span').width() > divWidth){
alert('Overflowed!');
// do something!
}
jsFiddle Demo
if you update your question with your html then I can update with your codes.
You could give the wrapper div the CSS property of overflow: hidden
This would mean that any elements inside of it are not visible when they leave the bounds of the wrapper.
Otherwise you could check whether your element is outside of the wrapper div using jQuery to compare the position to that of the parent.
There is a nice tool for testing if an element is visible on the screen.
Detect if a DOM Element is Truly Visible
It looks at an object and checks each of its parents to see if it’s still visible to the user.
I set up at test case on jsperf here: http://jsperf.com/rect-vs-offsettopmsa
Now a lot of the results there seem intuitive and make sense, however there is one thing that bothers me regarding ScrollTop and OffsetTop.
Why is it that ScrollTop is almost 3-4x faster than OffsetTop if they are both DOM Element properties? Especially since OffsetTop is also only a read-only property according to the msdn: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetTop
I can't say for sure — you'll have to examine source code to be sure. But the obvious reason for the offsetTop poorer performance is that it is simply more complicated to compute.
To calculate Element.scrollTop browser just needs to explore the Element you asking for — it can get all the input data it needs just from it.
But to calculate Element.offsetTop you need to calculate the element's position AND it's parent's — and then compare them to get the relative position. Hence more time needed to perform it.
Here you can find a description of how Element.offsetTop works according to spec.
If the element is the HTML body element or does not have any associated CSS layout box return zero and terminate this algorithm.
If the offsetParent of the element is null return the y-coordinate of the top border edge of the first CSS layout box associated with the element, relative to the initial containing block origin, ignoring any transforms that apply to the element and its ancestors, and terminate this algorithm.
Return the result of subtracting the y-coordinate of the top padding edge of the first CSS layout box associated with the offsetParent of the element from the y-coordinate of the top border edge of the first CSS layout box associated with the element, relative to the initial containing block origin, ignoring any transforms that apply to the element and its ancestors.
So yeah, even if it looks like a simple property, it still can trigger some calculations on the element.
UPDATED:
It looks that my answer is still incorrect because of this interface declared in the same spec:
partial interface HTMLElement {
readonly attribute Element? offsetParent;
readonly attribute long offsetTop;
readonly attribute long offsetLeft;
readonly attribute long offsetWidth;
readonly attribute long offsetHeight;
};
So, yes, both this properties are readOnly, so the fact that one is 3 times faster then other doesn't make much sense. Ignore what I've written earlier.
Selenium Webdriver contains a function that returns only visible text inside element. I'd want to write a function that will get only hidden text inside element (i.e. all text that isn't visible in meaning of Selenium Webdriver W3C spec). According to to this spec element is visible only if all following conditions are met:
The element must have a height and width greater than 0px.
The element must not be visible if that element, or any of its ancestors, is hidden or has a CSS display property that is none.
The element must not be visible if there is a CSS3 Transform property that moves the element out of the viewport and can not be scrolled to.
OPTIONs and OPTGROUP elements are treated as special cases, they are considered shown if and only if the enclosing select element is visible.
MAP elements are shown if and only if the image it uses is visible. Areas within a map are shown if the enclosing MAP is visible.
Any INPUT elements of "type=hidden" are not visible
Any NOSCRIPT elements must not be visible if Javascript is enabled.
The element must not be visible if any ancestor in the element's transitive closure of offsetParents has a fixed size, and has the CSS style of "overflow:hidden", and the element's location is not within the fixed size of the parent.
Is it possible to write a JS function that will return only hidden text contained inside element? Do you know of any library that contains such function? How slow will such function be?
Yes, it is possible to write such code if you are just monitoring for display: none, visibility: hidden and no size or even an absolute/relative position that is off the screen. You would have to iterate every element in the page, determine if the element is visible and, if so, collect the text from any text nodes in that element.
It will be no slower or faster than any other function that iterates every node in the document. The total time will depend upon how efficiently the iteration code is written (it can skip all children of a hidden element for example) and on how long/complicated the document is.
If you want to be able to tell the difference between text that is outside the edges of an element with overflow:hidden or elements that might be marked for visibility, but be off-screen or out of view or out of the current viewable scroll area or pieces of text that might be obscured by other elements, that would be very difficult and honestly I don't know if all of that can be figured out from pure javascript.