DOM manipulation performance when toggle visibility - javascript

I have bunch of images in a wrapper div. I need to hide all of them except the one user clicks on.
What is the most performance concise way of doing that?
Should I apply class on all of them to hide them all and than filter out the one that was clicked and show only that one or should I loop over them all hiding them as loop progresses with exception of the one that was clicked or something else?
Tnx

In modern desktop browsers you won't see any difference. Browsers are tuned so that they are blazing fast in rendering any changes is DOM three. Guts tell me that it might be sligtly faster to loop through all images and set visibility depending on item clicked using style attribute and not using class. In that way you have to process only N elements and no external CSS files are involved. If you hide all and show the element with was clicked, you process N + 1 elements.
In your situation I would use solution that is fastest, more managable and clean from the developers standpoint as there won't be much difference in the final result if you use one method or another.

PS: If you're using jquery, you can use the following:
Lets say, your div has id='test-div', and there are several images in it. All these images can be accessed as:
$('#test-div img')
Now, lets assume you know the id of image which got clicked. Lets assume id='my-image'.
You can execute the following to hide all other images (except 'my-image'):
$('#test-div img').not('#my-image').addClass('hide')

One of the most performant ways would be to let CSS do the visibility. It sounds to me like you're only displaying one at a time, in which case you can do it with two DOM operations by using classes;
// scope above
var lastClicked = null;
// then in click listener, 1st param `e`
if (lastClicked) lastClicked.className = ''; // remove visible class
lastClicked = e.target; // get clicked node
lastClicked.className = 'visible'; // add visible class
I'm assuming event.target but depending how the listener is attached, you might want to use this or some other logic. Further, if you expect element.classList support, you can use add and remove from that.
Example CSS of how to show only nodes with class token visible.
selector:not(.visible) {
display: none;
}

Related

Hide elements the correct way

I'm using JavaScript to hide and show some elements onclick events
Using this code
function showPreOne() {
document.getElementById('SecandModalFilter').classList.add('d-none');
document.getElementById('FirstModalFilters').classList.add('d-none');
document.getElementById('colocation').classList.add('d-none');
document.getElementById('coloc-row').classList.add('d-none');
document.getElementById('preFirstModalFilter').classList.remove('d-none');
document.getElementById('FirstModalFiltersa').classList.add('d-none');
}
I don't think this is the correct way! ? specially if I have a very large page with a lot of tabs and elements ?
Thank you
You could add a class on all the element that can be hidden (I assume you are handling a tab system), and just show the one you want to be visible:
function showPreOne() {
document.querySelectorAll('.tab').forEach(elt => elt.classList.add('d-none'))
document.querySelector('#SecandModalFilter').classList.remove('d-none');
}
Otherwise, your current method is not wrong per-say.
If the class is display: none, there's nothing wrong with that approach. Although the code would be more maintainable if you managed the add/remove elements with arrays of ids instead of doing it line by line.
Your approach can be a bit better, if you are showing/hiding elements I suggest you to use the toggle functionnality.
This way you can use only one function that will manage your click event and hide/show your elements, just be sure that the initial state (d-none class present or not) is correct :
function showHide() {
document.getElementById('SecandModalFilter').classList.toggle('d-none');
document.getElementById('FirstModalFilters').classList.toggle('d-none');
document.getElementById('colocation').classList.toggle('d-none');
document.getElementById('coloc-row').classList.toggle('d-none');
document.getElementById('preFirstModalFilter').classList.toggle('d-none');
document.getElementById('FirstModalFiltersa').classList.toggle('d-none');
}
More informations here Toggle specification

Javascript/HTML: How to implement a button that adds a click counter on click?

I understand how to implement a single click counter. However, I am struggling to figure out how to implement a button that will duplicate the click counter, while also allowing me to use the same javascript code.
My current layout for a single click counter has a div for the counter then a button for incrementing and another for decrementing. For the buttons, I am using a separate event listener for each one.
<div id="counter">0</div>
<button id="increment">+1</button>
<button id="decrement">-1</button>
Javascript:
var incr = document.getElementById('increment');
var decr = document.getElementById('decrement');
incr.addEventListener('click', ...);
decr.addEventListener('click', ...);
How would I implement another button that would copy the counter? Also, how do I approach the javascript side? My current approach does not seem to generalize well for multiple counters.
To generate a new div you will need to create it and append it to the DOM.
Something like:
const myDiv = document.createElement('div');
myDiv.id = 'id-blabla';
document.body.appendChild(div);
You can wrap that in the click handler.
Now, if you want to update several counters at the same time on a single button click, you will need to use another selector, instead of the id. Use a class or similar, like: <div class="counter"></div>, and then in the click handler get all those elements and iterate over them updating the counter value.
Does that make sense to you?
(Also, this link I think it will be very helpful for you, in terms of iterating over DOM elements and other concepts).
EDIT: This other answer on DOM manipulation is going to be very useful too!
EDIT2:
I understand now that what you want is to be able to generate counters (with the div and the buttons), that will have their click handlers and everything set it up correctly. You can use different approaches here, but the main purpose is to not repeat yourself:
You need your buttons to be able to identify the counter div target. You can achieve this using a custom data attribute in your buttons, like: <button class="increase" data-counterId="counter1"></button>. This way you would only need to write the click handler once (on all elements with "increase" or "decrease" class, and in the code extract the counterId data attribute so you would be able to edit the correct div counter.
You can achieve the same having some sort of common part in the id's of each block of HTML elements, like counter-1, increase-1 and decrease-1. Then each button would know which index it has, and which index the counter target should have.
This definitely looks like a component you could wrap in a Counter class. I don't know how familiar you are with JS UI frameworks, but depending on how complex your system is, you might want to think about using one. I recommend you to take a look to this link that explains React Components. Maybe it is out of scope of what you need, but I think it is worth to mention it.
I think with this information you have a good starting point to decide how you want to implement it, based on the size and complexity of what you want to build. I hope it helps!

Is there a more efficient way to show DOM element

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

JQuery Toggle Method Slow on 1000+ UL

I am working on a project where I am building a treeview and in some cases my tree could have a 1000+ child nodes. The problem is its really slow on like a IE7 machine.
I'm not doing any kind of animation or anything, just simply trying to hide the next UL in the item using JQuery's toggle feature. Does anyone have any ideas of how to improve the performance?
Thanks!!
If toggle is slow, you can set css styles directly via jquery like:
$(".tree_item").click(function(){
//check the next ul if it is expanded or not
if(this.next('ul:hidden').length == 0){
//if expanded hide it
this.next('ul').css('display', 'none');
}else{
//if not shown, show it
this.next('ul').css('display', 'block');
}
});
such approach would help. I don't know if it would work faster but give it a try...
Sinan.
I'm not surprised at all that this is slow if your treeview is that big. Silverlight 3 handles this problem with UI Virtualization.
You'll have to roll your own in javascript, but it shouldn't be that hard. Just make a huge blank div that's the size of what the rendered tree would have been, and put it inside a scrollable div, and then only render what should show up. Change it on the onscroll event.
You can try to build at start your own tree object from DOM document.
Just iterate through all elements and nest them in standard attributes and variables. You can make additional parent and children pointers using $(element).get(0).parent = $(parent).get(0);
Then if you want to get specified elements use $.map function.
We used it to prepare something like firebug on a project. It rebuilded all 5000+ nodes portal in 3 sec and provided fast access on ie6+
I've found that .toggle(showOrHide) is fast. It was added in jQuery 1.3 and really makes a difference on large collections (50+ elements) in IE8 if animation is not required. The current visibility state can be obtained from the first element, e.g.:
var isVisible = $('.myClass').first().is(':visible');
$('.myClass').toggle(!isVisible);

Logic behind hiding elements

I am having a dilemma in the logic of this particular issue. Forgive me if this is quite newbie question but I'd rather have a solid bg on it.
There are a lot of examples of this all around the web where you click on an element to display another element. such case may be a menu that when you hover your mouse on it (or click on it) its get displayed. Later the element gets hidden either on mouse out, OR CLICKING ON ANY OTHER ELEMENT.. so, how is this achieved? I am sure the solution is not to bind a "hideElem" function on all the elements.
regards,
I haven't done it in a while, but an easy solution is to add a click event to the top of the DOM tree that will close the open element. Here's an example in psuedo-javascript:
document.body.onclick = function() {
element.style.display = "none";
}
If you need complex behaviors inside the "shown" element, make sure your preventing the necessary events from propagating up the DOM tree.
element.onclick = function(e) {
e.stopPropagation()
}
In general, the logic is the other way around (at least with menus) i.e. the element in question is hidden until a state-event unhides it, then hidden again as dictated. The point being that the hiding/unhiding logic is usually tied to the element itself, not everything else on the page.
As to how it's done, methods vary. There are lots of Javascript solutions, mostly along the lines of those already outlined, but menus can also be done purely with CSS - typically utilising the display: none; property, though you can also do stuff like setting/unsetting a negative margin so that the element is moved 'off and on the page'.
To use some of my own work by way of example:
Drop-down menu with Javascript
Drop-down menu with jQuery
Drop-down menu with CSS
$('#target').bind('click', function(event)
{
var $element = $('#element');
$element.show();
$(document).one('click', function()
{
$element.hide();
});
// If you don't stop the event, it will bubble up to the document
// and trigger the click event we just bound.
// This will hide the element right now just after showing it,
// we don't want that.
event.stopPropagation();
}
You have to keep in mind that a Javascript event goes up and down the whole tree when begin fired. So you can bind event listeners to any parent when you want to listen for an event on many elements.
This is called event delegation.
A cheap way to do it potentially is to bind an event handler to the "(on)blur" event of the clickable item and/or it's target. If your design allows.
That is one way to do it.
You could also write a method that traps (hooks into) all 'click' events regardless of the element, and hide your menu from there.
JQuery would make this task easier for you.
step 1- use a javascript library so you can have the code be as cross browser as possible - otherwise you have to cater to two different event models between internet explorer and gecko/webkit based browsers. JQuery, Mootools, YUI - all will handle this for you - there are more but those 3 are my favorite and are well documented.
step 2 - you prob would want to implement a clickshield for this - essentially a block-level dom element that is absolutely positioned over your entire page with a higher z-index than the rest of the page. attach a click event to that, and you can perform your logic for hiding elements on the page. The clickshield could easily have javascript code expand it to the width -height of your page post DOM rendering using the methods of any of the aforementioned javascript libraries.

Categories

Resources