JavaScript

From WhyNotWiki
Jump to: navigation, search

This is almost exclusively about using JavaScript for web sites. I suppose I should probably separate out the more general stuff about JavaScript (the language, syntax, etc.)...

Contents

The language


http://www.howtocreate.co.uk/tutorials/javascript/variables JavaScript tutorial - Variables

http://javascript.crockford.com/remedial.html Remedial JavaScript


Problem: typeof() often just says "object" and doesn't tell you what kind of object...

Most of the "solutions" I've found are only able to identify built-in objects, like Array and String.

Planet PDF - Testing for Object Types in JavaScript (http://www.planetpdf.com/developer/article.asp?ContentID=testing_for_object_types_in_ja). Retrieved on 2007-05-11 11:18.


What I came up with relies on the fact that every object in JavaScript has a constructor function, which you can access explicitly by means of the constructor property. As an experiment, I suggest you try:

app.alert( (new Array).constructor );

[...] You'll get a dialog that says

function Array() { [native code] }

The trick, then (are you thinking what I'm thinking?), perhaps, is to cast the object's constructor to a String and test the result for the presence of the substring 'Array' (if we're testing for arrayness). In other words:

// Return a boolean value telling whether // the first argument is an Array object. 
function isArray() {
  if (typeof arguments[0] == 'object') {  
    var criterion = arguments[0].constructor.toString().match(/array/i); 
    return (criterion != null);
  }
  return false;
}

This is what I'm talking about: magnetiq.com » Blog Archive » Finding Out Class Names of JavaScript Objects (http://magnetiq.com/2006/07/10/finding-out-class-names-of-javascript-objects/). Retrieved on 2007-05-11 11:18.


The constructor property of a JavaScript object (except for intrinsic objects such as window and document) points to the object’s constructor, which has the same name as the class of the object. The name of the constructor can be obtained by parsing out the function name part from the string representation of the contructor. The following function makes use of this idea to implement a utility function for getting the exact class names of extrinsic objects. It returns undefined for intrinsic objects:

/* Returns the class name of the argument or undefined if
   it's not a valid JavaScript object.
*/
function getObjectClass(obj)
{
    if (obj && obj.constructor && obj.constructor.toString)
    {
        var arr = obj.constructor.toString().match(
            /function\s*(\w+)/);

        if (arr && arr.length == 2)
        {
            return arr[1];
        }
    }

    return undefined;
}

Enumerating objects

Dean Edwards: Enumerating JavaScript Objects (http://dean.edwards.name/weblog/2006/07/enum/). Retrieved on 2007-05-11 11:18.


Basically, by calling the forEach method on a function you can enumerate an object and compare the keys with that function’s prototype. That means that you will only enumerate custom properties of the object. An example is required:

// create a class
function Person(name, age) {
        this.name = name || "";
        this.age = age || 0;
};
Person.prototype = new Person;

// instantiate the class
var fred = new Person("Fred", 38);

// add some custom properties
fred.language = "English";
fred.wife = "Wilma";

Enumerate using the standard forEach method:

forEach (fred, print);
// => name: Fred
// => age: 38
// => language: English
// => wife: Wilma

Enumerate using the Person.forEach method:

Person.forEach (fred, print);
// => language: English
// => wife: Wilma

Note that the properties defined on the prototype are not enumerated in the second example.


Lambda notation (sort of) (>=1.8)

John Resig - JavaScript 1.8 Progress (http://ejohn.org/blog/javascript-18-progress/). Retrieved on 2007-05-11 11:18.


I think, probably, my favorite use for this shorthand will be in binding event listeners, like so:

document.addEventListener("click", function() false, true);

Or combining this notation with some of the array functions from JavaScript 1.6:

elems.some(function(elem) elem.type == "text");

This will give you some JS/DOM code that looks downright elegant.

Iterators / "reduce" / etc.

John Resig - JavaScript 1.8 Progress (http://ejohn.org/blog/javascript-18-progress/). Retrieved on 2007-05-11 11:18.


 // Add an iterator to all numbers
 Number.prototype.__iterator__ = function() {
     for ( let i = 0; i < this; i++ )
         yield i;
 };
  
 // Spit out three alerts
 for ( let i in 3 ) alert( i );
  
 // Create a 100-unit array, filled with zeros
 [ 0 for ( i in 100 ) ]
  
 // Create a 10-by-10 identity matrix
 [[ i == j ? 1 : 0 for ( i in 10 ) ] for ( j in 10 )]

...

Thus, if you wanted to sum up the numbers 0-99, you could do it like so (using JavaScript 1.8, and the number iterator from above):

[x for ( x in 100 )].reduce(function(a,b) a+b);

Pretty slick!

You could also use the reduce function to do things like "merge sets of DOM nodes into a single array", like so (thanks Andrew!):

nodes.reduce(function(a,b) a.concat(b.childNodes), []);

...

Prototype uses “inject” (our version of reduce) internally for things like DOM node accumulation. Imagine something like getting all of a collection’s child nodes:

function getChildren(nodes) {
  return nodes.inject([], function(total, node) total.concat(node.childNodes));
}

Implementations for use in pre-JavaScript-1.6 browsers

Sugar Arrays: Porting over JavaScript 1.6 Array methods (http://www.dustindiaz.com/sugar-arrays/). Retrieved on 2007-05-11 11:18.


As of Firefox 1.5, there has been a new wide array of Array helpers that were included in JavaScript 1.6. That’s all fine and great, however there isn’t a single other browser on the market (as of this writing) that supports any of the new array methods (let alone JavaScript 1.6). What’s a developer to do? [...] If you’re developing for todays grade A browsers, then you can count on being able to extend Array through it’s prototype. But, since I have a natural love toward things that are sweet, I’ve already done it for you with sugar and spice.


Sugar Arrays: JavaScript 1.6 Array Methods (http://www.dustindiaz.com/basement/sugar-arrays.html). Retrieved on 2007-05-11 11:18.



Array.prototype.forEach = function(fn, thisObj) {
    var scope = thisObj || window;
    for ( var i=0, j=this.length; i < j; ++i ) {
        fn.call(scope, this[i], i, this);
    }
};

...

Array.prototype.map = function(fn, thisObj) {
    var scope = thisObj || window;
    var a = [];
    for ( var i=0, j=this.length; i < j; ++i ) {
        a.push(fn.call(scope, this[i], i, this));
    }
    return a;
};



Destructuring assignment (>= 1.7)

John Resig - JavaScript 1.8 Progress (http://ejohn.org/blog/javascript-18-progress/). Retrieved on 2007-05-11 11:18.


[x,y] = z
for ([x,y] in z)
[x for ([x,y] in z)]


Array comprehension (>= 1.7)

Generator expressions (>= 1.8)

John Resig - JavaScript 1.8 Progress (http://ejohn.org/blog/javascript-18-progress/). Retrieved on 2007-05-11 11:18.


Because generator expressions yield an easy way to generate lazy evaluation, and lazy evaluation is cool.

Optional parameters / default values for functions

I was surprised to find out that you can't specify default values for parameters in the formal parameter list:

function f(a, b = 'default') { ... }

Fortunately, that's not a big problem. Even if a parameter doesn't have a default value (it can't), you can still omit it when you call the method. In other words, no arguments are required in JavaScript; you can omit as many of the final arguments as you want and it won't throw an error.

However, inside of the function, the arguments will not be initialized. They will be of type 'undefined.

Workaround: How to specify default values: Method 1:

function f(a, b = 'default')
  if (typeof(b) == 'undefined') { b = 'default' };
  ...
}

Workaround: How to specify default values: Method 2: "fatbrain" (2006-06-16). Default Arguments in JavaScript Functions (http://parentnode.org/javascript/default-arguments-in-javascript-functions/). Retrieved on 2007-05-11 11:18.


I often run into people who need (for reasons that are beyond me) to be able to give arguments in a javascript function default values. Kinda like what you have in C/C++:

void foo(int a, int b = 42) { ... }

I felt an urge to do it, so I sat down for a few minutes, trying to conjure something nifty that would be intuitive enough for even me to use.

The solution I came up with, and that hopefully will put an end to the I-need-default-arguments-in-javascript rant for good.

The solution is quite simple, and very intuitive. It may have some shortcomings but keep in mind I didn't explicitly write it for you. You could probably modify my solution to fit your needs anyway.

I've seen alot of developers (including myself, long time ago) using this pattern as a workaround for the lack of built-in support for default-arguments.

function foo(a, b)
{
  a = typeof(a) != 'undefined' ? a : 42;
  b = typeof(b) != 'undefined' ? b : 'default_b';
  ...
}

Which most developers probably find sufficient; I don't.

The solution may look scary at a quick first glance, but bear with me. I'll start by writing the framework-code. The code that will be required for this thing to work.

Function.prototype.defaults = function()
{
  var _f = this;
  var _a = Array(_f.length-arguments.length).concat(
    Array.prototype.slice.apply(arguments));
  return function()
  {
    return _f.apply(_f, Array.prototype.slice.apply(arguments).concat(
      _a.slice(arguments.length, _a.length)));
  }
}

See that wasn't so scary :).

In order for this to work you have to declare you functions in a special way. There are basicly two ways of declaring a function.

function foo(a, b)
{
  ...
}

Is identical to (apart from the lexical difference, and some other minor things)

var foo = function(a, b)
{
  ...
}

In order for this solution to work you have to declare all functions on which you wish to have default-arguments the latter way.

Usage

var foo = function(a, b)
{
  ...
}.defaults(42, 'default_b');

Is identical to the first code-block but without adding any code in the function-body.

Example

var bar = function(a, b)
{
}.defaults('default_b');

bar();
// a = undefined, b = 'default_b'

bar(1);
// a = 1, b = 'default_b'

bar(1, 'some_value');
// a = 1, b = 'some_value'
 

Prototype

[Enumerator (category)] [methods]

AlternateIdea: Prototype Meets Ruby: A Look at Enumerable, Array and Hash (http://encytemedia.com/blog/articles/2005/12/07/prototype-meets-ruby-a-look-at-enumerable-array-and-hash) (2005-12-07). Retrieved on 2007-05-11 11:18.


...

    $H(F.Products[0]).each(function(product) {
      logger.info(product.key + ": " + product.value);
    });

...


AlternateIdea: Inject and Pluck: The Secret Sauce Behind Prototype's Enumerable (http://encytemedia.com/blog/articles/2006/12/28/prototype-enumerable-pluck-and-inject). Retrieved on 2007-05-11 11:18.


...

  // WRONG
  grades: function() {
    return this.results.map(function(result) {
      return result.grade;
    });
  }

While the above code works, and works well, Prototype has a tool for this type of operation: pluck. Pluck accepts one parameter, the name of the key in which you wish to extract a value from. We have an array of objects, and we want to get the grades by plucking the grade value from each student. Here it is:

  // CORRECT
  grades: function() {
    return this.results.pluck('grade');
  }

When we invoke pluck on an array of objects, it will iterate over those objects grabbing the value in each one corresponding to the key you gave it. Simple, one-liner! [...]

In Ruby, you would do that like this:

def grades
  results.map(&:grade)
end

AlternateIdea: Inject and Pluck: The Secret Sauce Behind Prototype's Enumerable (http://encytemedia.com/blog/articles/2006/12/28/prototype-enumerable-pluck-and-inject). Retrieved on 2007-05-11 11:18.


Inject is also a great way to build arrays and objects. What if we wanted to get the names of the students who correctly answered the bonus question? You might be tempted to write something like this:

  // WRONG
  gotBonus: function() {
    var students = []
    this.results.each(function(student) {
      if(student.bonus) students.push(student.name);
    });
    return students;
  }

Again, the code above works, but it’s about the nastiest way to write it. It can be done in far fewer lines of code using inject:

  // CORRECT
  gotBonus: function() {
    return this.results.inject([], function(array, student) {
      return student.bonus ? array.concat([student.name]) : array;
    });

Inject takes care of creating and building our anonymous array and we just return the results of inject as our final outcome. In the case of our students, we’d get back the array [“Sally”, “Joe”]. We pass an array as the memo (lump of snow) and if the student got the bonus question right, we pack on a little more snow to our snowball, and if they got it wrong, we just leave our snowball as is and pass it back.


Libraries/frameworks


JQuery

http://michael.futreal.com/blog/0000052 Javascript Library Smackdown: JQuery v. Prototype

Fork

http://forkjavascript.org/

Prototype

http://prototypejs.org/

MooTools

http://mootools.net/

http://addictedtonew.com/archives/194/mootools-looks-cool/ Mootools Looks Cool

Yahoo! UI library

http://peter.michaux.ca/article/340 Fork JavaScript library launch


I like the Yahoo! UI library. Of the JavaScript libraries I've used it has the best API. The YUI library has many valuable nuggets of information about browser bugs and workarounds. The code approach of YUI suits browser scripting well. However there are more than a few places in the code where I'm left scratching my head and thinking "why did they do that?" Maybe that is how every developer looks at another developers code. The YUI API is the starting point for much of the Fork API.

Ext JS

http://extjs.com/ Ext JS - JavaScript Library

 

Javascript for the web (effects, etc.)

Learning/Reference

http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference Core JavaScript 1.5 Reference - MDC

http://www.quirksmode.org/js/contents.html

onLoad

[Snippets (category)]

function init() {
  //...
}
window.onload = init;

http://www.quirksmode.org/js/events_tradmod.html:

var old = (element.onclick) ? element.onclick : function () {};
element.onclick = function () {old(); spyOnUser()};

childNodes

http://developer.mozilla.org/en/docs/DOM:element.childNodes

This is not of type Array, but of type NodeList.

Which means, for example, that I can't use map, and can't do nice iterator stuff like this:

$('feedback_updated').innerHTML = el.childNodes.map( function(value, i, arr) { 
  return value.innerHTML; 
} ).join(', ');

Instead, I have to do all this:

    array = [];
    for (var node = el.firstChild; node != null; node = node.nextSibling) {
      array.push(node.innerHTML);
    }
    $('feedback_updated').innerHTML = array.join(', ');

Neat libraries / code

cssQuery

cssQuery() (http://dean.edwards.name/my/cssQuery/). Retrieved on 2007-05-11 11:18.


cssQuery is a powerful cross-browser JavaScript function that enables querying of a DOM document using CSS selectors. Example:

// find all anchor elements with "href" equal to "#"
var anchors = cssQuery("a[href='#']");
// find all images contained by the above anchors
var images = cssQuery("img", anchors);


IE7 -- makes IE behave like a standards-compliant browser

/IE7/ (http://dean.edwards.name/IE7/). Retrieved on 2007-05-11 11:18.


IE7 is a JavaScript library to make IE behave like a standards-compliant browser. It fixes many CSS issues and makes transparent PNG work correctly under IE5 and IE6 (learn more).


JavaScript Fading Tooltips

http://www.dustindiaz.com/sweet-titles/ JavaScript Fading Tooltips

sorttable: Make all your tables sortable

http://www.kryogenix.org/code/browser/sorttable/

NiftyCube

http://www.html.it/articoli/niftycube/index.html : Adds rounded corners to your body layout

File uploads: Automatically choose good target filename

Source: MediaWiki

function fillDestFilename() {
    if (!document.getElementById)
        return;
    var path = document.getElementById('wpUploadFile').value;
    // Find trailing part
    var slash = path.lastIndexOf('/');
    var backslash = path.lastIndexOf('\\');
    var fname;
    if (slash == -1 && backslash == -1) {
        fname = path;
    } else if (slash > backslash) {
        fname = path.substring(slash+1, 10000);
    } else {
        fname = path.substring(backslash+1, 10000);
    }

    // Capitalise first letter and replace spaces by underscores
    fname = fname.charAt(0).toUpperCase().concat(fname.substring(1,10000)).replace(/ /g, '_');

    // Output result
    var destFile = document.getElementById('wpDestFile');
    if (destFile)
        destFile.value = fname;
} 

Tips

window.onload ... throws "Not implemented" error in IE

http://p2p.wrox.com/topic.asp?TOPIC_ID=2439

Apparently you have to use an anonymous function, like this:

window.onload = function() {say("Hello!");};

rather than like this:

window.onload = say("Hello!");

Code analyzers/checkers/translators

http://www.crockford.com/javascript/jsmin.html JSMIN, The JavaScript Minifier

[JSLint http://www.jslint.com/] checks for problems in your JavaScript


Menus

Menus with a delay after mouseover before display

http://www.pjhyett.com/posts/206-timeout-your-mouseovers Timeout your Mouseovers

This is a fun effect I built for Chowhound that does one better for mouseovers. The problem with most menu systems is that they’re really touchy whether you’re too fast or slow with the mouse. The trick is to use a timeout with the effect, so it will wait a fraction of a second to pop-up, and a fraction of a second to go away…just enough to make the effect feel solid and not finicky.

<script type="text/javascript">
var RollIt = {
    timeout : null,
    showPopup : function(){
        clearTimeout(this.timeout);
        if($('rollit').style.display == 'none'){
            this.timeout = setTimeout(function(){new Effect.BlindDown('rollit', {duration:.3, fps:40})},400);
        }
    },
    hidePopup : function(){
        if($('rollit').style.display == 'none'){
            clearTimeout(this.timeout);
        }else{
            this.timeout = setTimeout(function(){new Effect.BlindUp('rollit', {duration:.3, fps:40})},300);
        }
    }    
}
</script>

<div style="padding: 10px; background: #fff999;">
<a href="#" onclick="RollIt.hidePopup('rollit')" onmouseout="RollIt.hidePopup('rollit')" onmouseover="RollIt.showPopup('rollit')">Roll over Me</a>
</div>

<div id="rollit" style="display:none; background: #ff9999;" onmouseout="RollIt.hidePopup('rollit')" onmouseover="RollIt.showPopup('rollit')"> 

<h3 style="margin: 0;">Roll It</h3>
By keeping your mouse hovering over this div or over the link that made it appear, it will stay open.

<br/><br/>
But, if you roll away from this div, it will roll back up.
</div>

<div style="background: #fff999; padding: 10px;">
  Here's a bunch of other content
</div>

How to add HTML elements dynamically

http://www.dustindiaz.com/add-and-remove-html-elements-dynamically-with-javascript/

http://www.dustindiaz.com/add-remove-elements-reprise/

http://www.matts411.com/webdev/creating_form_elements_with_javascript

It seems that you can add and remove elements (fields) from a form without problem -- unless the form is around a row of a table.

I wrote an example (not uploaded yet) to illustrate the (weird) problems I had with that. : adding_elements_to_a_form_in_a_table_row.html


How to make static paragraphs (etc.) turn into editable forms by clicking on them

http://www.quirksmode.org/dom/cms.html -- A nice tutorial/demo, focusing on how such a technique could be useful in a CMS.

Improving (Decreasing) JavaScript load time

http://betterexplained.com/articles/speed-up-your-javascript-load-time/ Speed Up Your Javascript Load Time | BetterExplained

http://dean.edwards.name/weblog/2007/03/google-it/


I was looking for a way to speed up the delivery of this site by moving all static content to code.google.com.

http://dean.edwards.name/my/google-js.html

<script type="text/javascript" src="http://deanedwards.googlecode.com/svn/trunk/test.js"></script>

Misc/Articles

What ASP.NET Developers Should Know About JavaScript (http://www.odetocode.com/Articles/473.aspx) (May 08, 2007). Retrieved on 2007-05-11 11:18.

...


Links/Sources

http://dean.edwards.name/weblog/

Ads
Personal tools