Utilities

In the sixth and final post of this series, I'm going to cover some of the utility methods that jQuery core provides, and show how you can acheive the same results with vanilla JavaScript. Focus will be on modern browsers, but I'll also go over achieving the same ends in ancient browsers as well. Modern browsers are considered to be anything newer than and including Internet Explorer 9. Anything older than that is "ancient".

I know I've stated the purpose of this series many times before, and most readers seem to understand. However, I continue to see comments from a few readers who seemed to have missed my point. So, once again, I am writing these posts to teach about the Web API and JavaScript as a language. If you really want to continue using jQuery, have at it. If not, these posts will hopefully give you the confidence and knowledge you need to remove this dependency from your future projects. At the very least, by reading all of this, you will gain a better understanding of the Web API and JavaScript as a language, which is important to your evolution as a web developer (whether you use jQuery or not).

All vanilla JavaScript code below will work in all browsers, unless otherwise noted. As always, edge cases not described here are always possible (both with and without jQuery), as this is the nature of software.

  1. Is this an Object, Array, or a Function?
  2. Combine and copy objects
  3. Iterate over object properties
  4. Iterate over array elements
  5. Find an element in an array
  6. Turn a pseudo-array into a real array
  7. Modify a function
  8. Trim a string
  9. Associate data with an HTML element
  10. Next

Is this an Object, Array, or a Function?

Due to the fact that JavaScript follows weak and dynamic value typing, you may find the need to check the type of a value and react accoringly based on the actual type. What follows is code that can help you achieve this goal in both jQuery and vanilla JavaScript.

jQuery

jQuery provides a few functions to identify a value as either an Object, Array, or a Function. They are appropriately and intuitively named. Each function returns a boolean.

$.isFunction(someValue);

$.isPlainObject(someValue);

$.isArray(someValue);

vanilla JavaScript

To determine if a value is a function, we can make use of the typeof operator.

// is this a function?
typeof someValue === 'function';

Determining if a value is an object is a bit less intuitive, but still doable in one line. We must include a null-check as some browsers will report null as an Object. The second half of the check uses the toString function of the Object prototype on the value to be tested.

// is this an object?
someValue != null &&
    Object.prototype.toString.call(someValue) === "[object Object]";

Checking for an Array is similar (but simpler) than the check for an Object. Plus, you'll see that a convenience method exists as an alternative for modern browsers.

// works in modern browsers
Array.isArray(someValue);


// works in all browsers
Object.prototype.toString.call(someValue) === "[object Array]";

Combine and copy objects

Suppose you have two objects, o1, and o2:

var o1 = {
        a: 'a',
        b: {
            b1: 'b1'
        }
    },

    o2 = {
        b: {
            b2: 'b2'
        },
        c: 'c'
    };

What if you want to take o2 and append it to o1, or create a new object that is the aggregate of both objects? The combined object should look like this:

{
    a: 'a',
    b: {
        b1: 'b1',
        b2: 'b2'
    },
    c: 'c'
}

jQuery

// updates o1 with the contents of o2
$.extend(true, o1, o2);

// creates a new object that is the aggregate of o1 & o2
var newObj = $.extend(true, {}, o1, o2);

If we just want to create a copy of one of the objects, we can do that too, also using $.extend:

var copyOfO1 = $.extend(true, {}, o1);

vanilla JavaScript

Accomplishing this task without jQuery involves iterating over the properties in o2. For each property with an object value, we'll need to iterate over that too, making a recursive call. As we encounter non-object values in o2, we'll assign them to the corresponding property in o1.

// our helper function
function extend(first, second) {
    for (var secondProp in second) {
        var secondVal = second[secondProp];
        // Is this value an object?  If so, iterate over its properties, copying them over
        if (secondVal && Object.prototype.toString.call(secondVal) === "[object Object]") {
            first[secondProp] = first[secondProp] || {};
            extend(first[secondProp], secondVal);
        }
        else {
            first[secondProp] = secondVal;
        }
    }
    return first;
};

// example use - updates o1 with the content of o2
extend(o1, o2);

// example use - creates a new object that is the aggregate of o1 & o2
var newObj = extend(extend({}, o1), o2);

As you can see above, we can create a new object that contains the combination of o1 and o2, without modifying either of the source objects, simply by creating a copy of o1, and extending that with the properties of o2. In case you missed it, here's a simpler example that focues specifically on copying o1:

var copyOfO1 = extend({}, o1);

Note: the simple code used above to iterate over object properties is fine for this example, but you may want to read the next section, which covers iterating over object properties for a more complete understanding of how this should be done with vanilla JavaScript.

Iterate over object properties

Let's say we have the following Object, called parentObject:

var parentObject = {
    a: 'a',
    b: 'b'
};

...and then we create myObject, which inherits the properties of parentObject:

var myObject = Object.create(parentObject);
myObject.c = "c";
myObject.d = "d";

Now, myObject has 4 properties, but only 'c' and 'd' belong to myObject. Properties 'a' and 'b' are part of parentObject, which is on the same prototype chain as myObject. Most likely, when we want to iterate over the properties of myObject, we only want the properties that belong to this object, and not any that belong to parentObject.

jQuery

$.each(myObject, function(propName, propValue) {
    // handle each property...
});

The above code will iterate over all of the myObject properties, ignoring any properties that do not directly belong to myObject.

vanilla JavaScript

In the previous section, while discussing copying and combining objects, I included a barebones method of iterating over an object's properties. In case you need to deal with something more complex or want to exploit native approaches available in modern browsers, I'll go over some other approaches, as well as some things you should be aware of when dealing with object property iteration.

For reference, here is the simple for...in loop presented in the "Combine & Copy Objects" section, now iterating over myObject:

for (var myObjectProp in myObject) {
    // deal with each property in the `myObject` object...
}

So, if we outputted every property encountered by the loop above, we'd see the properties on myObject, as well as the properties of parentObject. Remember, parentObject and myObject are on the same prototype chain, and myObject inherits all of the properties from parentObject. But, this is not what we want. We only want the properties that myObject owns! To acheive this, we need to exclude any other properties, and this can be done via the hasOwnProperty method.

// works in all browsers
for (var prop in myObject) {
    if (myObject.hasOwnProperty(prop)) {
        // deal w/ each property that belongs only to `myObject`...
    }
}

All objects have a hasOwnProperty method, as this method is available on Object.prototype.

But wait! There's another solution, specific to modern browsers. ECMAScript 5 defines a new function: Object.keys(someObj), which returns an array of properties on the passed object. Also, this array will only contain the properties owned by the passed object.

// works in modern browsers
Object.keys(myObject).forEach(function(prop) {
    // deal with each property that belongs to `myObject`...
});

Note that I'm using forEach on the returned array of property names above. I'll talk more about iterating over arrays in the next section.

Iterate over array elements

Given an array, like this:

var myArray = ['a', 'b', 'c'];

...we'd like to be able to simply iterate over each of the elements in this array.

jQuery

Iterating over arrays in jQuery is a lot like iterating over object properties. In both cases, just use the jQuery.each method:

$.each(myArray, function(arrayIndex, arrayValue) {
    // handle each array item...
});

vanilla JavaScript

This isn't much harder without jQuery, regardless of the browser. We can simply iterate over the elements in the array using a simple for loop:

// works in all browsers
for (var index = 0; i < myArray.length; i++) {
    var arrayVal = myrray[index];
    // handle each array item...
}

But that doesn't look nearly as nice as jQuery, does it? ECMAScript 5 to the rescue again! We can make use of Array.prototype.forEach in modern browsers instead:

// works in modern browsers
myArray.forEach(function(arrayVal, arrayIndex) {
    // handle each array item
}

Notice that the order of the parameters passed to your callback function by Array.prototype.forEach differs from that of jQuery.each. I actually prefer the ordering used by forEach as it allows me to omit the index parameter if I only care about the values. Note that there are a lot of other useful functions on Array.prototype in ES5 and beyond.

Find an element in an array

Here's I'll cover two similar operations on an array:

  1. Find the index of an array that contains a specific value.
  2. Find all elements in an array that satisfy a particular condition.

Let's again use a simple array:

var theArray = ['a', 'b', 'c'];

jQuery

To find the index of an array for a specific value, we can use the inArray function:

var indexOfValue = $.inArray('c', theArray);

indexOfValue will be -1 if the value represented by the first parameter, 'c', is not found in theArray. Otherwise, it will be the index of theArray where the passed value was found. In this case, that index is 2.

If we want to use jQuery to find all elements in an array that satisfy a particular condition, we can use the grep function:

var allElementsThatMatch = $.grep(theArray, function(theArrayValue) {
    return theArrayValue !== 'b';
});

allElementsThatMatch will equal ['a', 'c'] as our filter function excludes any value that matches 'b'.

vanilla JavaScript

If you'd like to find the index of an array for a specific value, AND you are using a modern browser, you can make use of the Array.prototype.indexOf method:

// works in modern browsers
var indexOfValue = theArray.indexOf('c');

Sadly, the indexOf method didn't come about until ECMAScript 5. So, if you are suffering with a requirement to support an ancient browser, such as IE 8, you must iterate over the elements of the array, using a for loop, until you find the desired element. For example:

// works in all browsers
function indexOf(array, valToFind) {
    var foundIndex = -1;
    for (var index = 0; index < array.length; index++) {
        if (array[index] === valToFind) {
            foundIndex = index;
            break;
        }
    }
    return foundIndex;
}

// example use
indexOf(theArray, 'c');

If you want to find all values that satisfy a specific condition in vanilla JS, you can make use of the shiny new Array.prototype.filter method in ES5:

// works in modern browsers
var allElementsThatMatch = theArray.filter(function(theArrayValue) {
    return theArrayValue !== 'b';
});

That actually looks a bit nicer than jQuery's grep method!

If you need to deal with ancient browsers, such as IE 8, you'll need to write a bit more code:

// works in all browsers
function filter(array, conditionFunction) {
var validValues = [];
    for (var index = 0; index < array.length; i++) {
        if (conditionFunction(theArray[index])) {
            validValues.push(theArray[index]);
        }
    }
}

// use example
var allElementsThatMatch = filter(theArray, function(arrayVal) {
    return arrayVal !== 'b';
})

Turn a pseudo-array into a real array

In JavaScript, there are real arrays:

var realArray = ['a', 'b', 'c'];

...and "pseudo"-arrays:

var pseudoArray1 = {
    1: 'a',
    2: 'b',
    3: 'c'
    length: 3
};

There are some native pseudo-arrays as well, such as NodeList, HTMLCollection, and FileList, to name a few. These are not real arrays in that they are not on the same prototype chain as Array. That is, they inherit nothing from Array.prototype because they are not arrays. In fact, they are just objects. But, due to the length property, you can treat them as an array, in some respects, by iterating over their "elements" using a for loop, just as you would any real array.

If you are dealing with a pseudo-array in an ancient browser, such as IE 8, it probably doesn't matter much, since you'll need iterate over the properties/elements using a for loop whether it is a real array or not. But, suppose you are using a modern browser, and you want to make this object that looks very much like an array act like an array. Perhaps you want to use forEach, or map, or filter. Or perhaps you must pass it to an API method that expects a real array.

jQuery

You can convert a pseudo-array to a real array in jQuery using the makeArray method:

var realArray = $.makeArray(pseudoArray);

vanilla JavaScript

If you'd like to transform a pseudo-array to a real array, and make all methods on Array.prototype available, you can use this trick:

var realArray = [].slice.call(pseudoArray);

If you only care to use a specific array method on a pseudo-array, such as forEach you can do this instead:

[].forEach.call(pseudoArray, function(arrayValue) {
    // handle each element in the pseudo-array...
});

Why does this work? There's an excellent Stackoverflow answer that provides a nice explanation.

Modify a function

There are two ways to modify an existing function:

  1. Change its context.
  2. Create a new function with some pre-determined arguments.

In both cases, you would want to take an existing function and create a new function based on the original.

1 - Change a function's context

To demonstrate #1, let's propose we have an event handler. By default, the context of an event handler in modern browsers and jQuery is the element that started the event (except for inline event handlers, which are a bad idea anyway). But what if you want your event handler to assume a different context? For example, say you have some public instance variables attached to the context of a function, and you want to have easy access to these variables inside your event handler:

function Outer() {
    var eventHandler = function(event) {
        this.foo = 'buzz';
    }

    this.foo = 'bar';
    // attach `eventHandler`...
}

var outer = new Outer();
// event is fired, triggering `eventHandler`

Without any changes, outer.foo === 'bar'. Since the foo property is being set in the context of an event handler, the element that triggered the event is actually receiving a foo property with a value of 'buzz'. But we want outer.foo === 'buzz'!

jQuery

It is possible to re-assign the context of a function in jQuery using the proxy function. To make this work, we simply need to wrap the function in a call to jQuery's proxy utility method:

function Outer() {
    var eventHandler = $.proxy(function(event) {
        this.foo = 'buzz';
    }, this);

    this.foo = 'bar';
    // attach `eventHandler`...
}

var outer = new Outer();
// event is fired, triggering `eventHandler`

Now, when the event handler is triggered, outer.foo === 'buzz'.

vanilla JavaScript

Modern browsers provide a bind function on Function.prototype. This allows you to change the context of a function, just like jQuery's proxy, but in a slightly more elegant way:

// works in modern browsers
function Outer() {
    var eventHandler = function(event) {
        this.foo = 'buzz';
    }.bind(this);

    this.foo = 'bar';
    // attach `eventHandler`...
}

var outer = new Outer();
// event is fired, triggering `eventHandler`

We simply call bind on the function, pass the desired context, and the resulting function assigned to eventHandler is associated with the context of our Outer function, which means outer.foo === 'buzz' when the event handler is ultimately invoked inside our Outer instance.

If you must support an ancient browser, like IE 8, you'll need to take one of two alternate approaches. The first involves adding an implementation of bind to Function.prototype if the browser does not provide a native implementation (such as with IE 8 and older). Mozilla Developer Network provides some code for such a polyfill. I'm not going to repeat the code here, as it is displayed quite nicely on MDN. Simply invoking that function on page load will ensure that a bind utility function will be available for all functions. Another option is to take a completely different approach, and store the value of this in a variable inside of the Outer function. You can then reference that variable (instead of this) inside of eventHandler to ensure the correct property is updated. For instance:

// works in all browsers
function Outer() {
    var self = this,
        eventHandler = function(event) {
            self.foo = 'buzz';
        };

    this.foo = 'bar';
    // attach `eventHandler`...
}

var outer = new Outer();
// event is fired, triggering `eventHandler`

When the eventHandler is invoked, our instance of Outer, represented by the outer variable, will contain the expected value of foo, which is 'buzz'. This is a feasible solution in all browsers.

2 - Create a new function with some pre-determined arguments

Another way to phrase the method demonstrated here is "partial function application" or "currying". Instead of silly pointless examples involving functions that add numbers together, let's try to demonstrate something at least a bit more realistic.

Say we have a utility function that is part of a shared library used among many of our applications. This function logs messages to a remote server for aggregation and further evaluation. The function takes an application ID, a level (such as "info" or "error"), and a log message. It looks like this:

function log(appId, level, message) {
    // send message to central server
}

We've pulled in the shared library containing this function into our app, and our application has an ID of "master-shake". Now, every time we log a message in our app, do we really want to pass the same first parameter, our app ID, which never changes? We certainly could, or, we can create a new function that fills in this parameter automatically for us. In other words, we want to call a function and only pass in the variable data: the level and message, with the same end result.

jQuery

As before, we'll use jQuery's proxy function:

var ourLog = $.proxy(log, null, 'master-shake');

// example use
ourLog('error', 'dancing is forbidden!');

Invoking ourLog will more or less pass the application ID ("master-shake"), as well as the specified error and message, to the original function.

vanilla JavaScript

We can again use the bind function for modern browsers. As before, if an ancient browser is required, a shim can be used to help us out.

var ourLog = log.bind(null, 'master-shake');

// example use
ourLog('error', 'dancing is forbidden!');

You're probably wondering about the significance of the null parameter we've passed to jQuery's proxy and native JS's bind function. We don't care much about context in this example, but passing null as the context to bind will set the value of this inside the bound function to the global function (window if we're in a browser). In the case of jQuery's proxy function, a null context parameter (at least as of jQuery 1.9) means that the proxied function will assume a value of this equal to the context of the calling function.

Trim a string

Given this string:

"  hi there!   "

We want to trim all of the leading and trailing whitespace to produce this:

"hi there!"

jQuery

We can using jQuery's trim function to do this:

$.trim('  hi there!   ');

The string, sans leading & trailing whitespace, will be returned by the function call above.

vanilla JavaScript

In modern browsers, we can use the trim function on String.prototype:

// works in modern browsers
'  hi there!   '.trim();

For ancient browsers, we aren't so lucky, and have to resort to a regular expression to remove the leading and trailing spaces:

// works in all browsers, but needed in IE 8 and older
'  hi there!   '.replace(/^\s+|\s+$/g, '');

You can roll this into a function that will use the trim method if it exists, else the regexp if we're dealing with an ancient browser:

// works in all browsers
function trim(string) {
    if (string.trim) {
        return string.trim();
    }
    return string.replace(/^\s+|\s+$/g, '');
}

Associate data with an HTML element

You may find yourself wishing to track data (objects, strings, numbers, and even other elements) in the context of a specific HTML element. The safest way to do this, cross browser, to avoid memory leaks associated with circular references between elements, is to not attach this data directly to the element's associated JavaScript object as a property. If you're only tracking string, number, or simply object values, then this isn't much of an issue, but once you involve elements as data values as well, older browsers may spring memory leaks. For more information on that, feel free to read Joel Webber's old but interesting points on the subject.

To keep things simple, straightforward, and safe, we're going to avoid storing anything other than strings and numbers directly on the element. So, we'll need to tag our elements with an ID/key, and attach that key to our data in an object maintained in a data store apart from the element.

Let's say we have two elements, and when we click on one, we want the other to be either hidden or made visible (the opposite of its current state).

<div id="one">one</div>
<div id="two">two</div>

This example is a bit contrived, admittedly, but it illustrates our topic rather simply.

jQuery

jQuery's data method will create an ID for each element we want to tag with data, add that ID as a property to the element's JavaScript object, and use that ID to tie the element to the data in a central object maintained by the library.

// make the elements aware of each other
$('#one').data('partnerElement', $('#two'));
$('#two').data('partnerElement', $('#one'));

// on click, either hide or show the partner element
$('#one, #two').click(function() {
    $(this).data('partnerElement').toggle();
});

vanilla JavaScript

There are a couple ways to do this in vanilla JS. The first will work in all browsers, but requires a bit more code. For this first approach, let's create a couple functions. One will tag an element with an ID and store that ID along with the data in an object. The other will retrieve data, given an element:

// works in all browsers
var data = (function() {
    var lastId = 0,
        store = {};

    return {
        set: function(element, info) {
            var id;
            if (element.myCustomDataTag === undefined) {
                id = lastId++;
                element.myCustomDataTag = id;
            }
            store[id] = info;
        },

        get: function(element) {
            return store[element.myCustomDataTag];
        }
    };
}());

...and now let's use it based on our previously stated requirements:

// make the elements aware of each other
var one = document.getElementById('one'),
    two = document.getElementById('two'),
    toggle = function(element) {
        if (element.style.display !== 'none') {
            element.style.display = 'none';
        }
        else {
            element.style.display = 'block';
        }
    };

data.set(one, {partnerElement: two});
data.set(two, {partnerElement: one});

// on click, either hide or show the partner element
// remember to use `attachEvent` in IE 8 and older, if support is required
one.addEventListener('click', function() {
    toggle(data.get(one).partnerElement);
});
two.addEventListener('click', function() {
    toggle(data.get(two).partnerElement);
});

Yes, this is a hell of a lot more code than the jQuery example. I know. Remember, this is about understanding JavaScript, not necessarily about counting lines of code. But the awesome thing about JavaScript as a language is that it continues to evolve and make our lives easier. ECMAScript 6 brings a new collection, called a WeakMap. A WeakMap can contain keys that are objects, and values that are anything. Keys are "weakly" held by the collection. This means that they are eligible for garbage collection by the browser if nothing else references them. So, we can use the reference elements as keys!

While WeakMap is only supported in the latest and greatest browsers (IE 11+, Chrome 36+, Safari 7.1+) and Firefox 6+, we can perhaps make use of it if the browser provides such support. If we rely only on the WeakMap, we can eliminate our data helper entirely, and our entire set of code looks like this instead:

// works only in the latest browsers
// make the elements aware of each other
var weakMap = new WeakMap(),
    one = document.getElementById('one'),
    two = document.getElementById('two'),
    toggle = function(element) {
        if (element.style.display !== 'none') {
            element.style.display = 'none';
        }
        else {
            element.style.display = 'block';
        }
    };

weakMap.set(one, {partnerElement: two});
weakMap.set(two, {partnerElement: one});

// on click, either hide or show the partner element
// remember to use `attachEvent` in IE 8 and older, if support is required
one.addEventListener('click', function() {
    toggle(weakMap.get(one).partnerElement);
});
two.addEventListener('click', function() {
    toggle(weakMap.get(two).partnerElement);
});

Of course, we can't rely entirely on WeakMap, yet (unless we only support IE 11+). So, we'll probably instead want to continue to use our data provider, relying on WeakMap only if the browser provides an implementation:

// works in all browsers
var data = window.WeakMap ? new WeakMap() : (function() {
    var lastId = 0,
        store = {};

    return {
        set: function(element, info) {
            var id;
            if (element.myCustomDataTag === undefined) {
                id = lastId++;
                element.myCustomDataTag = id;
            }
            store[id] = info;
        },

        get: function(element) {
            return store[element.myCustomDataTag];
        }
    };
}());

Next

"You Don't Need jQuery (anymore)!" is complete! I hope you've found some use in these posts. I personally have enjoyed the format of this blog, and I think a series of posts about a central topic is interesting and useful in other instances.

I plan to write another similar blog series next, this time covering cross-domain browser-based communication. Some of the posts in that series will likely cover: the same-origin policy, CORS, JSONP, the Web Messaging API, and content security policies, among other topics. Stay tuned, and I'll be sure to link to the new blog after I've completed the first post.

Written on January 21, 2015