JavaScript code snippets and links

Hans's picture
Sun, 2009-09-06 19:15 by Hans · Forum/category:

Table of contents

for this article

Introduction

This article holds a collection of code pieces for re-use. It is expected to grow.

The content is somewhat advanced and not meant for beginners.

Static variables

The following examples show pieces of code that should be used inside functions. They make no sense in the global scope.

JavaScript has no static keyword, although it is one of the reserved words, presumably for future expansion.

But we don't need it for now. Instead of:

static counter; // wrong!

which is wrong, we can, inside a function fn, simply use:

fn.counter

But first we have to initialize it only once, for example, with:

var fn.counter = fn.counter || 0;

Or, if this is too javascripty for you, use this slightly longer, but possibly not slower version:

var fn.counter;
if (!fn.counter) fn.counter = 0;

So here is a complete example for a web browser, in a function named count, where we can use the alert(…) function:

function count() {
  count.counter = count.counter || 0;
  alert(++count.counter);
}

count();
count();
count();

It will count to 3. Look ma, no globals!

If you want to try it as a simple text file outside a web browser, but inside Microsoft Windows, then copy the following program into a text file, name it count.js, and double-click on it.

function count() {
  count.counter = count.counter || 0;
  WScript.Echo(++count.counter);
}

count();
count();
count();

So how does it work? count is a function, but functions are also objects. Since objects can have properties, this one can have the property counter.

This is also the meaning of the magic "lambda" word in the science of programming languages. In JavaScript, functions, which are the same as methods, are first-class objects that can even be applied to other objects by means of the apply or call method. Tell that a Java programmer and he will drop dead. C# programmers at least have a clue. They know them as "delegates".

A block with local variables

As you should already know, JavaScript knows only two variable scopes, function and global. If a newcomer to JavaScript believes that the variable elementStyle in the following (syntactically and semantically correct) piece of code is local to the block, he is in for a bad surprise.

{
  var elementStyle =
      document.getElementById("xy").style;
  elementStyle.color = "red";
  elementStyle.visibility = "visible";
}

In fact, if the block is inside a function, the scope of the variable elementStyle is the function, otherwise it is global, which is even worse.

For this reason the use of such blocks is not recommendable, because it is misleading for programmers, who do not yet know JavaScript inside out, and also totally superfluous. The only places where such blocks are needed is in connection with control flow commands like if, while, for, switch, etc.

Global variables are rather evil, so what can we do? It is simple. In many cases like this, where we want to emulate something we know from other languages in JavaScript, we can actually use a function to do it. This example effectively creates a block with local scope:

(function () {
  var elementStyle =
      document.getElementById("xy").style;
  elementStyle.color = "red";
  elementStyle.visibility = "visible";
}());  

Technically this is a nameless function, which is defined and immediately executed. The effect is the same as that of a block in other languages.

The parentheses behind and around the nameless function definition are needed, because we need to turn it into an expression first, before we can call it. In other words, a function definition cannot be called, particularly not a nameless one, but a function can, by appending parentheses to it and then turning it into an expression with another pair of outer parentheses.

By the way, if you embed a nameless function (inner) in another function (outer), it has access to all variables in the outer function (except for the pseudo-variable this, which is apparently due to an oversight in the design of JavaScript and is planned to be fixed in a future version). The standard workaround (courtesy Douglas Crockford) is the following line in the outer function:

var that = this;

Then refer to that in the inner function.

Fixing the for…in statement

[This and the following are some pieces of code from one of the true masters of JavaScript, Douglas Crockford. Be sure to read his web site, particularly his articles on JavaScript, and watch his lectures.]

The problem is that the for…in statement delivers not only the keys in the object, but also all inherited keys, for example those that had been added to the prototype. Here is the recommended workaround:

for (name in obj) {
    if (obj.hasOwnProperty(name)) {
        …
    }
}

From for in Intrigue by Douglas Crockford

Prototypal inheritance

The following code creates a new object that inherits directly from an old object. Note that JavaScript's new operator does not do this—it inherits from the old object's prototype instead, in a questionable attempt to emulate Java behavior.

Note that JavaScript does not have classes, but it does not need them either.

function createNewObjectFrom(o) {
  function F() {}
  F.prototype = o;
  return new F();
}

Usage example:

newObject = createNewObjectFrom(oldObject);

From Prototypal Inheritance in JavaScript by Douglas Crockford

Styles

To read a dynamic style, i.e. one which may have been changed by JavaScript code, you have to use both the Microsoft and the W3C methods.

Microsoft

node.currentStyle.stylename

W3C

document.defaultView().
    getComputedStyle(node, "").
    getPropertyValue("stylename")

Events

Add event listener

W3C

element.addEventListener(
  type, eventListener, false
);

Note that the third parameter, here false, indicates that event "bubbling" is active, as it always is in the Microsoft variant, meaning that events on subordinate elements "bubble" up and are caught in the event handler of the superior element.

Microsoft

element.attachEvent(
  "on" + type,
  eventListener
);

Combined example for the click event

function addEvent(
  element,
  eventNameWithoutOn,
  eventListener
) {
  try {
    element.addEventListener(
      eventNameWithoutOn,
      eventListener,
      false
    );
  } catch (ignore) {
    try {
      element.attachEvent(
        "on" + eventNameWithoutOn,
        eventListener
      );
    } catch (ignore) {}
  }
}

Analogous function to remove an event listener

function removeEvent(
  element,
  eventNameWithoutOn,
  eventListener
) {
  try {
    element.removeEventListener(
      eventNameWithoutOn,
      eventListener, false
    );
  } catch (ignore) {
    try {
      element.detachEvent(
        "on" + eventNameWithoutOn,
        eventListener
      );
    } catch (ignore) {}
  }
}

Determining the event target element

To determine the target element of a bubbled event in various browsers, i.e. an event that was not caught at its element but rose up in the DOM tree, looking for an event listener, you can use this code as your event listener in the parent or higher element:

function (e) {
  if (!e) e = event;
  var target = e.target || e.srcElement;
  …
}

Cancel bubbling

To tell an event e that it should no longer bubble up in the DOM while looking for a higher-level event listener, do this at the end of your event listener:

e.cancelBubble = true;
try {
  e.stopPropagation();
} catch (ignore) {}
return false;

Absolute element position

top

function absTop(el) {
  var t = 0;
  do t += el.offsetTop;
  while (el = el.offsetParent);
  return t;
}

left

function absLeft(el) {
  var t = 0;
  do t += el.offsetLeft;
  while (el = el.offsetParent);
  return t;
}

HTML to text

Sometimes we want to turn some HTML code into plain text, removing any HTML tags and converting any HTML entity or other HTML encoding into plain text. The following simple code does it.

function htmlToText(s) {
  var div = document.createElement("div");
  div.innerHTML = s.replace(/<.*?>/g, "");
  return div.innerHTML.replace("&amp;", "&");
}

The last replace is needed, because the ampersand (&) often remains encoded when it is not the beginning of a HTML entity.

Insert thousands separator

Assuming that sn is a string containing a number consisting only of digits and a decimal separator, use this expression:

sn.replace(/(\d+?)(?=((\d{3})+)\D)/g, "$1,");

The character after $1 is the thousands separator to be inserted. {3} indicates to insert the separator every 3 places before the decimal sign.

If there is no decimal separator, use this expression instead:

sn.replace(/(\d+?)(?=((\d{3})+)$)/g, "$1,");

Links

Average: 4 (2 votes)