I've built a large table in bootstrap, about 5,000 rows x 10 columns, and I need to filter the table for specific attributes, fast, using only JavaScript. The table has both an id column and an attribute column, i.e.
id | attr | ...
---------------
2 | X | ...
3 | Y | ...
4 | X | ...
To make the filtering process fast, I built a hashtable table that maps the attributes back to the column ids. So for example, I have a mapping:
getRowIds["X"] = [2,4]
The user can enter the attribute "X" in a search box, the hashtable then looks up the corresponding rows that contain "X" (2 and 4 in this case), and then calls the following functions via a map operation:
this.hideRow = function(id) {
document.getElementById(id).style.display="none"
}
this.showRow = function(id) {
document.getElementById(id).style.display=""
}
This process is still quite slow, as the user is allowed to select multiple attributes (say X,Y).
Is there a faster way of hiding the rows?
Would it be faster if I could somehow detach the table from the DOM, make the changes, and then re-attach? How do I do this in javascript?
Are there other more efficient/smarter ways of doing the filtering?
Thanks :)
Here is a on the fly filter solution, that filter the table using letters typed in input box on
keypress
event.Though right now I am using DataTables in my current project development, yet if you want a strict
javascript
solution here is it. It may not be the best optimized but works good.Cheers!!
see this link it might help, the only problem is its not in pure javascript it also uses angularjs.
Using AngularJS can indeed be a good idea, which lets us render your rows as simple as
where you only need to supply your
rowArray
as array of objects like{id: 1, attr: 'X'}
, see the documentation forng-repeat
directive. One ofAngular
's big powers lies in its extremely compact code.Among other things, Angular also has powerful filter building library to filter and sort your rows on the fly right inside your HTML:
Having said that, it'll clearly be a performance drag to throw 5K rows into your array. That would create a huge HTML in your browser memory that, however, will not fit into your viewport. Then there is no point to have it in the memory if you can't show it anyway. Instead you only want to have the viewable part in your memory plus possibly a few more rows around.
Have a look at the directive "Scroll till you drop" provided by Angular UI Utils - it does exactly that!
Pagination as mentioned in another answer is surely a valid alternative to the infinite scroll. There is lot written on the web about strengths and weaknesses of pagination vs infinite scroll if you want to dig into that.
Speaking of your code specifically, it has other performance drags. For instance, on each call, this function
will look up the DOM for the element by its
id
, and then will look up its property.style
(which can be a drag if the JavaScript needs to go high up in the Prototype chain). You could do much better performance wise by caching direct reference links to thedisplay
properties, which are the ones you really need.EDIT. By caching here I mean pre-compiling a
hash
linkingid
with the interesting properties:Then you switch the style by simple setting:
This way of calculating
hash
assumes that your elements are all inside the DOM, which is bad for performance, but there are better ways!Libraries like
jQuery
and, of course,Angular
:) will let you create your HTML elements with their complete style properties but without attaching them to the DOM. That way you are not overloading your browser's capacity. But you can still cache them! So you will cache your HTML (but not DOM) Elements and their Display like that:and then attach/ detach your elements to the DOM as you go and update their
display
properties using the display cache.Finally note that for better performance, you want to concatenate your rows in a bundle first, and only then attach in a single jump (instead of attaching one-by-one). The reason is, every time your change the DOM, the browser has to do a lot of recalculation to adjust all other DOM elements correctly. There is a lot going on there, so you want to minimize those re-calculations as much as possible.
POST EDIT.
To illustrate by an example, if
parentElement
is already in your DOM, and you want to attach an array of new elementsthe way you want to do it is:
as opposed to running a loop attaching one
rowElement
at a time.Another good practice is to
hide
yourparentElement
before attaching, then only show when everything is ready.I would ask
I would suggest that you look at using one of the several JavaScript packages that already do this. There are many more packages that the two below. I'm showing these two as examples of what is available.
I whipped up a filtering solution that you might want to check out.
Features
How it works
The JavaScript is very simple. All it does is create a unique class name for each filter and add it to every row that matches the filter parameters. The class names can be used to determine which rows a given filter is currently filtering, so there's no need to store that information in a data structure. The classes share a common prefix, so they can all be targeted by the same CSS selector for applying the
display: none
declaration. Removing a filter is as simple as removing its associated class name from the rows that have it.The Code
If you want to show only rows that have a value of "X" or "Y" in column 2, the function call would look something like this:
That's all there is to it! Instructions on removing a filter can be found in the demo code below.
Demo
The demo in the code snippet below allows you to apply any number of filters with any number of values to a 5000 row table like the one the OP described, and remove them afterward. It may look like a lot of code, but most of it is just for setting up the demo interface. If you were to use this solution in your own code, you'd probably just copy over the first two js functions (addFilter and removeFilter), and the first CSS rule (the one with
display: none
).*Performance will vary depending on how much CSS is being applied to the table rows and cells, and whether that CSS was written with performance in mind. Whatever filtering strategy you use, there's not much you can do to make a heavily- or inefficiently-styled table perform well, other than load less of it (as others have suggested).
Your best option is to not render all those things and store object versions of them and only show a max of 50 rows at a time via pagination. Storing that many objects in memory, in JS is no problem. Storing all of those in DOM on the other hand will bring browsers to their knees. 5000 is at around the upper bound of what a browser can do on a good machine while maintaining decent performance. If you start modifying some of those rows and tweaking things ('hiding', 'showing') things definitely will get even slower.
The steps would look something like:
The following code should be considered pseudo code that probably works: