re-render vs visibility hidden - javascript

I have a simple component of conditional rendering.
And I can make 2 ways for this.
function SimpleComponent({ visible }){
return (
<button style={{position: 'absolute'}}>
{visible && <p>Visible!</p>}
<p style={{ visibility: `${visible ? 'visible' : 'hidden' }}>Visible!</p>
</button>
);
}
But I am not sure which is a better way for efficiency.
Please give me an idea or opinion about it.

It really depends on your needs. If you want to hide the element, but do not remove it from the DOM tree (so you can have access to it - apply some animations, pop it out smoothly, measure element height/width etc), use css visibility property.
If you just want to hide it and you don't want to access it while it's hidden, use condition visible && .... That element won't be rendered in the DOM tree at all.
Note: In matter of efficiency - the second approach is better since the less elements in the DOM tree, the better.
Note also that even if element has applied visibility: hidden property, it still takes space in your app (in the viewport) and can affect other elements position.

Related

Find scrollable container of an element, including in shadow DOM

I'm looking for a generic solution to find the scrollable container of an element (I'd like to listen to the scroll event).
The normal approach would be to traverse up in the DOM tree in check the parent elements until one of them is scrollable - here are some solutions like this.
However this will fail if shadow DOM is also in the picture.
Here's an example structure:
<app-home>
<ion-content>
#shadow-root
<div class="inner-scroll">
<slot>
-> <my-element> (reveal)
</slot>
</div>
<my-element>...</my-element>
</ion-content>
</app-home>
In the above example the scrollable element is .inner-scroll, which is inside the shadow dom of ion-content. Since my-element gets into the container through the slot, traversing up the tree will never reach the scroll container (my-element -> ion-content -> app-home).
I found that ion-content has a getScrollElement method, which can be used in this specific case, however I'd like to know if there's a generic DOM based solution, since I'd like this to work regardless of the context.
UPDATE:
To be more clear, I'd like my-element to be a standalone, reusable component, which can find it's scrolling container, no matter where it's placed.
What I found, that the built in scrollIntoView method works on the DOM element, and it scrolls the correct container, meaning that the browser somehow figures it out, but does not expose a method which returns the scroll container.
Another idea was to listen to the scroll event on a higher level, without knowing the exact element, but this is not working either, because the scroll event does not propagate.
Any ideas?
Get the <ion-content> element and use its shadowRoot element to query the scrollable element.
const ionContent = document.querySelector('app-home > ion-content')
const scrollable = ionContent.shadowRoot.querySelector('.inner-scroll')
Then you can use querySelector in your scrollable element to find the desired element. If there are more scrollable elements in the shadow DOM, you can use querySelectorAll to return an array of nodes.
It may be too late, but answering if it helps anyone else looking for an answer.
One possible solution is to use the getComputedStyle w.r.t the parent element and keep iterating upwards until you find a minimum of 2 combinations - height+width is longer than its own parent + it has overflow property (via getComputedStyle) set to anything that allows scroll.

Force browser to trigger reflow while changing CSS

I am building non-jQuery responsive image slider based on CSS3 transitions.
The structure is simple: a viewport and inside relatively positioned UL with left floated LIs.
I am facing a problem in such situation:
User clicks "prev" arrow.
JS appends proper LI before currently displayed LI node.
For now UL has set CSS transition as none 0s linear to prevent animation changes. In this moment I decrease UL CSS left value by slider width (let's say: from 0px to -1200px) to make view the same as it was.
Now I am changing UL's transition property to all 0.2s ease-out.
Now I am changing UL's left property to trigger CSS3 animation. (let's say: from -1200px to 0px).
What is the problem? Browser simplifies changes and does not make any animations.
Stoyan Stefanov wrote about reflow problem at his blog here, but in this case trying to force a reflow on element doesn't work.
This is a piece of code doing this (I skipped browser prefixes for simplification):
ul.style.transition = 'none 0s linear 0s';
ul.style.left = '-600px';
ul.style.transition = 'all 0.2s ease-out';
ul.style.left = '0px';
Here is fiddle to see problem in action: http://jsfiddle.net/9WX5b/1/
Requesting the offsetHeight of an element does everything nicely. You can force a reflow using this function and passing it the element that styles have been changed on:
function reflow(elt){
console.log(elt.offsetHeight);
}
And call this where reflows are needed. See this example: http://jsfiddle.net/9WX5b/2/
EDIT: recently needed to do this, and wondered if there was a better way than to console.log it. You can't just write elt.offsetHeight as it's own statement, as the optimizer (Chrome's, at least) will not trigger a reflow because it is just accessing a property with no getter set, no need to even evaluate it. So, AFAIK the cheapest way to do this is void(elt.offsetHeight), as it does not know for sure if void has side effects or not. (could be overridden or something, idk).
function reflow( element ) {
if ( element === undefined ) {
element = document.documentElement;
}
void( element.offsetHeight );
}
It works OK with Chrome and FF, and seems to be the simplest and most portable way to do it ATM.

setting visibility vs. hide/show

What is the difference between element.css('visibility', 'visible') and element.show(). Also, what is the difference between element.css('visibility', 'hidden') and element.hide()?
Update: In addition, what is the most proper way to hide an element and all its elements' subtree?
Update N2: Which is the proper way to know if an element (and its subtree) is visible: element.is(':visible') or element.css('visibility')?
Update N3: Is there a way to hide an element (completely), but it still will reserve the space/area on the browser page? (as far as I've got it - the proper way would be to call hide() but it might lead to the page visual restructuring.
Visibility will still reserve the space in your Browser.
A hidden element is set to display: none thus all space occupied by this element collapses.
If you only set the element to visibility: hidden the element will just go transparent but the space is occupied as if the element is still there.
.hide() is equal to .css('display', 'none')
.show() is equal to .css('display', 'block') - I'm pretty sure jQuery does some magic here to decide if it's really block that should go in there but it's somewhat equal.
#Update:
Once you hide an element with .hide() (or .css('display', 'none')) all elements down the dom-tree that are children of that element will be hidden too.
#Update 2:
If you are using .hide() and .show() it's .is(':visible')
If you are using the visibility css attribute then .css('visibility')
#Update 3:
That's exactly what .css('visibility', 'hidden') does, it hides the element without the page restructuring. .hide() will completely "remove" the element.
jquery.hide() is equivalent to calling .css('display', 'none') and and jquery.show is equivalent to calling .css('display', 'block')
The .css() method is just setting the visibility property.
From the css point of view difference :
visbility : hidden
The value hidden makes the generated boxes invisible without removing them from the layout. Descendant boxes can be made visible. See this
display : none
A value of none makes the element generate no box at all. Descendant boxes cannot generate boxes either, even if their display property is set to something other than none.See this
With hidden the element is still generated.
Taken from w3schools.com:
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 the element is not there:
In addition to bardiir's explanation here is good demo of "display:none" and "visibility:hidden"
http://www.w3schools.com/css/css_display_visibility.asp
"remove" button sets "display:none" and "hide" button sets "visibility:hidden".
They are setting 2 different css properties: hide/show sets the display property to none, show removes this setting so that the default is used (e.g. 'block' for a div).
The difference as the other answers point out is that calling hide on an element means that it (and all its ancestors) will not take up any space. Where as setting visibility to hidden will effectively just make the elements completely transparent (but still take up space).
For answers to your edits:
Hide all ancestor (this is automatically done with both methods).
Use element.is(':visible') since this performs a check on both the visibility and display properties and to see if the height and width aren't 0, it also performs it recursively on the ancestors, whereas element.css('visibility') just tells you the visibility of the element.
Setting element.css('visibility', 'hidden') will do this - not calling element.hide().

HTML / CSS/ JS: Is it possible to find the element's size before appending it to the DOM tree?

I'm creating some HTML elements dynamically via JavaScript. Something line this:
var author = document.createElement("div");
author.innerHTML = _feed[i].author;
author.className = "author";
The browser returns offsetHeight = 0 if the element is not added to the document yet.
I want to know the element's height before appending them to the document because if the element's resulting height is too big I need to take some actions (make it smaller), and only after that add it to the document.
My suggestion is to add it to the page with a left margin of -10000 or something, so that it's not visible. Then you can get the dimensions, do what you need, and then change the left margin so that it's where you want it. This is assuming you would use absolute positioning.
But so far as I know, the browser will not report that information on elements not in the DOM.
Dan the problem you are going to have is that unless you add it to the DOM then you can never accurately know what the height will be. The reason for this is that as the element is not in the DOM, it cannot be affected by styles set in CSS or the browsers default styles. Even if the element itself has no specific styles applied to it then its parent element may do, or even its grandparent element (did I just make grandparent element up?).
The only accurate way of getting the height is to place the element exactly where it will sit in the DOM and then detect the height. I don't think visibility:hidden; will work as its presence will still be felt by elements around it (pushing margins around and positioning of other elements).
How about position:absolute; left: -5000px;? That may work.

Javascript clientHeight and alternatives

I am currently trying to modify a Javascript function that "slides in" a <div>. The script as it is requires you to define the height of the div, so it is mostly useless in dynamically filled <div>s. I found some text on the clientHeight property in javascript, but it would appear that it doesn't support <div>s with display set to none (which is the method used to slide the div in). That makes sense, as the height of that div in the client window is nothing.
Basically I was wondering what other methods you all know of, or if there's a way to get around the clientHeight = 0 when display: none.
Thanks!
Oh, and here's the function I'm using:
function getDivHeight(objName) {
return boxHeight = document.getElementById(objName).clientHeight;
}
A simple solution is to set it's visibility to "hidden" and it's display to "block" and measure it. However, some modern browsers will manage to update the page layout during this short time and you will get a nasty flicker. The easiest way to overcome this is to place the element in an absolutely positioned container with overflow set to "hidden".
I've had luck cloning the element, moving it offscreen, then displaying it to get the client height:
var original = document.getElementById(some_id);
var new_item = original.cloneNode(true);
document.body.appendChild(new_item); // item already hidden, so it won't show yet.
// you may wish to validate it is hidden first
new_item.style.position = "absolute";
new_item.style.left = "-1000px";
new_item.style.display = "block";
var height = new_item.clientHeight;
EDIT: Looking through the jQuery code, they do exactly what Tsvetomir Tsonev suggests. jQuery temporarily sets the style to "display: block; position: absolute; visibility: none", and then measures the height, swapping the properties back after the measurement.
So, it looks like you're stuck with having to do something hackish, whether it's cloning the node or risking having it flicker in some browsers... I like Tsvetomir's suggestion better than my initial hack as it, at least, doesn't involve cloning a node into the DOM that you don't need. Either way, the element must not be set to "display: none" in order to measure it's height. Isn't the DOM wonderful? :-)
EDIT 2: Also worth noting that, after jQuery gathers the height, it adds allowances for padding, margin and border sizes, so you may need to as well.
Yes, an element that is not displayed on the page has no dimensions.
It kind of makes sense. Consider an element that has been created and filled with a bunch of text, but not yet added to the document tree. How high is it? Depends on font-size. How big is font-size? Depends where in the document that div is inserted; its parent font-size would inherit through.
Similarly for an element with “display: none”. It's not rendered, so it has no dimensions. Couldn't we ask “how high would this be if it were ‘display: block’”? Turns out no, because if it were displayed, that in itself could change the dimensions of its parent block, and then the dimension of displayed elements would be inconsistent with the dimensions of non-displayed elements!
The typical solution is to unset “display: none”, measure the height of the element, and then immediately re-set “display: none”. The browser won't redraw in the middle of a bit of JavaScript, so you won't see a flicker on the page.
I nkow you guys solved this a long time ago but I thought I should share this since it quite tricky to get the height of a hidden div tag.
heres what I did after reading your post,
I placed the div i want to slide inside a 1px height div with overflow set to hidden.
you dont even need to set the display of the inner div to none since it is already there and if you use offsetHeight it should return the proper height for all browsers and you can use that height to slide your div up an down.
PEACE!!!
In IE you could try scrollHeight, but I'm not sure if it will work or if it is cross browser.

Categories

Resources