7 Essential JavaScript Functions

By  on  

I remember the early days of JavaScript where you needed a simple function for just about everything because the browser vendors implemented features differently, and not just edge features, basic features, like addEventListener and attachEvent.  Times have changed but there are still a few functions each developer should have in their arsenal, for performance for functional ease purposes.

debounce

The debounce function can be a game-changer when it comes to event-fueled performance.  If you aren't using a debouncing function with a scroll, resize, key* event, you're probably doing it wrong.  Here's a debounce function to keep your code efficient:

// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
function debounce(func, wait, immediate) {
	var timeout;
	return function() {
		var context = this, args = arguments;
		var later = function() {
			timeout = null;
			if (!immediate) func.apply(context, args);
		};
		var callNow = immediate && !timeout;
		clearTimeout(timeout);
		timeout = setTimeout(later, wait);
		if (callNow) func.apply(context, args);
	};
};

// Usage
var myEfficientFn = debounce(function() {
	// All the taxing stuff you do
}, 250);
window.addEventListener('resize', myEfficientFn);

The debounce function will not allow a callback to be used more than once per given time frame.  This is especially important when assigning a callback function to frequently-firing events.

poll

As I mentioned with the debounce function, sometimes you don't get to plug into an event to signify a desired state -- if the event doesn't exist, you need to check for your desired state at intervals:

// The polling function
function poll(fn, timeout, interval) {
    var endTime = Number(new Date()) + (timeout || 2000);
    interval = interval || 100;

    var checkCondition = function(resolve, reject) {
        // If the condition is met, we're done! 
        var result = fn();
        if(result) {
            resolve(result);
        }
        // If the condition isn't met but the timeout hasn't elapsed, go again
        else if (Number(new Date()) < endTime) {
            setTimeout(checkCondition, interval, resolve, reject);
        }
        // Didn't match and too much time, reject!
        else {
            reject(new Error('timed out for ' + fn + ': ' + arguments));
        }
    };

    return new Promise(checkCondition);
}

// Usage:  ensure element is visible
poll(function() {
	return document.getElementById('lightbox').offsetWidth > 0;
}, 2000, 150).then(function() {
    // Polling done, now do something else!
}).catch(function() {
    // Polling timed out, handle the error!
});

Polling has long been useful on the web and will continue to be in the future!

once

There are times when you prefer a given functionality only happen once, similar to the way you'd use an onload event.  This code provides you said functionality:

function once(fn, context) { 
	var result;

	return function() { 
		if(fn) {
			result = fn.apply(context || this, arguments);
			fn = null;
		}

		return result;
	};
}

// Usage
var canOnlyFireOnce = once(function() {
	console.log('Fired!');
});

canOnlyFireOnce(); // "Fired!"
canOnlyFireOnce(); // nada

The once function ensures a given function can only be called once, thus prevent duplicate initialization!

getAbsoluteUrl

Getting an absolute URL from a variable string isn't as easy as you think.  There's the URL constructor but it can act up if you don't provide the required arguments (which sometimes you can't).  Here's a suave trick for getting an absolute URL from and string input:

var getAbsoluteUrl = (function() {
	var a;

	return function(url) {
		if(!a) a = document.createElement('a');
		a.href = url;

		return a.href;
	};
})();

// Usage
getAbsoluteUrl('/something'); // https://davidwalsh.name/something

The "burn" element href handles and URL nonsense for you, providing a reliable absolute URL in return.

isNative

Knowing if a given function is native or not can signal if you're willing to override it.  This handy code can give you the answer:

;(function() {

  // Used to resolve the internal `[[Class]]` of values
  var toString = Object.prototype.toString;
  
  // Used to resolve the decompiled source of functions
  var fnToString = Function.prototype.toString;
  
  // Used to detect host constructors (Safari > 4; really typed array specific)
  var reHostCtor = /^\[object .+?Constructor\]$/;

  // Compile a regexp using a common native method as a template.
  // We chose `Object#toString` because there's a good chance it is not being mucked with.
  var reNative = RegExp('^' +
    // Coerce `Object#toString` to a string
    String(toString)
    // Escape any special regexp characters
    .replace(/[.*+?^${}()|[\]\/\\]/g, '\\$&')
    // Replace mentions of `toString` with `.*?` to keep the template generic.
    // Replace thing like `for ...` to support environments like Rhino which add extra info
    // such as method arity.
    .replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
  );
  
  function isNative(value) {
    var type = typeof value;
    return type == 'function'
      // Use `Function#toString` to bypass the value's own `toString` method
      // and avoid being faked out.
      ? reNative.test(fnToString.call(value))
      // Fallback to a host object check because some environments will represent
      // things like typed arrays as DOM methods which may not conform to the
      // normal native pattern.
      : (value && type == 'object' && reHostCtor.test(toString.call(value))) || false;
  }
  
  // export however you want
  module.exports = isNative;
}());

// Usage
isNative(alert); // true
isNative(myCustomFunction); // false

The function isn't pretty but it gets the job done!

insertRule

We all know that we can grab a NodeList from a selector (via document.querySelectorAll) and give each of them a style, but what's more efficient is setting that style to a selector (like you do in a stylesheet):

var sheet = (function() {
	// Create the <style> tag
	var style = document.createElement('style');

	// Add a media (and/or media query) here if you'd like!
	// style.setAttribute('media', 'screen')
	// style.setAttribute('media', 'only screen and (max-width : 1024px)')

	// WebKit hack :(
	style.appendChild(document.createTextNode(''));

	// Add the <style> element to the page
	document.head.appendChild(style);

	return style.sheet;
})();

// Usage
sheet.insertRule("header { float: left; opacity: 0.8; }", 1);

This is especially useful when working on a dynamic, AJAX-heavy site.  If you set the style to a selector, you don't need to account for styling each element that may match that selector (now or in the future).

matchesSelector

Oftentimes we validate input before moving forward; ensuring a truthy value, ensuring forms data is valid, etc.  But how often do we ensure an element qualifies for moving forward?  You can use a matchesSelector function to validate if an element is of a given selector match:

function matchesSelector(el, selector) {
	var p = Element.prototype;
	var f = p.matches || p.webkitMatchesSelector || p.mozMatchesSelector || p.msMatchesSelector || function(s) {
		return [].indexOf.call(document.querySelectorAll(s), this) !== -1;
	};
	return f.call(el, selector);
}

// Usage
matchesSelector(document.getElementById('myDiv'), 'div.someSelector[some-attribute=true]')

There you have it:  seven JavaScript functions that every developer should keep in their toolbox.  Have a function I missed?  Please share it!

Recent Features

Incredible Demos

Discussion

  1. You don’t need to slice.call to indexOf when you can just indexOf.call any collection directly ;-)

  • Gist? I don’t understand what you’re saying but I’d like to update the post… :(

  • Ilya Panasenko

    in matchesSelector you can write the following:

    return [].indexOf.call(document.querySelectorAll(s), this) !== -1;
    
  • Java Script

    Array.prototype.indexOf.call is longer but won’t create a new instance of Array

  • Tom

    or even

    return ~[].indexOf.call(document.querySelectorAll(s), this);
    
  • Al Val

    or using lodash?

    p.s.
    nice post :)

  • Hey, great post! I have another handy utility function you can use! (I kinda think its like your once function on steroids!)

    When(condition, fctn);
    

    I have a node.js library right here for you to try: https://www.npmjs.com/package/when-conditional
    and here is the github repo if you want to use the code: https://github.com/thekemkid/when-conditional :D

    What it does:
    You pass two functions to the when function, condition and fctn. condition is a function which must return a truthy value, and fctn is a function which is what you want to execute when the condition returns truthy!
    Here is an example of it in use:

    this will log someVar is false 10 times, then tell you it is true once and exit.

    var when = require('when-conditional');
    var someVar = false;
     
    var interval = setInterval(function(){
      console.log('someVar is ' + someVar);
      if(someVar === true) clearInterval(interval);
    }, 1000);
     
    var immediate = when(function condition(){
    	return (someVar === true);
    }, function code(){
    	console.log("someVar is now true, and this was only triggered when it became true!");
    });
     
    setTimeout(function(){
    	someVar = true;
    }, 10000);
    

    Notes: The node.js library uses a setImmediate shim which should get the when function working on most browsers! :D
    and it allows you the ability to clear the when listener, if you need to! :D

    Thanks for reading :)

  • I am using randomRange a lot to provide a random value between min and max.

    function rand(min, max, integer) {
      var r = Math.random() * (max - min) + min; 
      return integer ? r|0 : r;
    }
    console.log(rand(2,5)); // float random between 2 and 5 inclusive
    console.log(rand(1,100,true)); // integer random between 1 and 100
    
  • Joshua Koudys

    I like the getAbsoluteURL() approach, and I find there are a lot of ways to leverage the DOM to do the heavy lifting for you. You’re in a web browser already (or if node.js, an engine developed for a web browser), so it makes sense that there’d be many web-related functions available to you.

  • Joshua Koudys
    function stripTags(htmlstr) {
        var div = document.createElement('div');
        div.innerHTML = htmlstr;
        return div.textContent;
    }
    
  • Peter

    I tried to abuse the getAbsoluteUrl function to do the following :

    Detect if a webpage is run locally or served remotely from the server.
    None of the (very few) suggestions online work with Safari webarchives

    The getAbsoluteUrl always returns the URL from where the original page was saved… so even when the webarchive is running inside the browser with the file:// protocol, the function returns the URL as if it were running on the server.

    Sorry that this is a bit off topic, I was just hoping to find a solution (the javascript location object does not work).
    Any pointers or tips on how to detect a file being run locally would be greatly appreciated.

    • Drew

      I’d check how jQuery does it. They basically use a regex to detect whether or not a given url is local, cross domain, or file:// and ultimately that might be the shortest route.

  • Nice! My original article shows a version with promises:

    http://davidwalsh.name/javascript-polling

  • Great list!

    The only thing I would prefer would be to use promises with poll.

    function poll(fn, timeout, interval) {
      var endTime = Number(new Date()) + (timeout || 2000);
      interval = interval || 100;
    
      return new Promise(function(resolve, reject) {
        (function p() {
          // If the condition is met, we're done!
          if (fn()) {
            resolve();
          }
          // If the condition isn't met but the timeout hasn't elapsed, go again
          else if (Number(new Date())  0;
    }).then(function() {
      // Done
    }).catch(function() {
      // Fail
    });
    
  • Back to this blog, after a long time and it made me happier again with the wealth of information and this time on Javascript. I would love to try out these functions.

  • Eric

    I think throttle is what youre describing debounce as. Similar but not the same. Debounce makes sure that a function only gets called once it is no longer being called repeatedly. If it gets called a second time before n milliseconds has elapsed, it will cancel the first call and begin another delayed call to the function, repeating this process until the function is no longer called anymore.

  • There is a slightly more efficient way to do debounce. Setting a new timeout per time it is called can be expensive (especially if you’re calling debounce a lot in a single window).

    http://modernjavascript.blogspot.co.uk/2013/08/building-better-debounce.html

  • Oleksii Davydov

    I think Date.now() is nicer and more efficient than Number(new Date()).

  • Always nice to see various implementations of these indispensable utility functions. In the past, when I’ve implemented matchesSelector, instead of querying the entire document, I’ve used the child’s parentNode property:

    function matchesSelector(el, selector) {
    	var p = Element.prototype;
    	var f = p.matches || p.webkitMatchesSelector || p.mozMatchesSelector || p.msMatchesSelector || function(s) {
    		return [].indexOf.call(this.parentNode.querySelectorAll(s), this) !== -1;
    	};
    	return f.call(el, selector);
    }
    
    • Well, that was my first thought when I saw this implementation, but what if the selector is something like this:

      body > #container .child

      Then you would have to rely on document.querySelectorAll

    • Victor

      Try it yourself: https://jsfiddle.net/45cy05mj/ – you’ll be surprised.

      This is how

      {query,matches}Selector

      actually works – selector is applied from the document’s root, as in CSS stylesheet.

  • Christian Haller

    Burp Suite is complaining about getAbsoluteUrl()

    The application may be vulnerable to DOM-based link manipulation. Data is read from the baseURI property of a DOM element and written to the href property of a DOM element

    They don’t notice, that the anchor element is never touching the DOM

  • For matchesSelector I’d recommend just using element.matches as per spec and polyfilling it where it isn’t present, also see: https://developer.mozilla.org/en/docs/Web/API/Element/matches

  • Also another useful function is element.closest()

  • Drew

    Similar to getAbsoluteUrl, you can use a modern browser’s native ability to validate emails from a string by creating an temporary element and testing its validity:

    function isValidEmail(string){
        string = string||'';
        var lastseg = (string.split('@')[1]||'').split('.')[1]||'',
            input = document.createElement('input');
            input.type = "email";
            input.required = true;
            input.value = string;
        return !!(string && (input.validity && input.validity.valid) && lastseg.length);
    }
    
    isValidEmail("");// -> false
    isValidEmail("asda");// -> false
    isValidEmail("asda@gmail");// -> false
    isValidEmail("[email protected]");// -> true
    

    If you wanted to support older browsers you could fall back to trying a regex instead, but with this method you can be ensured that whatever the user’s browser considers to be a valid email, you’re using the exact same logic (however strict or lenient that might be).

    • Victor

      Actually, email “asda@gmail” is perfectly valid and could exist in intranet network, and it is marked as valid by email field in current Chromium.

      Please, don’t be too restrictive in email validation. Browsers may have bugs and different implementations, email RFCs will inevitably change over time, email servers may support own non-standard extensions etc. Start to learn from historical errors and mistakes (“tld should not be longer than 3 letters”, “email should not contain non-latin characters”).

      The only sane and (more or less) future-proof assumption we could have about email is that it should contain “@” sign.

      StackOverflow has good answers to that question, e.g.
      https://stackoverflow.com/a/202528/1235394
      https://stackoverflow.com/a/201854/1235394
      https://stackoverflow.com/a/41129750/1235394

  • Fernando
    function yourFn() {
       // do something...
    
       yourFn = null;
    }
    

    We can simplify concepts…

  • Xavier
    // Build a better Sheet object 
    Sheet = (function() {
    	// Build style
    	var style = document.createElement('style');
    	style.setAttribute('media', 'screen');
    	style.appendChild(document.createTextNode(''));
    	document.head.appendChild(style);
    
    	// Build and return a single function
    	return function(rule){ style.sheet.insertRule( rule, style.sheet.cssRules.length ); } ;
    })();
    
    // Then call as a function
    Sheet(".stats { position: relative ; top: 0px }") ;
    
  • This a simple function for input number only

    /* Input Number Only */
    function isNumberKey(evt){
        var charCode = (evt.which) ? evt.which : event.keyCode
        if (charCode > 31 && (charCode  57))
            return false;
    	return true;
    }
    
  • you guys are absolutely brilliant. Thanks for doing all the heavy lifting for we novices.

  • Andrew

    Thanks everyone for sharing all this!

  • Bill

    I find that the throttle function is better suited for some situations than debounce is, including window’s resize for when you’d like to show periodic updates whilst continually resizing.

    This simple throttle example curries the throttled function and returns a function that will pass its arguments to the throttled function

    function throttle (callback, limit) {
    
      var wait = false;
      return function () {
        if (!wait) {
    
          callback.apply(null, arguments);
          wait = true;
          setTimeout(function () {
            wait = false;
          }, limit);
        }
      }
    }
    
  • A big thanks to you for these post. After reading these posts I feel how stupid i am before reading these. Every weekend I read your post again and again.

  • Peter

    Excellent article. I feel like some (or all) of these functions should be included in JavaScript interview questions. Might be a little tricky but still good. At least when it comes to senior or advanced developers.

  • I would be happy if JavaScript has a function to check for not defined. Otherwise you need to do a check as this one:

    if( typeof foo !== 'undefined' ) {    
    }
    • Victor
      if ('foo' in window) {
      }
  • Hynek

    I am fairly new to JS, so I’m trying to comprehend how these functions actually work. But right at the first function, debounce, i’ve found a thing that is confusing me. There are 3 arguments at the declaration of debounce function:

    function debounce(func, wait, immediate) {…}

    But only 2 at the example below:

    var myEfficientFn = debounce(function() {
    	// All the taxing stuff you do
    }, 250);

    Is it just a typo, or am I missing something? Thanks!

    • Anthony Eneh

      In javascript if a function is called with missing arguments (less than declared), the missing values are set to: undefined

  • Hey great article about debouncing. I’ve had some trouble in the past debouncing React Controlled components because of the Synthetic Event Pool. Has anyone ever found a good way to do this vanilla, without using lodash or something.

  • Umair Ghazi

    You are awesome David!!

  • Thanks for sharing JavaScript app, and it’s helpful for me!

  • Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!