813Copying Arrays in Javascript with slice(0)

Using Arrays in Javascript retain their values by references.

var oneArray = ["a", "b", "c"];
var anotherArray = oneArray;
anotherArray[0] = "z";

console.log(oneArray, anotherArray);
// ["z", "b", "c"], ["z", "b", "c"]

Ok, but let's say you'll need to make a copy of the array. How do you do that?

var oneArray = ["a", "b", "c"];
var anotherArray = oneArray.slice(0);
anotherArray[0] = "z";

console.log(oneArray, anotherArray);
// ["a", "b", "c"], ["z", "b", "c"]

If you'd really, really want you could also wrap it into a prototype function. Although the code-characters savings are minimal.

Array.prototype.copy = function() {
    return this.slice(0);
}

var anotherArray = oneArray.slice(0);
//vs
var anotherArray = oneArray.copy();

Minimal, but maybe contextually for self-explaining.

809Measuring Text in Canvas

HTML Canvas allows for the drawing of text with

ctx.fillStyle = "666";      // text color
ctx.textAlign = "center";       // alignment
ctx.font = "normal 12px Helvetica Neue";        // font
ctx.fillText("my Text, x , y);      // draw

Sometimes, you might want to draw a border around the text. Getting the textHeight should not present a problem, after all we can set the font size directly. But textWidth is not fixed in that way, because it depends (nnnn) on the actual text that is being displayed.

ctx.measureText takes the text as an argument and returns an object with a width property, describing the width of the text.

var texttWidth = ctx.measureText(columnName).width;

width is for now the only inhabitant of that object, but it would be a fair guess, that it might be more densely populate in the future.

806Replacing HTML elements with jQuery’s replaceWith

Let's say you are having some HTML elements that should only be enabled, if the browser sports certain features. By default, the element is disabled, like in the following example:

Open File...

Next, we check in Javascript is this certain feature is supported. If it is, we replace the disabled element with an active version.

if (isSupported()) {
    $('#item').replaceWith('Open File...');
}

Which results in this:

Open File...

Simple, but effective.

804Freezing Objects and Arrays in Javascript

Since the ECMA-262 specifications (aka Javascript 1.8.5 aka ECMAScript 5th Edition) it is possible prevent Objects from accepting any changes to their properties. After applying Object.freeze(myObj) it won't be possible to change, add or remove properties from myObj.

So far so good.

As in Javascript Arrays also inherit from Object, I could see not reason, why freezing an array should not work.

var obj = {'a':1, 'b:2'};
Object.freeze(obj);
Object.isFrozen(obj);       // returns true
obj.a = 10;                 // new assignment has no affect
obj.a;                      // returns 1

var arr = [1, 2];
Object.freeze(arr);
Object.isFrozen(arr);      // returns true
arr[0] = 10;
arr;                       // returns [10, 2] ... ouch!

It turns out, that it seems to be an implementation bug in Safari (5.1). It's working in the latest Firefox and Chrome releases.

To check whether freezing arrays works in your brower, click the following button:

ps. I asked the question first on Stack Overflow: 'Freezing' Arrays in Javascript?. Thanks for the constructive answers.

802Objects and Key Array Length in Javascript

With arrays is trivial to get the length, with objects, the situations is slightly different. One approach would be to get all the keys and get the length of that array:

Object.keys(myObject).length;

More details at Stackoverflow. This functionality is part of EMCAScript 5 specs.

785Filtering Arrays with ECMAScript 5 and filter()

Suppose you have an array of object:

var array = [   {a:13, b:false },
        {a:7, b:true },
        {a:78, b:true } ]       

The old, ECMAScript 3 way of checking for objects, in which b is true would be a simple for-loop:

var results = [];
for (var i=0; i

Ok, that's working, but not very elegant nor concise. Luckily, ECMAScript 5 offers a nicer way:

var results = array.filter( function(item) {
    return item.b;      // if item.b is true
});

779Sums and NaNs in Javascript

var array = [132, 32, 14, 525, 52, 12, 52];
var sum;
for (var i=0; i

I was slightly puzzled, why the above code outputs Nan instead of the consecutive sums of the array.

var array = [132, 32, 14, 525, 52, 12, 52];
var sum = 0; // sum needs to be init...?
for (var i=0; i

After declaring sum with a number, it works. Still have to find out why.

749Javascript, Array and For-Loops

Ok, looping over an array is something that we do all the time, for most of us it has become a second nature.

var myArray = ["a", "b", "c", "z"];

for (int i=0; i

But I recently came across a Javascript loop that puzzled me slightly.

var myArray = ["a", "b", "c", "z"];

for (var i=0,f ; f=myArray[ i++]; ) {
    console.log(f);
}

Let's have a closer look what's happening here. As a reminder, For-statements have the following structure: for (initiation; condition; increment) { block }

In the initiation, variables are initiated; Condition tests this var, and if it returns true, continues another loop; and here's something funny; the increment is left blank. Don't panic, because we are already incrementing i at the condition test.

This is equivalent:

var myArray = ["a", "b", "c", "z"];

for (var i=0,f ; f=myArray[ i]; i++) {
    console.log(f);
}

This, however, is not the same:

var myArray = ["a", "b", "c", "z"];

for (var i=0,f ; f=myArray[ ++i]; ) {
    console.log(f);
}

i gets incremented before it is used to traverse myArray, therefor resulting in this output:

b
c
z

In nice and simple arrays like in this example, it should not make any difference to test the array length at every time coming around, but a larger ones it might be beneficial.

As usual, take care that the array does not contain any falsy objects. And don't loop an object that way.

Thanks to the Re-introduction to Javascript https://developer.mozilla.org/en/a_re-introduction_to_javascript for clarifying that issue for me.

Update

Turns out things are not so clear after all. Let's consider the following case: I have an array of numbers, I'd like to get the sum of all numbers in the array. One way to do it:

var myArray = [1,2,3,5,7];
for (var i=0, sum=0; i

So, shouldn't this work too...?

var myArray = [1,2,3,5,7];
for (var i=0, sum=0; sum = myArray[i]; i++) {};
console.log(sum);       // undefined

Apparently not... I am trying to find out why not.

This, on the other hand works:

for (var i=0, sum=0; this[i]; sum+=this[i++]){};

737Processing Math convenience methods in pure Javascript

Like everybody else, I am huge fan of Processing, and like everybody else I find myself working more and more with Javascript.

There is a implementation port of Processing (aptly called ProcessingJS) by John Resig of jQuery fame. It's still lacking the 3D features, but not because they would be hard to implement in JS, but rather the OpenGL bindings in the browsers don't exists wide-spreadly (yet). Let's hope WebGL is going to change that soon.

After working a lot with Processing, one becomes quite spoilt with it's methods. The two best kept secrets of Processing are on the one hand the matrix state operations - pushMatrix(), and popMatrix() - and on the other hand the very handy map() method, basically mapping a number from one range to another.

In Javascript's canvas you have the pushMatrix() popMatrix() equivalents as save() and restore(). The map() methods does not exists as such, but it is easy implemented. I took a look at the Processing source and rewrote the Java methods in Javascript.

var p5 = {};

p5.sq = function(a) {
    return a*a;
}

p5.constrain = function(amt, low, high) {
  return (amt < low) ? low : ((amt > high) ? high : amt);
}

p5.degrees = function(radians) {
  return radians * (180/Math.PI);
}

p5.mag = function(a, b) {
  return Math.sqrt(a*a + b*b);
}

p5.dist = function(x1, y1, x2, y2) {
  return Math.sqrt(p5.sq(x2-x1) + p5.sq(y2-y1));
}

p5.lerp = function(start, stop, amt) {
  return start + (stop-start) * amt;
}

p5.norm = function(value, start, stop) {
  return (value - start) / (stop - start);
}

p5.map = function (value, istart, istop, ostart, ostop) {
    return ostart + (ostop - ostart) * ((value - istart) / (istop - istart));
}

The code including comments and a minified version is also up on GitHub, show you love and fork it. https://github.com/trembl/p5.Math.js/

Update - Extending Math

Rather than creating a dedicated object, and as all the function are kind of related to Math, I thought it would be a good idea to extend the Math object itself. (Well, it's a good idea for my project - your mileage, especially if you are flying with a lot of libraries might vary).

And how to you extend a Javascript build-in object? With prototype, right?

Math.prototpe.sq = function(a) {
    return a*a;
}

Wrong. Because Math does not have a constructor, it therefore hasn't been constructed and therefore does not have the prototype method. It has been instantiated and you add methods (or functions?) like this:

Math.sq = function(a) {
    return a*a;
}

You get the idea. The new code also lives at GitHub, check it out. https://github.com/trembl/p5.Math.js/

721jQuery’s Live Clicks

Update Since jQuery 1.7 live() is marked for deprecation and should not be used any more. Use on() instead.

When content is dynamically created by jQuery it is not automatically inserted into the DOM. Therefore, if you dynamically add - for example - a link, and you want to capture a click on this link, it is necessary to use the live() function.it is necessary to use the on() function.



NG. The link is added to the container, but the click function is not called.

// $('.container > a').live('click', function(e) {  // pre jQuery 1.7
$(document).on('click', '.container > a', function(e){
    // do things
    e.preventDefault();
});

OK. The link is added to the DOM, and the click function is called.

Yeah.

Of course the same also goes for appended elements:

$("#container").append('');

// ...

// $('#container canvas').live('mousedown', function (e) {
$(document).on('click', '#container canvas', function(e){   // pre jQuery 1.7
    console.log(this.id);
});