JQuery Toggle Method Slow on 1000+ UL - javascript

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);

Related

DOM manipulation performance when toggle visibility

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;
}

hide all divs that contain a phrase inside them

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).

Best way to manipulate many sliding objects with moo tools's Fx.Slide

I'm trying to think of the most elegant solution to handle multiple Fx.Slides within mootools. I'm developing a dictionary page with a very long list of words where there's a pair word -- translation and all the translations must be hidden by default showing just a word list. I'm looking for a solution that won't require creating a separate slide for each word on the page, so that they're created on-the-fly when a visitor clicks on a word because the size of the script and performance hit concern me. There's another problem in that their initial states must be set to 'hidden' beforehand and I don't want to do it in CSS (that would hide everything from people whose browsers don't support javascript).
Is anything of this sort possible or will I have to rely on creating slides in a loop (my element ids go like w01, w02, ...)? If so, how would I put that block inside a loop?
Check out this question regarding if the user does not have Javascript Embedding extra styles with noscript.
After that is taken care of we can concentrate in mootools. You want the elements to have visability: hidden when you load the page with Javascript. Give your elements a class so we can select them all at once. Example to initialize the elements.
$$('.sliders').each(function(el) {
el.slide('hide').setStyle('visibility', 'visible');
});
Now we need to handle the click event. The same goes here.
Example html:
<h3 class="slideIn" >Some title</h3>
<div class="sliders>Some lengthy text<div>
Example html:
$$('.slideIn').addEvent('click', function() {
this.getNext().getChildren('.sliders').slide();
});
​
Example fiddle: http://jsfiddle.net/b4Zjs/
Edit: If there are a lot of elements that should have click events it's better to use event delegation. Then you are only adding one event listener to the page, and it can make a huge difference some times.
$('parent').addEvent('click:relay(h3.slideIn)', function(event, target) {
target.getNext().getChildren('.sliders').slide();
});
jsFiddle example: http://jsfiddle.net/b4Zjs/2/

Fastest way to hide thousands of <li> elements?

I have an autocomplete form where the user can type in a term and it hides all <li> elements that do not contain that term.
I originally looped through all <li> with jQuery's each and applied .hide() to the ones that did not contain the term. This was WAY too slow.
I found that a faster way is to loop through all <li> and apply class .hidden to all that need to be hidden, and then at the end of the loop do $('.hidden').hide(). This feels kind of hackish though.
A potentially faster way might be to rewrite the CSS rule for the .hidden class using document.styleSheets. Can anyone think of an even better way?
EDIT: Let me clarify something that I'm not sure too many people know about. If you alter the DOM in each iteration of a loop, and that alteration causes the page to be redrawn, that is going to be MUCH slower than "preparing" all your alterations and applying them all at once when the loop is finished.
Whenever you're dealing with thousands of items, DOM manipulation will be slow. It's usually not a good idea to loop through many DOM elements and manipulate each element based on that element's characteristics, since that involves numerous calls to DOM methods in each iteration. As you've seen, it's really slow.
A much better approach is to keep your data separate from the DOM. Searching through an array of JS strings is several orders of magnitude faster.
This might mean loading your dataset as a JSON object. If that's not an option, you could loop through the <li>s once (on page load), and copy the data into an array.
Now that your dataset isn't dependent on DOM elements being present, you can simply replace the entire contents of the <ul> using .html() each time the user types. (This is much faster than JS DOM manipulation because the browser can optimize the DOM changes when you simply change the innerHTML.)
var dataset = ['term 1', 'term 2', 'something else', ... ];
$('input').keyup(function() {
var i, o = '', q = $(this).val();
for (i = 0; i < dataset.length; i++) {
if (dataset[i].indexOf(q) >= 0) o+='<li>' + dataset[i] + '</li>';
}
$('ul').html(o);
});
As you can see, this is extremely fast.
Note, however, that if you up it to 10,000 items, performance begins to suffer on the first few keystrokes. This is more related to the number of results being inserted into the DOM than the raw number of items being searched. (As you type more, and there are fewer results to display, performance is fine – even though it's still searching through all 10,000 items.)
To avoid this, I'd consider capping the number of results displayed to a reasonable number. (1,000 seems as good as any.) This is autocomplete; no one is really looking through all the results – they'll continue typing until the resultset is manageable for a human.
I know this is question is old BUT i'm not satisfied with any of the answers. Currently i'm working on a Youtube project that uses jQuery Selectable list which has around 120.000 items. These lists can be filtered by text and than show the corresponding items. The only acceptable way to hide all not matching elements was to hide the ul element first than hide the li elements and show the list(ul) element again.
You can select all <li>s directly, then filter them: $("li").filter(function(){...}).hide() (see here)
(sorry, I previously posted wrong)
You can use the jQuery contains() selector to find all items in a list with particular text, and then just hide those, like this:
HTML:
<ul id="myList">
<li>this</li>
<li>that</li>
<ul>​
jQuery
var term = 'this';
$('li:contains("' + term + '")').hide();​
You could use a more unique technique that uses technically no JavaScript to do the actual hiding, by putting a copy of the data in an attribute, and using a CSS attribute selector.
For example, if the term is secret, and you put a copy of the data in a data-term attribute, you can use the following CSS:
li[data-term*="secret"] {
display: none;
}
To do this dynamically you would have to add a style to the head in javascript:
function hideTerm(term) {
css = 'li[data-term*="'+term+'"]{display:none;}'
style = $('<style type="text/css">').text(css)
$('head').append(style);
}
If you were to do this you would want to be sure to clean up the style tags as you stop using them.
This would probably be the fastest, as CSS selection is very quick in modern browsers. It would be hard to benchmark so I can't say for sure though.
How about:
<style>
.hidden{ display: none; }
</style>
That way you don't have to do the extra query using $('.hidden').hide() ?
Instead of redefining the Stylesheets rules, you can directly define 'hide'
class property to "display:none;" before hand and in your page, you can just
apply the class you defined after verifying the condition through javascript,
like below.
$("li").each(function(){if(condition){$(this).addClass('hide');}});
and later, if you want to show those li's again, you can just remove the class like below
$("li").each(function(){if(condition){$(this).removeClass('hide');}});

how to determine how many jQuery objects are on a page?

Can I determine the number of jQuery objects on a page?
I want to use the number of elements as a sort of weak benchmark for page complexity. I'm thinking that if I can reduce the number of elements that jQuery knows about , the page might run more efficiently.
Does this make sense?
Is it as simple as doing a * select and counting the results?
related:
How can I clear content without getting the dreaded “stop running this script?” dialog?
http://api.jquery.com/size/
var elementCount = $('*').size();
Although this might be more what you want:
var elementCount = $('body').find('*').size()
var n= 0;
for (var i in jQuery.cache)
n++;
Now n holds the number of elements jQuery has ‘touched’ (added data to, such as event handlers).
Previously this used to be a whole lot, as it would ‘touch’ every element it was even checking for data. This unpleasantness is fixed in jQuery 1.4.
As for clearing content, yes, you can use innerHTML= '' to remove all the content without giving jQuery the chance to detach its data so very slowly. If you know there are no ‘touched’ elements inside the element that's a win, but otherwise it's a potential memory leak until the page is reloaded, as unused jQuery.cache data stays around.
Using live()/delegate() event binding avoids adding data to its target elements, which can allow you to use this short-cut more freely. However if you have a lot of events to bind and they're not selectors that are very easy to match quickly, this can make event handling slow.
(Because there is no browser-native speedup like querySelectorAll for matching elements against a particular selector as delegation needs to do; this is proposed for Selectors-API level 2.)
var elementCount = $('*').length;
This doesn't really have much to do with jQuery except insofar as it's a handy way to get the answer.

Categories

Resources