UPDATE
The technique in this post is incorrect and does not work. I failed to realize that jQuery’s selector method (i.e. $() ) always returns a jQuery object. If the element is not in the dom, the jQuery object will be an empty set. But a set nonetheless and not null. Thus, it will never coalesce in the way I intended. I wondered why no-one else was doing this. It seems I have my answer.
I learnt something interesting about JavaScript that I was not aware of this last week. I’ll elaborate by walking you through what I discovered. Basically, I was trying to come up with a pattern that stored a jQuery object, and if for some reason that object had become null at some point, then a failsafe re-query would occur. It looked like this:
- Declare a variable
- Add it to the
pageObjectsobject, with a cascade to the jQuery selection call - Prime the variable in the
document.readyfunction - All dom manipulations are via the
pageObjectsobject
var someDiv; // step 1
// step 2
var pageObjects = {
someDivOnPage: someDiv || $('#someDiv'), // different name not necessary, but illustrative
otherProperty: // ... pageObjects has many properties
};
//********* document.ready function ********* /
$(function() {
// step 3
someDiv = $('#someDiv');
});
// ... access object later in script ...
// step 4
pageObjects.someDivOnPage.css('color', #eeeeee);
I discovered, after a while, that this did not work. If that div in one operation was removed from the dom, and re-added in another operation, calling pageObjects.someDivOnPage was undefined. Apparently, the coalesce in that statement was a one-off. That is, the first time pageObjects.someDivOnPage is accessed, the statement coalesces to the jQuery query as planned. But that statement only runs once and the property of that object is stored in a memory location. Once the element underpinning that jQuery object is removed from the dom, that property no longer points to that jQuery object and it doesn’t coalesce to the re-query as it did upon first access.
The proper way to do it is as follows:
var someDiv;
var pageObjects = {
someDivOnPage: function () {
return someDiv || $('#someDiv'); // different name not necessary, but illustrative
},
otherProperty: // ... pageObjects has many properties
};
//********* document.ready function ********* /
$(function() {
someDiv = $('#someDiv');
});
// ... access object later in script ...
pageObjects.someDivOnPage().css('color', #eeeeee);
As you can see, I have changed the property in the pageObjects object to be a function. And later on, when I reference that property I call it as a function.
So, the conclusion is that when the interpreter first parses the pageObjects object, the properties are assigned in the way that an expression is executed. To use pseudo code, it would look like this:
// for my first flawed approach.
... {
someDivOnPage = someDiv || $('#someDiv');
};
// for my corrected approach
... {
someDivOnPage = function () {
return someDiv || $('#someDiv'); // different name not necessary, but illustrative
};
};
That assignment occurs once and the result of that expression is assigned to the property. Makes complete sense really. It’s just something I had not picked up on. Felt like a goose.
So you’re probably wondering about performance? Well, that was the whole idea. The project which I am working on had jQuery performing queries heaps of times for the same dom objects in different operations during the lifecycle of the page (button-clicks, mouse-overs etc.).
If you look at this benchmark which I created at jsperf.com, you will see that this approach is very performant.
The other advantage is that if you are using a good IDE, you get intellisense when you “dot through” to the relevant property on the pageObjects object. This makes maintenance a lot easier and reduces the opportunity for typos when writing out each and every jQuery selector.
0 Comments.