In a complex web application, I have to check whether certain HTML elements are visible partly/completely on the screen.
There are various reasons why certain elements are not visible on the screen:
They have certain attributes, like display:none visibility:hidden width:0 height:0 opacity:0 and maybe others.
Other elements are in front of the element.
They are outside the parent's visible scroll area.
To a certain extent, some of their parents have one or more of the properties above (elements with absolute or fixed positioning break the chain).
They are outside the parent's visible area for other reasons, for instance the parent may be partly overlaid by other elements and they are only covering that part.
The things I found on stack overflow (like How to Check if element is visible after scrolling?, http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport and others) are doing simple checks for one or two of the reasons above, but do not even nearly try to detect all reasons I lined out.
Before I now start implementing everything manually, including all the corner cases: Is there a reason-agnostic check available through javascript, that will give me feedback on whether the element is visible in the rendered result?
For now, the easiest and most reliable thing I could come up with was the idea (didn't test it yet) to modify the background-color attribute of the element, use the html2canvas library before and after, and check whether the resulting images match.
Is there anything short of that "workaround"?
(Update: I have tested my html2canvas approach and not only is it slow, it also gives false positives because the images are not always the same, even if the element with changed background-color is not visible at all.)
Related
I'm developing a slide in/out panel using HTML and CSS.
I notice in some CSS code examples on the internet that they set the visibility property of the div to hidden once the div is completely out of view (via some negative margin-left index or some other), and obscured by overflow: hidden of its parent container. (and then back to visible when the user slides it out again)
However, in other examples they ignore this property, instead relying on the fact that it is completely hidden due to being out of view already.
I'm curious of the differences of these two approaches. One example I can think of is performance: I'm ignorant of whether or not browser rendering engines apply better optimizations to elements that are set to visibility: hidden, because it is guaranteed that they will not be seen.
Or is the browser smart enough to realize it is not being shown? Or does none of this matter, and the performance is identical regardless of whether it is visible to the user, hidden by overflow, or hidden by visibility: hidden?
Does anyone have any insight into this? Or can recommend the best practice?
There was an interesting study done on this actually which can be found here
To save you reading it, I'd say this is the main bit you're probably after:
The renderers correspond to the DOM elements, but the relation is not
one to one. Non visual DOM elements will not be inserted in the render
tree. An example is the "head" element. Also elements whose display
attribute was assigned to "none" will not appear in the tree (elements
with "hidden" visibility attribute will appear in the tree).
Basically saying, setting an element to have a visibilty of none will not stop it getting rendered but using display: none will.
In regards to the coding examples you spoke of, I'd say it's personal preference. Some people will set the visibility to none to maybe double-cover for themselves in the case that the hidden element gets put into view but doesnt need to show a particular element, maybe also to prevent the horizontal scroll bar from appearing too?
It's definitely not needed or required though and I'd personally leave the visibility property alone in this case.
I am building a web app where - as part of the UI - I have an occasional, temporary need for some extra elements in the DOM, which are therefore dynamically inserted. When they have served their purpose, they are hidden and then for good measure removed using jQuery's .remove().
For some reason, removing the elements creates a jump and a relocation of the viewport contents (reside within an iFrame) as if it is re-rendered despite the fact that the elements are hidden and therefore should not have any influence on the rendering of the content.
I have tried to remove the elements from the console instead of in my script and the jump still occurs. I have also seen a similar effect in Firefox.
I might end up keeping the elements if there is no solution, but if any of you have experience with a similar scenario and know of a solution, I would be very interested to hear it. Thank you very much.
UPDATE
After much scrutiny, I have finally pinned down the cause of this "error". It originates at the beginning of the content, which starts out with a h1 headline. The headlines have been styled with a top padding, except for the first child, where it is set to zero with a h1:first-child rule.
Now, the dynamic elements are inserted at various locations including before the first headline. Apparently, the dynamic element now becomes the first-child even though it is not rendered and therefore the general padding rule for the headlines becomes active and the headline receives a top padding. This causes it to jump up when the dynamic element is removed and it again gets a top padding of zero.
So I know the cause of this but I am not sure how to best solve the problem since the content as well as the styling is dynamic - ie. I do not control it and therefore can not simply replace the first-child rule with something more suitable.
IMHO it is completely unintuitive that the first-child rule is affected by elements that should be taken out of the document flow with display:none. It should only take "visible" elements into account.
Will the element be visible even for a blink of an eye If it is added to DOM and instantly removed?
var feed = $('<div class="entry"></div>').text(data.status).appendTo(app.twitter_feed);
console.log(feed.height());
feed.remove();
I've tried the above code on a few browsers and couldn't see the element. But is this behaviour consistent through all platforms/browsers?
After reading your previous question as well, it seems that you very badly want to calculate the display height of an element before actually displaying it. I 'm not entirely clear why you want to do this (it gives off a bad smell), but here's how to anyway.
Put a <div> in your page with height: 0, overflow: hidden, and the desired width of your element¹. Add the <div> we 're talking about inside that outer helper div (it will not show no matter what), and get its height after the browser performs layout. After that you can proceed however you want (e.g. by moving the inner <div> to another position in the DOM tree).
¹ it would be best to put it exactly where you want the .entry to end up (i.e. the .entry and the helper div will end up being siblings).
PS: It's always better for everyone if you mention your real purpose.
I can imagine a situation wherein for the browser to be able to compute the element's effective height, it will have to render it on the window, or at least have the element's box reflow against the current site layout. It might not be visible (as, yeah, it's instantly removed), but a situation like this will reflow the page, and the movement of the affected elements on the page can be seen.
Images come to mind, for example, because browsers generally have no idea what the dimensions of images are until they try to lay them out (correct me if I'm wrong there though).
So, no, I wouldn't say that this is consistent behavior.
Implement it like this. Make a clone of app.twitter_feed, and send it to hell (Coordinates: x:-30000, y:-30000) and try whatever you like there.
var cloneTWFeed = $(app.twitter_feed).clone();
cloneTWFeed.css("position", "absolute").css("top","-30000").css("left","-30000");
var feed = $('<div class="entry"></div>').text(data.status).appendTo(cloneTWFeed);
console.log(feed.height());
feed.remove();
You current code works just fine, but you never get to see the element, since just after you append it, you remove it.
The browser sees it, by the time that he takes to remove it, just after being appended.
See this working Fiddle Example!
There I've replaced the console.log with an alert() to force the browser to wait for my response, thus enabling me to see the element on the page.
Note: Also works fine with console.log(), given me the 18px of height.
You either have the remove() wrapped on a timer to actually see the element (visually), or if the HTML markup is to intense, or the desired effect is to only collect data from the element, place your element inside an hidden one, that way you can remove it whenever you've done collecting data from it.
If what you must acheive is an height measurement, try to add the element without visibility (see CSS:
http://reference.sitepoint.com/css/visibility)
In a zone of the page that causes no problems on the element flow.
I'm creating a custom select plugin. Everything is going great, but the dropdowns (<ul>-objects) are overlapping on each other :(
I understand, that the overlapping order is set after the elements order on page or when they are created. So my question is: What is the method to make the latest opened/shown object (<ul>) on top of the hierarchy?
I just need the correct method. I'm not going to copy the full code, but a little example:
$('#trigger').click(function () {
new_dropdown.slideDown();
});
(A picture is worth of 1000 words)
So lets say, that I open the green select the last.. How can I make it on top of the yellow one?
EDIT
For easier testing I created jsfiddle. For future references I'll post the link: http://jsfiddle.net/hobobne/uZV5p/1/ This is the live demo of the problem at hand.
What you're looking for is the CSS z-index property (higher values put elements at the front).
You could probably just set them in ascending order (e.g. set green one to 1000, yellow to 1001), but if you really need to bring it to the front when clicked, you can change the z-index with javascript
var zindex=100;
$("#trigger").click( function() {
newdropdown.css('z-index', ++zindex);
});
Here's a demo: http://jsfiddle.net/waitinforatrain/Vf7Hu/ (click the red and blue divs to bring to front).
Edit: gilly3's approach is better, and as was mentioned there may be some issues with older versions of IE.
Two ways:
Set a z-index
Setting a z-index will change the default stacking order. You can have a counter that increments and use that to set the z-index of newly stacked items. You may have issues with IE 7 or earlier, though, and those can be fixed by setting the z-index of other items. https://developer.mozilla.org/en/Understanding_CSS_z-index/Adding_z-index
Use absolute positioning, and append the div to the body
If you use absolute positioning, you can append the div to the body and still have it appear below the element. If you append the div to the body, the one last added should be on top, because of the default stacking order.
Give it a class when it is opened, and remove that class from the previously opened ones:
$(".slidedown_active").removeClass("slidedown_active");
$(this).addClass(".slidedown_active");
Then your users can use z-index in their style definition for that class to ensure the active list is always visible.
The reason I don't recommend setting the z-index directly is because you can mess up your users' layout unnecessarily. These kind of overlap issues can be a real headache for a web developer. For a plugin to try to guess at how to resolve overlap issues, without any knowledge of the code or design, would be virtually impossible. Instead, give your users the tools they need to fix the overlap issues themselves. It may be that your users would never encounter overlap issues, so setting the z-index for them would be pointless at best, and potentially harmful.
Ok so the story is my users need a multi-select dropdownlist, which doesn't exist in ASP.NET, so the simple solution I'm going with is I'm using listboxes with multiselect on and I start them off at size 1, and onmouseover I change the size to say 10, onmouseout sets it back to one. Simple enough and the users don't know the difference.
Now, my issue comes from the fact that since I have any number of controls on my web app, I've set these listboxes to higher z-index numbers than the other controls, which creates a problem: on my listboxes closer to the bottom of the page the list expands below and not above, and part of the listbox goes under the bottom of the page but since onmouseout resets the size of the listbox I can't scroll the page down.
Does anybody know what I need to set to make it expand up instead of down?
edit Also, some may ask "why don't you just rearrange the listbox to a higher position in the page," the reason this isn't a viable option is I have well over 40 controls on the page and it they're grouped cohesively, I didn't just randomly place them where they are.(ie. investment info in one section, account in another, suitability in another)
EDIT: It's worth noting that the jQuery version of the below will be more compact and, in my opinion, more easily understood.
Glo, the code you have currently would be helpful here, especially since it seems you will have difficulty changing anything we give, or implementing what we might describe. Anyway, this works as intended in IE7, Firefox, and Opera; Safari and Chrome go quirky with it: http://jsfiddle.net/bUFzq/35/ (modified from http://www.plus2net.com/html_tutorial/html_frmddl.php).
The CSS just makes the select position-able relative to its default placement. Elements can only be positioned relative to other positioned elements. `position: relative;' leaves the element where it was until you move it, unlike absolute and fixed. It also positions relative to the edges of its nearest positioned ancestor. (The IT industry has the unfortunate convention of increasing Y downward rather than upward; just a heads up - or down.)
element.offsetHeight is the computed height of the element - how big it appears on the screen. element.style.bottom (like its cousins top, left, and right) sets the element's offset from the corresponding edge, in the direction of the element's center. setAttribute is fairly self-explanatory; it acts as if you were actually editing the HTML. Most properties of element.style (that aren't on all other objects) represent and modify similarly named CSS properties. For example, element.style.backgroundColor sets the background-color property.
addEvent is a function copied from Dustin Diaz's Rock Solid addEvent() because the browsers don't agree very well on how to do events. However, I would have put his script in a separate file and mine in yet another if I weren't working within a single script area. I did the `var addEvent = init();' thing just so you wouldn't have to scroll through his source, even though it is a good example of good code.
mouseover & mouseout are the actual listeners, explicitly called using apply 1) because I needed that height value for later and 2) because for some reason (at least within jsFiddle) it doesn't start out in the correct position, and only if the listeners are called in that order will it get there.