Written by 11:37 Basics of JavaScript, Languages & Coding

JavaScript Recipes: A Problem-Solution Article

Why reinvent the wheel every time you run into a problem with JS? This article is chock-full of code recipes that address common programming tasks.

Q: What is Javascript object creation best practice?

How to protect themselves from errors, if the new keyword is used? I always capitalize constructor names. I use this instanceof funcName to validate the instance, and avoid this instanceof arguments.callee due to performance reasons.

A:

It is definitely a good practice to create objects with help of the new keyword. It is common convention: constructor functions have names that start with capital letters. The fact that constructor invocation is different from regular function invocation is another reason we give constructors names start with capital letters. Constructors are created to be invoked as constructors, with the new keyword. Constructors will not work properly, if they are invoked as a regular functions.

var Obj = function () {
    "use strict";
    this.pew = 100;
};
 
// Correct
new Obj.pew++;
 
// A user will get an error
Obj(); // TypeError: this is undefined

The new keyword must not be used for factory methods and short jQuery constructors:

// Good: code is short
var Obj = function () {
    "use strict";
    this.pew = 100;
};
 
// Not good: superfluous and unnecessary code
var Obj = function () {
    if (!(this instanceof Obj)) {
        return new Obj();
    }
    this.pew = 100;
};

Q: How to determine which mouse button is clicked?

A:

All the buttons call the mousedown mouseup events. Only left mouse button calls the clickevent. In the event handler, you must check the button code event.button to determine which one has been clicked. (0 — Left, 1 — Middle, 2 — Right). But do not forget about IE8 where it doesn’t work.

var button = document.getElementById('button'),
              //  0       1         2
   buttonMap = ['Left', 'Middle', 'Right'],
   handler = function (event) {
       event = event || window.event;
       alert(buttonMap[event.button] + ' id: ' + event.button);
    };
 
if (button.addEventListener) {
    button.addEventListener('mousedown',  handler, false);    
} else {
    // IE         0      1       2        3      4
    buttonMap = ['???', 'Left', 'Right', '???', 'Middle'];
    button.attachEvent('onmousedown',  handler);
}

jQuery fixes this problem. You need to check event.which instead event.button

$('button').mousedown(function (event) {
    alert(['Left', 'Middle', 'Right'][event.which]);
});

Here is a jsfiddle example.

For more details please read the Which mouse button has been clicked? paragraph.

jQuery event.which: api.jquery.com/event.which/

Q: How to catch keystroke events?

Example: I have a table that is larger the screen size. I need to navigate between rows with the help of keyboard arrows. How to forbid the browser scrolling the window while navigating the table?

A:

To do so, you need to disable the default action. You can use jQuery:

// IE and Safari do not track arrows for keypress,
// and Оpera misbehave for  keyup
$(window).bind($.browser.opera ? 'keypress' :
 'keyup', function (event) {
    event.preventDefault();
    // or
    return false;
});

There is an important point. You must perform the preventDefault() before the defaultAction is executed. For example, you do not want to pass the focus to the input while clicking it — then you need to add the event handler to the event in the chain, before the mousedown defaultAction :

$('input').bind('mousedown', function (event) {
    event.preventDefault();
    // or
    return false;
});

The event chain looks as follows:

1. mousedown
2. focus (before focus, the blur is invoked for another object)
3. mouseup
4. click

If you add an event for focus or “below”, nothing will work, because defaultAction will be executed after mousedown.

Q: How to stop GIF animation by pressing ESC when the button is binded?

A:

The answer is similar to one listed above. While pressing ESC, some browsers stop GIF animation and stop page loading. It is default behaviour. You need to disable the default behaviour with event.preventDefault():

$(document).bind($.browser.webkit ? 'keydown' :
 'keypress', function (event) {
    // Esc is pressed
    if ((event.which || event.keyCode) === 27) {
        event.preventDefault();
        // or
        return false;
    }
});

Q: What is the ( ) operator?

A:

Parser determines what kind of parentheses goes after a function: grouping or the function call operator. If we do like this:

function () {
    // source
}()

We will get a syntax error, because we do not have a function name. Function declaration must always have a name! If we add a name:

function foo() {
  // source
}()

We suppose that everything is OK as far as we already have a name. However, we still have an error. This time we have a grouping operator without an expression inside. Note, this time it is the grouping operator that follows after function declaration and not parentheses for function call.
There are several ways to avoid parsing errors:

!function () {
    // source
}();

+function () {
    // source
}();

[function () {
    // source
}()];

var a = function () {
    // source
}();

You may find this approach in jQuery. This allows you to separate all the code in one block with local scope. It doesn’t clutter up the global namespace and allows minifiers better compress the code. This also speeds up access to internal variables.
For more details read here: Named function expressions demystified

Q: How to organize the commands execution queue with a delay and no script hangup?

A:

JavaScript has one thread. Whenever you perform a chain of operations (cycles or heavy functions) the user interaction with the UI is blocked. Web workers help to prevent UI blocking. If there is no possibility to use web workers, you need to perform the optimization of cycles and “heavy” functions.
Nicholas C. Zakas stated — If the interface responds to user input within 100 ms, the user thinks that he is “directly manipulating the objects in the user interface.” Any amount of time more than 100 ms means the user feels disconnected from the interface. Since the UI cannot update while JavaScript is executing, the user cannot feel in control of the interface if that execution takes longer than 100 ms.

The following code is optimized:

function timedProcessArray(items, process, callback) {
    var todo = items.concat();   //create a clone of the original
    setTimeout(function () {
        var start = +new Date();
        do {
            process(todo.shift());
        } while (todo.length > 0 && (+new Date() - start < 50)); if (todo.length > 0) {
            setTimeout(arguments.callee, 25);
        } else {
            callback(items);
        }
    }, 25);
}

function saveDocument(id) {
    var tasks = [openDocument, writeText, closeDocument, updateUI];
    timedProcessArray(tasks, [id], function () {
        alert("Save completed!");
    });
}

The timedProcessArray function blocks UI for 25 ms, executes a chain of actions and then releases UI for another 25 ms and so on.

Q: How to determine that a user is finished resizing the window?

A:

There is no event for this case. However, you can get the information about when the user resized the window last time?

var time = 0,
    timerId,
    TIME_ADMISSION = 100; // 0.1s
 
function onresizeend () {
    console.log('onresizeend');
};
 
function resizeWatcher () {
    if (+new Date - time >= TIME_ADMISSION) {
        onresizeend();
        if (timerId) {
            window.clearInterval(timerId);
            timerId = null;
        }
    }
};
 
$(window).resize(function () {
    if (!timerId) {
        timerId = window.setInterval(resizeWatcher, 25);
    }
    time = +new Date;
});

Q: How to open a new window (not a tab) with the help of window.open()?

A:

This behavior depends on the browser. Opera always opens a tab and it is represented as a window, Safari always opens a window, Chrome, Firefox, and IE are manageable.

If you pass additional parameters — the position of the window, a new window opens:

window.open('http://www.codingsight.com', '_blank',
 'toolbar=0,location=0,menubar=0');

If you do not pass it, a new tab opens:

window.open('http://www.codingsight.com');

More often a new tab is required. In this case, you may have some troubles with Safari. By default (depending on settings) it opens a new window. However, while clicking a link and holding Ctrl+Shift/Meta+Shift it opens a new tab regardless the settings.To open a new tab you need to emulate a mouse click when Ctrl+Shift/Meta+Shift are hold.

function safariOpenWindowInNewTab(href) {
    var event = document.createEvent('MouseEvents'),
        mac = (navigator.userAgent.indexOf('Macintosh') >= 0);
 
    // executing Ctrl+Shift+LeftClick/Meta+Shift+LeftClick
    // create our own event
    event.initMouseEvent(
        /* type */          "click",
        /* canBubble */     true,
        /* cancelable */    true,
        /* view */          window,
        /* detail */        0,
        /* screenX, screenY, clientX, clientY */ 0, 0, 0, 0,
        /* ctrlKey */       !mac,
        /* altKey */        false,
        /* shiftKey */      true,
        /* metaKey */       mac,
        /* button */        0,
        /* relatedTarget */ null
    );
 
    // creating a link in memory and click it with help of our event
   $('', {'href': href, 'target': '_blank'})[0].dispatchEvent(event);
}

Q: What is the best way to clone/deep copy?

A:

If an oldObject is not going to be changed, the most efficient way is to use the prototype:

function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}
 
var newObject = object(oldObject);

If you need a true cloning, it is better recursively go through the object tree:

var cloner = {
    _clone: function _clone(obj) {
        if (obj instanceof Array) {
            var out = [];
            for (var i = 0, len = obj.length; i < len; i++) {
                var value = obj[i];
                out[i] = (value !== null && typeof value === "object") ? _clone(value) : value;
            }
        } else {
            var out = {};
            for (var key in obj) {
                if (obj.hasOwnProperty(key)) {
                    var value = obj[key];
                    out[key] = (value !== null && typeof value === "object") ? _clone(value) : value;
                }
            }
        }
        return out;
    },

    clone: function (it) {
        return this._clone({
            it: it
        }).it;
    }
};

var newObject = cloner.clone(oldObject);

For jQuery:

// simple copy
var newObject = jQuery.extend({}, oldObject);
 
// deep copy
var newObject = jQuery.extend(true, {}, oldObject);

Q: How to create a destructor/finalizer analogue? How to manage object lifetime?

A:

In JavaScript, the object is deleted when there are no references to this object.

var a = {z: 'z'};
 
var b = a;
var c = a;
 
delete a.z;
delete a; // we just deleting the "а" ref.
console.log(b, c); // object exists in fact: Object {} Object {}, however it is empty

You can not delete the object entirely by using a destructor. You can only clean its content.

Q: How to process binary data?

A:

In JavaScript all numbers are provided for use in the string format. There are no built-in tools to work with binary data. However, there is the JavaScript BinaryParser library to work with binary data. Its code is a nightmare!

ECMAScript 6 + (strawman) contains the StructType draft (like struct in C++). Here’s how it might look in the future:

const Point2D = new StructType({ x: uint32, y: uint32 });
const Color = new StructType({ r: uint8, g: uint8, b: uint8 });
const Pixel = new StructType({ point: Point2D, color: Color });

const Triangle = new ArrayType(Pixel, 3);

let t = new Triangle([{ point: { x: 0, y: 0 }, color: { r: 255, g: 255, b: 255 } },
                      { point: { x: 5, y: 5 }, color: { r: 128, g: 0, b: 0 } },
                      { point: { x: 10, y: 0 }, color: { r: 0, g: 0, b: 128 } }]);

You can use JavaScript typed arrays to read from buffers, however, you will not be able to get a number in binary format.

Q: How to change function context variables from another function?

A:

1. You can pass a reference to the primer context object in smth.
2. You can pass a function created in the primer context into the smth function.

var primer = function (){
    var a, b, c, d, e = {};
 
    smth(function () {
        a = 1;
        b = 2;
        c = 3;
        d = 4;
    }, e);
 
    alert([a, b, c, d, e.pewpew]);
},
smth = function (callback, e) {
    callback();
    e.pewpew = "pewpew";
};
 
primer();

Q:  How to pass execution scope from one function to another?

A:

There’s no way! There was the possibility to use __parent__ in FireFox 3.6. In v.4 they have thrown this feature.

Q: How to get a global object without explicit pointing, without eval and with ‘use strict’?

A:

There’s no way! If we omit one of the conditions or use the global scope:

// 1: eval - on
(function(){
    "use strict";
    var globalObject = (0, eval)("this"); 
    return globalObject;
}());
 
// 2: specifying name - on
(function(global){
    // ...
}(window));
 
// 3: "use strict" - off
(function(){
    return this;
}());
 
// 4: If execute in global, we get a ref to a global var. But it will not work in other cases.
// Most optimal approach
"use strict";
(function(global){
    // global
})(this);

Q: I have Intercepted a javascript event, can I restart it later?

A:

You can explicitly pass a reference to an event handler:

$('#smth').click(function onSmthClick(event) {
    if (smth) {
        // create handler
        event.handlerFunction = onSmthClick;
        event.handlerContext = this;
        // pass it to some obj
        // now otherObjectSetSomeEvent can use event.handlerFunction and call handler
        otherObjectSetSomeEvent(event);
    } else {
        // do something else
    }
});

Unfortunately, it is not a good solution. It is better to change the logic and separate the handler:

$('#smth').click(function handler1(event) {
    if (smth) {
        // pass it to some obj
        leftObjectSetSomeEvent(event, function handler2(e) {
            // do something with event or e
        });
    } else {
        // do something else
    }
});

function leftObjectSetSomeEvent(event, callback) {
    callback(event);
    // do something with even
}

Q: How to intercept all clicks on the page?

A:

You need to add an event handler to the click event to the “last/lowest” element in the DOM tree.

// jQuery
$(window).bind('click', function (e) {
    console.log('Clicked on ', e.target);
});
 
// You can limit it with some area by using jQuery delegate
$('#pewpew').delegate('*', 'click', function (e) {
    console.log('Clicked on ', e.target);
});
 
// limit targets
$('#pewpew').delegate('.pewpew', 'click', function (e) {
    console.log('Clicked on element with .pewpew class name');
});

Q: How to execute XHR without jQuery?

A:

Not a cross-browser function:

function xhr(m,u,c,x){with(new XMLHttpRequest)onreadystatechange=function(x){readyState^4||c(x.target)},open(m,u),send(с)}

A cross-browser function is little bit longer:

function xhr(m,u,c,x){with(new(this.XMLHttpRequest||ActiveXObject)("Microsoft.XMLHTTP"))onreadystatechange=function(x){readyState^4||c(x)},open(m,u),send(с)}

How to use:

xhr('get', '//ya.ru/favicon.ico', function(xhr){console.dir(xhr)});

If you have something to add, please drop a comment below! Thank you!

Tags: Last modified: September 23, 2021
Close