I need to determine the height of a DOM element using javascript - specifically, in my case, a div containing some text. Due to the way that HTML works, I can only reliably do this if the element is visible. The general purpose solution is to show the element, get it's height, and then hide it - simple enough in the single element case.
However, in the general case, the element in question may be a child of other elements that are hidden, thus preventing the above solution from working - calling jQuery's show() function on the element in question doesn't actually cause it to be shown due to the hidden parent, so you still can't get the height.
How can I make an element visible long enough to get its height, taking into account any parent elements that need to be made visible to make it work?
Use case: I'm trying to write some code that I can apply to any table element, that creates some other elements whose height should match the height of the table header. I want to keep the code generic enough that it will work regardless of where in the DOM the table is located, or if it is currently visible. An alternate solution would be to have some javascript that simply adjusts the size of the created elements when the size of the table header changes (such as when it is shown), but conceptually that seems less efficient. Still, if it is easier, I will accept that as an answer.
Edit: To give an example, while keeping in mind I am going for a general solution that is not tied to this specific HTML layout, consider the following HTML:
<div style="display:none; line-height:22px; font-size:18px;">
...Some random text or other content...
<div id="desired_size">
I want to find the height of this div when visible
</div>
...Possibly some more content/other stuff...
</div>
The goal is to get the height of that inner div, but I can't do that because it isn't displayed - it is hidden due to the parent div being hidden. If all I know about the HTML is the desired_size div, how would I go about making it visible enough to get the height? Granted, this example is trivial, but I'm trying to generalize it.
Edit 2: One suggestion was to clone the element and move it to somewhere that is visible. This works, but with a caveat: any inherited CSS properties that would affect the size are lost.
Edit 3: I'm trying to write a block of code that I can re-use in a variety of web pages, not just coding to a specific layout. As such, I can't make any assumptions about or changes to the parent HTML. The example above shows one case where this can cause difficulties.
Edit 4: As has been pointed out, it would be trivial to change the HTML such that the visual appearance is the same, but the issue doesn't exist. However, I am trying to find a solution that works with the HTML as written, regardless of how the HTML is written.
demo - http://jsbin.com/tosusanowu/edit?html,js,output
Assuming you know that desired_size div has always a parent that is hidden.
$(function(){
var desired_size = getDesiredSize('#desired_size');
});
function getDesiredSize(el) {
var $el = $(el), $parent = $el.parent(), desired_size = 0;
$parent.attr('style', 'opacity:0;position:absolute');
desired_size = $el.height();
$parent.attr('style', 'display:none');
return desired_size;
}
<div style="display:none;">
...Some random text or other content...
<div id="desired_size">
I want to find the height of this div when visible
</div>
...Possibly some more content/other stuff...
</div>
The following javascript/jQuery function should work in the general case where the HTML structure is unknown, as requested:
function getHeight(objectID){
var object=$('#'+objectID);
var nextObject=object;
var changedObjects=[];
var counter=0; //to prevent infinite looping
while(!object.is(':visible') && counter<100){
counter+=1;
var curObject=nextObject; //store a reference for use in loop
nextObject=curObject.parent();
var curStyle=curObject.css('display') //see if current object is hidden
if(curStyle!='none')
continue; //don't mess with non-hidden objects
//see if the display style is inline, or from a CSS file
var inlineStyles=curObject.attr("style");
if(typeof(inlineStyles)!=='undefined'){
inlineStyles.split(";").forEach(function(element){
var style = element.split(":");
if ($.trim(style[0]) === 'display') {
//Record the fact that the display properly is an inline style
curObject.data('floatinghead_inline_style',true);
return false; //break out of the forEach loop
}
});
}
curObject.show(); //if object is hidden, show it for now
visitedObjects.push(curObject); //save a reference so we can put it back later
}
var height=object.height(); //this should work properly, since object should now be visible
visitedObjects.forEach(function(item){
if(item.data('floatinghead_inline_style')===true)
item.hide();
else
item.css('display','');
})
}
The above code makes no assumptions about the HTML structure, particularly the depth of the object in question from the hidden parent element. Also, unlike the "clone item to a different, visible, location in the DOM" option, it should properly take into account any inherited attributes that affect the height.
Related
A value in tab header changed through JS value is not persisting on #media breakpoint/accordion screen - although the one in the tabbed content area is persisting.
The js code:
function check_1_input(X, Y) {
var ids=X.split("_"); console.log(' X '+X+' id '+ids[1]);
var PX=document.getElementById(X);
var PY=document.getElementById(Y);
PX.childNodes[0].textContent = "Changed";
PY.childNodes[0].textContent = "Changed";
}
which makes the changes is at the end of the html - not sure why it only works in that location.
The fiddle is
https://jsfiddle.net/PhilB/077dbf37/10/
The problem you're having is that you're using element IDs in your check_1_input function. The responsive tab plugin clones your tab elements on initialization to have both horizontal and vertical tabs with different classes that get hidden or exposed based on the media width, but it keeps the IDs the same. Since your function is looking for elements using getElementById, it will only find the first one.
Ultimately, you end up with two different elements with the same ID, which is bad practice. Using a class as the selector instead of the ID fixes the problem. I used jQuery to select the classes instead of ID's since you have jQuery loaded. You may consider removing the ID's all together in the tab elements, since the responsive tab plugin is always going to clone them and result in an invalid DOM (two elements with the same ID).
See my updated version of your fiddle here.
I want to implement a simple menu just by using Vanilla JS. So I have a working onclick function, where I just twist visibility property on click of the menu item. CSS is by default set on visibility:hidden
function getContentP() {
var div = document.getElementById("menu1Content");
if (div.style.visibility === "hidden") {
div.style.visibility = "visible";
} else {
div.style.visibility = "hidden";
}
};
<a href="#menu1" onclick="getContentP()">
<h2>title</h2>
</a>
<!-- CONTENT BOX, show on clicks-->
<div id="menu1Content" style="background: #fefefe">
Some content to make it visible
</div>
This works as expected, but really slowly, and with poor results.
Any good suggestion to improve the code? And maybe add some nice transitions like fadeIn effect without using jQuery?
Thanks a lot.
Im not sure what slowly means in this case or what poor results you are seeing, but in general the thing that jumps out at me is the usage of onclick. When passing code like that to a dom element the runtime will essentially eval the snippet which is bad for a number of reasons, and certainly not great for performance (also likely can never be optimized by the vm).
element.addEventListener('click', function() { 'your stuff here' }, false);
may give you better performance but id be shocked if you can even tell the difference unless this is called thousands or maybe millions of times.
You could also cache the dom element since you seem be doing a lookup by id, then you dont have to do a potentially expensive dom search every time the thing is clicked.
I'm not css guru but you can probably get something cool with this without too much effort.
[from comments] I usually need two clicks on the link to get it to show in the first place, which is really strange
No, that is anything but strange.
element.style.property only allows you to access CSS values that where set either directly via a style attribute on the element, or by assigning a value to element.style.property via script.
Both is not the case here, and your class="hidden" that you had in your code initially (before editing it out) was likely to set the element’s visibility hidden from the stylesheet, right?
Therefor, when you check the current visibility of the element here for the first time,
if (div.style.visibility === "hidden") {
that condition is not true, so you set the visibility to hidden in your else-branch – and therefor the element stays hidden.
Now, on your second click, the value hidden was set via script, so now you can read it back. Now that if condition results in true, and your script sets the element to visible.
Easy way to work around this – turn the logic around:
Check div.style.visibility === "visible", and set the element to hidden if that’s true, and visible if it is false.
The element is hidden (via the stylesheet) when your page first loads. Because of what I explained before, div.style.visibility can’t read the value on the first click, so the condition will be false, and the element will be made visible.
The HTML (note the added id):
<a href="#" id="menu1Toggle" style="visibility: hidden;">
<h2>title</h2>
</a>
The handler (note the added ev and preventDefault()):
function getContentP(ev)
{
var div = document.getElementById("menu1Content");
div.style.visibility = div.style.visibility === "hidden" ? "visible" : "hidden";
ev.preventDefault();
};
Attach the event with:
document.getElementById("menu1Toggle").onclick = getContentP;
You could use opacity if you want to fadeIn/Out. Just add a CSS transition. An easy toggle method would be:
elem.style.opacity = +!parseInt(elem.style.opacity);
Not sure if this would perform better/worse, but here is a Fiddle
I have a
<div id="content">
</div>
and three js variables that store different html: content1, content2 and content3.
By user interactions, the content of mentioned above div changes to one of that that stored in js variables.
What is preferable either to directly set div content to what I need by user interaction:
$("#content").html(content2);
or to change div structure to:
<div id="content">
<div id="c1">
// value of content1 variable here
</div>
<div id="c2">
// value of content2 variable here
</div>
<div id="c3">
// value of content3 variable here
</div>
</div>
And doing hide() and show() to that inner blocks, i.e when I want content2 to be shown:
$("#c1").hide();
$("#c2").show();
$("#c3").hide();
?
I'd say hiding & showing divs.
It's less intensive, and if the content inside the javascript variables happens to contain elements that you'll bind to, you won't have to rebind everytime you refresh the content, and if you wanted to have some sort of animation between the different content, multiple divs also allows that.
As a side note, using jQuery it's less code to do something like
$("#c2").show().siblings().hide();
The two aren't really all-that comparable since they do different things. They may well give a similar perception but what's happening isn't the same in terms of markup. In fact, it's not uncommon to see .html('Something').show() chained together.
Passing a string to .html() replaces the content of the selected element, it does nothing to affect the element itself.
Calling .show() or .hide() only affects the element itself - all the descendants remain exactly the same, they just can't be seen because their parent is not being displayed.
By using .html() you are replacing everything inside your element. All references to these descending elements will become undefined and direct (non-delegated) event listeners will also be lost.
.hide() and .show() do exactly what they say. The data inside your element is still preserved, the event handlers still in place, it's all just 'hidden' by way of display: none.
If the content dynamically changes, without page-load, use .html(), if not, .show() and .hide() are more appropriate.
For the ease of use and shorter more cleaner looking code, setting the content through HTML is the right option!
Think of it as what you're trying to do, 1 DIV => Can contain 3 different contents, you can manipulate it through JS.
So, in your first solution, you actually have one div and manipulating it through JS:
$("#content").html(content1);
$("#content").html(content2);
$("#content").html(content3);
Whereas, in the second solution, you are actually using 4 divs for the same functionality! So definitely, if you can do something with 1 div. That's the preferred way.
They both are taking equal lines for JS, but with the second approach, your HTML will contain a lot more code considering your contents are large.
I think that the best solution is to store the different contents into three variables and then assign to the div the choosen one with
$("#content").html(content2);
In this way you have three less nodes on your DOM tree
There isn't that much difference between the two options. One factor that might affect this is the actual size of the content you are changing. If the content is relatively small then it really doesn't matter which way you choose.
Another thing to consider is how available the three versions of the content variable is. If you have to fetch this HTML content each time you load it then it might make sense to pre-populate the content before you display it to your users so as to save the time it takes to load it. Then just show/hide the appropriate content.
I'd like to hide (remove) all <div> that contain a certain string. Like a filter. Think of it having the functionality of a browser add-on.
Basically anything that is
Within a tag within the div, or
Immediately inside the div (but not a tag)
Any ideas of how to best do this efficiently? Ideally I'd like a way to have the browser respond well to this by not only hiding the div, but removing it in such a way that the page has no large blank spaces.
I'm really not super fluent in javascript, so thought I'd see if I could get some pointers here. (And is better to use straight JS or JQuery?)
thanks!
First, make a plan:
Find all the nodes that have the phrase. This SO Q will help.
For each node you find, go up the dom tree until you find the first enclosing div to remove. See JQuery dom transversal helpers
Then remove the appropriate divs. You may want to animate their removal, use jquery for that too.
Re: Use jquery? Up to you. If you want your code to work on multiple browsers then jquery will help. Your problem can be solved with or without jquery.
Good luck.
Try my JQuery plugin searchEngine
Syntax
$(selector).searchengine(textFilter,action,caseSensitive)
For your case , invoke it as following :
var myTxt="dfvdf...."
$('div').searchengine(myTxt,'remove',false);
If you want to remove also all <p> that contains myTxt :
$('div,p').searchengine(myTxt,'remove',false);
if you want to hide it instead of removing it :
$('div').searchengine(myTxt,'hide',false);
if you want to show it after that :
$('div').searchengine(myTxt,'show',false);
and so forth
Demo : http://jsfiddle.net/abdennour/k3x53/1/
Hacked together from this answer: Get all visible DIVs on a page with javascript?
This is fairly simple with jQuery, but you requested non-jquery so I will give it a quick go:
function removeDivs(withString) {
var divs = document.getElementsByTagName("DIV");
for(var i = 0; i < divs.length; i++) {
div = divs[i];
if (div.style.display != "none" && div.innerHTML.indexOf(withString) > -1) {
div.style.display = "none";
}
}
}
I would note that you said "phrase inside them", but did not specify the level to which you wanted to find out that the phrase is inside. In the case of nested divs, this will hide all the divs as far up as it can go that have the phrase in their innerHTML. In other words, since innerHTML contains all the contained divs, any div containing a div that has the phrase in it will also be hidden, and so forth on up the tree.
Another note, this is not efficient at all in that it probably will end up redundantly hiding divs that don't need to be hidden.
If I were to do it, I would use jQuery and try to get something a little more efficient...it would be re-inventing the wheel not to IMHO (unless there are technical restrictions or browser restrictions that prevent jQuery from running).
There are a couple of ways I could do this (That I'm aware of).
Test css display
if ($('#foo').css('display') == 'none')
Test the visibility
if ($('#foo').is(':visible'))
In the visibility I can check if the element is there.
Elements are considered visible if they consume space in the document.
Visible elements have a width or height that is greater than zero.
Elements with visibility: hidden or opacity: 0 are considered visible,
since they still consume space in the layout.
Source
But, note that in both I can't test the visibility (by the user) because:
Hiding an element can be done by setting the display property to
"none" or the visibility property to "hidden". However, notice that
these two methods produce different results:
visibility: hidden hides an element, but it will still take up the same
space as before. The element will be hidden, but still affect the
layout.
display: none hides an element, and it will not take up any space. The
element will be hidden, and the page will be displayed as if the
element is not there:
Source
So in neither of the examples I test if the element is visible in all senses for the user.
So my question is:
What're the differences between the two if's codes from above?
What's the best way to test if an element is visible to the user:
Should I have to use something like:
if ($('#foo').is(':visible') &&
$('#foo').css('opacity') > 0 &&
$('#foo').css('visibility') != 'hidden')
I think your best bet is to implement a custom function like below and test/improve as new things come up,
$.fn.isReallyVisible = function () { //rename function name as you like..
return (this.css('display') != 'none' &&
this.css('visibility') != 'hidden' &&
this.css('opacity') > 0);
}
The above should be cross browser proof as we are using jQuery .css function (specifically for opacity).
DEMO
The difference between the two is that being hidden using "visible" attribute leaves the element on the page, just not actually displayed. So, it's spacing will be taken into account when the page renders the rest of the display.
It seems that doing it the other way actually stops the element from being put onto the page, which can change how other elements on the page are laid out.
usually testing the visible part is enough from my experience, but if you are wanting to be more complete, then yeah you would have to check using "&&" conditions on multiple attributes.
It really all depends on how clean the code you are using is, and how well tested the rest of the UI aspect of the system is.
The other thing to consider is what is the purpose of the test. Are you testing code that you wrote, or how the browser is using Javascript to render the page? You want to be testing the code that you are creating, and rely on the fact that the browser works (because if the browser stops working, then the whole thing is unreliable anyway). So if your code tells the element to set some attribute, then checking for that attribute is all the testing you need to do. Anything on top of that can only really be proven by testing outside of the code itself (as in manualy looking at the page and other things like that).
If you want to see if an element exists in the DOM you could just do this:
$.fn.exists = function () {
return this.length > 0;
}
Usage:
$('#myid').exists();