Javascript caching tools and techniques - javascript

I have a requirement where in I want to loop through all the DropDown (select) controls on a page. I am picking all the DropDown controls using:
var DropDowns = document.getElementByTagName('select');
I loop through these DropDowns to set an option programatically of few controls:
for (i=0; i<= DropDowns.Length; i++)
{
var CurrentControl = DropDowns[i];
CurrentControl.options[CurrentControl.selectedIndex].value = 1;
}
Is there any Javascript framework which supports caching? I want to cache all the DropDown control names and loop through the cached names instead of directly looping through Document object.
Are there any tricks to increase the performance of the loop?

I don't think that you're going to see huge improvements in performance for a loop that is only iterating through 30-40 elements, but some browsers will get a huge speed boost from looping through an array instead of a NodeList or HTMLCollection as it is called in some browsers that implement DOM level 1.
So, to answer your question, you can "cache" the objects etc. in an array and it should speed up that loop for future iterations.
Be aware that you need to keep this array up-to-date because it is not "live" like the DOM is.

In jquery, it would look something like this:
var $menus = $('select');
$.each($menus, function(index, menu) {
// Do whatever you want to the menu here
};

I am suggesting a different approach -
Save the options in a hidden field with a comma separated values and on page load set the values of control by parsing the , separated values.

I think you're already doing this much more efficiently than some of the answers describe. Bear in mind you're not continuously looping through the document, you're looping through your cached NodeList of DropDowns. If you want to change each one of these elements, then you will inevitably need to loop through them to change them individually.

Related

source clear BUG in OpenLayers 3

I'm using OpenLayers 3 (v3.20). What I want to achieve is just to remove all features from a particular layer. I see that there is a clear method and documentation says, that
clear(opt_fast)
Remove all features from the source.
However, when I apply it to my layer source like so:
layer.getSource().clear();
I see a blink (features are removed) and then I see a server request, so that features are reloaded again. So, either documentation is incomplete, or there is a bug.
I also tried to remove features like so:
features = source.getFeatures();
for (i = 0; i < features.length; i += 1) {
source.removeFeature(features[i]);
}
But it works really strange. If, for example, I have four features, when I loop once, it removes just two features and when I loop twice, one extra feature is removed. All in all, I have to loop three times (which is indeed not DRY) to remove all features. I really wonder, why is that and how can I fix it. Thanks!
As pointed by Karl-Johan Sjögren, removing a array member when iterate through it modifies the array itself, so, you use reverse array or use a native function from Array MDN reference:
features = source.getFeatures();
features.forEach(function (feature){
source.removeFeature(feature);
});

Global JQuery Selector caching to increase performance

I am trying to increase the jquery performance of my mobile html 5 app. I read some guides about storing used jquery selectors in global objects. The app is pretty big and I didn't expected a big perfomrance boost, but the app was running even slower (like 20%).
I only use jquery to find elements by Id ( $("#id") or $_element.find("#id")). Ids are unique, so I am only interested in finding the first element. I managed to globalize all jquery calls in a cacheHandler object, which stores all selectors by id. The cache is cleared frequently and contains around 30 elements per cycle.
With this changes the app ran slower, so I tried some more things to increase performance:
cache[id] = $(document.getElementById("id"))
cache[id] = document.getElementById("id")
store selectors with hashCode: cache[id.hashCode()]
I came up with the idea, that this solution is slow, because the memory is increased frequently, because the whole dom with all its children is stored in the cache.
So I had the new Idea, to cache the element path as array, like
document.body.children[1].children[5].children[0] => [1,5,0]
So I just have to find the element once, store the array and look up the path, if I need the element again.
This doesn't change anything, and all ideas were even slower than calling $("#id"), whenever I need the element.
If needed I can provide more informations or snippets.
I am thankfull for each explanation, why this is slowing down my app.
If it's a mobile html5 app why are you using jQuery for selectors? Seems very redundant.
I usually do somthing along the lines of this:
// helpers, since i hate typing document.get ..
function _id(e){ return document.getElementById(e); } // single id
function _all(e){ return document.querySelectorAll(e); } // single elem
function _get(e){ return document.querySelector(e); } // multiple elem
// loop helper (for changing or doing things to collection of elements)
function _for(e,f) { var i, len=e.length; for(i=0,l=len;i<l;i++){ f(e[i]); }}
// VARs (global)
var c = _id('c'), // main selector
box = c.querySelectorAll('.box'), // boxes in 'c'
elements = box.querySelectorAll('.element'); // elems in 'box'
// Change background-color for all .box using the _for -helper
_for(elements, function(e){ e.style.backgroundColor = 'red'; }
I only store main parents of elements so that i can then query down the DOM if needed (limiting the lockup needed for traversing). In the example variables above one could imagine that something in the .box would change several times OR that .box is a slow selector.
Do note that global variables increase memory usage though since those variables can interfer with garbage collection. Also note that objects can be slower in some browsers and if it doesn't bloat your code to much you should instead store it more plainly (you shouldn't store too many global variables anyway so....).
Edit, jsPerf:
http://jsperf.com/global-vs-local-vars-14mar2015
Do note however that depending on what you're selection and exactly what you're doing will have the greatest impact. In the jsPerf-example the difference between local and global quickly diminishes as soon as one starts selecting descendants from the globally cached selectors ie doing box.find('p').css('color','blue') etc.
This is quite old but we never know, someone can read this post.
jQuery is based on Sizzle which is way smaller: https://sizzlejs.com/
You can eventually include only this library. I would not recommend to maintain your own piece of code for this purpose. It is already done and maintained by someone else.

Restructuring DOM searching method?

As part of a Chrome extension I am searching the entire DOM for elements containing specific words inside each ID/Class.
Currently it looks something like:
"allSelectors": document.querySelectorAll("[id*='example'][class*='example']"),
"collapse": function () {
for (var i = 0; i < MyObject.allSelectors.length; i++) {
// Manipulate MyObject.allSelectors[i] etc
}
},
First, I would like to restructure it somehow (possibly using an array?) so that it is easy to add new selectors as doing it like this:
document.querySelectorAll("[id*='example'][class*='example'][id*='other'][class*='other']")
Isn't easily scaleable or good.
Secondly, I think document.querySelectorAll is very slow - the reason I am using it is because I need to search anywhere in an id/class (hence the use of *=) and cannot use a big external library (such as jQuery), as this is a small file and is being injected user side.
Is there an any solution to either of these problems? because if there are many matches then this slowness might become an issue.
First of all I would totally go for querySelectorAll, I don't think it's that slow, and also it totally fits in a situation like yours. I agree with you that adding a library is overkill for this, and additionally it might not be as beneficial as someone thinks (let's test it here).
Then, again I agree with you that the current solution is not very scalable and that the array is the way to go. Here's a very basic implementation using an array:
// an array of classes and ids to match
var nodes,
searches = [
'[id*="test"]',
'[class*="example"]'
];
// a simple function to return an array of nodes
// that match the content of the array
function getNodes(arr){
return Array.prototype.slice.call(document.querySelectorAll( arr.join() ));
}
nodes = getNodes(searches);
The good thing is that new classes and ids can be easily added or removed from the array, for example, later on you can add:
searches.push('[id*="some"]');
nodes = getNodes(searches); // new nodes will be fetched
Here's a jsbin with a full example code.

Select list search on very large dataset

Currently I have Select2 in my application, and have previously implemented ajax calls to the database to get a smaller subset based on search query entered by a user.
However, users want to be able to click the back arrow on the browser, and have the query automatically run again (something that currently does not happen with Select2). I was able to implement this by pulling the entire dataset (over 18,000 elements) in and calling select2 on that.
The problem with this is that Select2 basically does a foreach in a foreach when doing a search (foreach element in the dataset, go through each string and get the index of the query - which I understand is basically breaking the string into a char array and checking each char individually to see if the combination is found). So every time someone types a character, we're looking at over 18,000 operations, even though the majority of elements are eliminated as options.
Is there a way to make Select2 actually eliminate the options that don't match (create and bind to a temp array or something like that) or perform a binary search instead of a linear search? If not, are there any alternatives to Select2 that DO implement binary search instead of linear search, or would I need to create my own jQuery plugin to do this?
In this jsfiddle1 a hidden select element is used and a clone of that to filter input. The filtering is done with:
for (var i = 0; i < that.selector.options.length; i++) {
if (re.test(that.selector.options[i].text)) {
sclone.add(new Option(that.selector.options[i].text, i));
}
}
Where re is a RegExp created from an input field that placed above the select clone.
Maybe that's an idea to play with?
1 The language used in the first selector is dutch, but I suppose that shouldn't be obstructive to the idea.

Getting all HTML elements in the DOM where an attribute name starts with some-string

I've stumbled upon a tricky one, that I haven't been able to find any references to (except one here on Stackoverflow, that was written quite inefficiently in Plain Old Javascript - where I would like it written in jQuery).
Problem
I need to retrieve all child-elements where the attribute-name (note: not the attribute-value) starts with a given string.
So if I have:
<a data-prefix-age="22">22</a>
<a data-prefix-weight="82">82</a>
meh
My query would return a collection of two elements, which would be the first two with the data-prefix--prefix
Any ideas on how to write up this query?
I was going for something like:
$(document).find("[data-prefix-*]")
But of course that is not valid
Hopefully one of you has a more keen eye on how to resolve this.
Solution
(See accepted code example below)
There is apparently no direct way to query on partial attribute names. What you should do instead (this is just one possible solution) is
select the smallest possible collection of elements you can
iterate over them
and then for each element iterate over the attributes of the element
When you find a hit, add it to a collection
then leave the loop and move on to the next element to be checked.
You should end up with an array containing the elements you need.
Hope it helps :)
Perhaps this will do the trick -
// retrieve all elements within the `#form_id` container
var $formElements = $("form#form_id > *");
var selectedElements = [];
// iterate over each element
$formElements.each(function(index,elem){
// store the JavaScript "attributes" property
var elementAttr = $(this)[0].attributes;
// iterate over each attribute
$(elementAttr).each(function(attIndex,attr){
// check the "nodeName" for the data prefix
if (attr.nodeName.search('data-.*') !== -1){
// we have found a matching element!
if (selectedElements.length < 2){
selectedElements.push(elem);
break;
}else{
if (selectedElements.length == 2){
break(2);
}
}
}
});
});
selectedElements will now hold the first two matching elements.
jsFiddle
You can use jquerys filter() method to have a selections elements being processed by a function. Inside that function you are free to do whatever you want to.
So start with selecting all elements inside the dom tree and filter out everything you dislike. Not especially fast, but working.

Categories