cljs: Deleted old duplicate FAQ notes
/trunk/cljs/faq_notes/closures.html |
---|
0,0 → 1,1574 |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" |
"http://www.w3.org/TR/html4/strict.dtd"> |
<html lang="en"> |
<head> |
<title>Javascript Closures</title> |
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> |
<link href="../../faq.css" rel="stylesheet" type="text/css"> |
<link href="../notes.css" rel="stylesheet" type="text/css"> |
<style type="text/css"> |
CODE { white-space:nowrap; } |
.scopeCh { |
white-space:nowrap; |
font-family:Courier, monospace; |
} |
</style> |
</head> |
<body> |
<h1>Javascript Closures</h1> |
<div id="faqNav"> |
<a href="../../">FAQ</a> > <a href="../">FAQ Notes</a> |
</div> |
<ul> |
<li><a href="#clIntro">Introduction</a></li> |
<li><a href="#clResO">The Resolution of Property Names on Objects</a> |
<ul> |
<li><a href="#clResA">Assignment of Values</a></li> |
<li><a href="#clResR">Reading of Values</a></li> |
</ul> |
</li> |
<li><a href="#clIRExSc">Identifier Resolution, Execution Contexts and scope chains</a> |
<ul> |
<li><a href="#clExCon">The Execution Context</a></li> |
<li><a href="#clScCh">scope chains and [[scope]]</a></li> |
<li><a href="#clIdRes">Identifier Resolution</a></li> |
</ul> |
</li> |
<li><a href="#clClose">Closures</a> |
<ul> |
<li><a href="#clAtGb">Automatic Garbage Collection</a></li> |
<li><a href="#clFrmC">Forming Closures</a></li> |
</ul> |
</li> |
<li><a href="#clClDo">What can be done with Closures?</a> |
<ul> |
<li><a href="#clSto">Example 1: setTimeout with Function References</a></li> |
<li><a href="#clObjI">Example 2: Associating Functions with Object Instance Methods</a></li> |
<li><a href="#clEncap">Example 3: Encapsulating Related Functionality</a></li> |
<li><a href="#clOtE">Other Examples</a></li> |
</ul> |
</li> |
<li><a href="#clAc">Accidental Closures</a></li> |
<li><a href="#clMem">The Internet Explorer Memory Leak Problem</a></li> |
</ul> |
<h2 id="clIntro">Introduction</h2> |
<blockquote cite="http://groups.google.com/groups?selm=wu535hos.fsf@hotpop.com"> |
<dl> |
<dt id="clDefN">Closure</dt> |
<dd>A "closure" is an expression (typically a function) that |
can have free variables together with an environment that binds |
those variables (that "closes" the expression). |
<dd> |
</dl> |
</blockquote> |
<p> |
Closures are one of the most powerful features of ECMAScript |
(javascript) but they cannot be property exploited without |
understanding them. They are, however, relatively easy to create, |
even accidentally, and their creation has potentially harmful |
consequences, particularly in some relatively common web browser |
environments. To avoid accidentally encountering the drawbacks and |
to take advantage of the benefits they offer it is necessary to |
understand their mechanism. This depends heavily on the role of |
scope chains in identifier resolution and so on the resolution of |
property names on objects. |
</p> |
<p> |
The simple explanation of a Closure is that ECMAScript allows inner |
functions; function definitions and function expressions that are |
inside the function bodes of other functions. And that those inner |
functions are allowed access to all of the local variables, parameters |
and declared inner functions within their outer function(s). A closure |
is formed when one of those inner functions is made accessible outside |
of the function in which it was contained, so that it may be executed |
after the outer function has returned. At which point it still has |
access to the local variables, parameters and inner function |
declarations of its outer function. Those local variables, parameter |
and function declarations (initially) have the values that they had |
when the outer function returned and may be interacted with by the |
inner function. |
</p> |
<p> |
Unfortunately, properly understanding closures requires an |
understanding of the mechanism behind them, and quite a bit of |
technical detail. While some of the ECMA 262 specified algorithms have |
been brushed over in the early part of the following explanation, much |
cannot be omitted or easily simplified. Individuals familiar with |
object property name resolution may skip that section but only people |
already familiar with closures can afford to skip the following |
sections, and they can stop reading now and get back to exploiting |
them. |
</p> |
<h2 id="clResO">The Resolution of Property Names on Objects</h2> |
<p> |
ECMAScript recognises two categories of object, "Native Object" |
and "Host Object" with a sub-category of native objects called |
"Built-in Object" (ECMA 262 3rd Ed Section 4.3). Native objects |
belong to the language and host objects are provided by the environment, |
and may be, for example, document objects, DOM nodes and the like. |
</p> |
<p> |
Native objects are loose and dynamic bags of named properties (some |
implementations are not that dynamic when it comes to the built in |
object sub-category, though usually that doesn't matter). The defined |
named properties of an object will hold a value, which may be a |
reference to another Object (functions are also Objects in this sense) |
or a primitive value: String, Number, Boolean, Null or Undefined. The |
Undefined primitive type is a bit odd in that it is possible to assign |
a value of Undefined to a property of an object but doing so does not |
remove that property from the object; it remains a defined named |
property, it just holds the value <code>undefined</code>. |
</p> |
<p> |
The following is a simplified description of how property values are |
read and set on objects with the internal details brushed over to the |
greatest extent possible. |
</p> |
<h3><a name="clResA" id="clResA">Assignment of Values</a></h3> |
<p> |
Named properties of objects can be created, or values set on existing |
named properties, by assigning a value to that named property. So |
given:- |
</p> |
<pre> |
var objectRef = new Object(); <span class="commentJS">//create a generic javascript object.</span> |
</pre> |
<p> |
A property with the name "testNumber" can be created as:- |
</p> |
<pre> |
objectRef.testNumber = 5; |
<span class="commentJS">/* - or:- */</span> |
objectRef["testNumber"] = 5; |
</pre> |
<p> |
The object had no "testNumber" property prior to the |
assignment but one is created when the assignment is made. Any |
subsequent assignment does not need to create the property, it just |
re-sets its value:- |
</p> |
<pre> |
objectRef.testNumber = 8; |
<span class="commentJS">/* - or:- */</span> |
objectRef["testNumber"] = 8; |
</pre> |
<p> |
Javascript objects have prototypes that can themselves be objects, as |
will be described shortly, and that prototype may have named |
properties. But this has no role in assignment. If a value is assigned |
and the actual object does not have a property with the corresponding |
name a property of that name is created and the value is assigned to |
it. If it has the property then its value is re-set. |
</p> |
<h3><a name="clResR" id="clResR">Reading of Values</a></h3> |
<p> |
It is in reading values from object properties that prototypes come |
into play. If an object has a property with the property name used in |
the property accessor then the value of that property is returned:- |
</p> |
<pre> |
<span class="commentJS">/* Assign a value to a named property. If the object does not have a |
property with the corresponding name prior to the assignment it |
will have one after it:- |
*/</span> |
objectRef.testNumber = 8; |
<span class="commentJS">/* Read the value back from the property:- */</span> |
var val = objectRef.testNumber; |
<span class="commentJS">/* and - val - now holds the value 8 that was just assigned to the |
named property of the object. */</span> |
</pre> |
<p> |
But all objects may have prototypes, and prototypes are objects so they, in |
turn, may have prototypes, which may have prototypes, and so on forming |
what is called the prototype chain. The prototype chain ends when one |
of the objects in the chain has a null prototype. The default prototype for the |
<code>Object</code> constructor has a null prototype so:- |
</p> |
<pre> |
var objectRef = new Object(); <span class="commentJS">//create a generic javascript object.</span> |
</pre> |
<p> |
Creates an object with the prototype <code>Object.prototype</code> that itself has a |
null prototype. So the prototype chain for <code>objectRef</code> contains only one |
object: <code>Object.prototype</code>. However:- |
</p> |
<pre> |
<span class="commentJS">/* A "constructor" function for creating objects of a - |
MyObject1 - type. |
*/</span> |
function MyObject1(formalParameter){ |
<span class="commentJS">/* Give the constructed object a property called - testNumber - and |
assign it the value passed to the constructor as its first |
argument:- |
*/</span> |
this.testNumber = formalParameter; |
} |
<span class="commentJS">/* A "constructor" function for creating objects of a - |
MyObject2 - type:- |
*/</span> |
function MyObject2(formalParameter){ |
<span class="commentJS">/* Give the constructed object a property called - testString - |
and assign it the value passed to the constructor as its first |
argument:- |
*/</span> |
this.testString = formalParameter; |
} |
<span class="commentJS">/* The next operation replaces the default prototype associated with |
all MyObject2 instances with an instance of MyObject1, passing the |
argument - 8 - to the MyObject1 constructor so that its - |
testNumber - property will be set to that value:- |
*/</span> |
MyObject2.prototype = new MyObject1( 8 ); |
<span class="commentJS">/* Finally, create an instance of - MyObject2 - and assign a reference |
to that object to the variable - objectRef - passing a string as the |
first argument for the constructor:- |
*/</span> |
var objectRef = new MyObject2( "String_Value" ); |
</pre> |
<p> |
The instance of <code>MyObject2</code> referred to by the <code>objectRef</code> variable has a |
prototype chain. The first object in that chain is the instance of |
<code>MyObject1</code> that was created and assigned to the prototype |
property of the <code>MyObject2</code> constructor. The instance of |
<code>MyObject1</code> has a prototype, the object that was assigned to the function |
<code>MyObject1</code>'s prototype property by the implementation. That object has |
a prototype, the default <code>Object</code> prototype that corresponds with the |
object referred to by <code>Object.prototype</code>. <code>Object.prototype</code> has a null |
prototype so the prototype chain comes to an end at this point. |
</p> |
<p> |
When a property accessor attempts to read a named property form the |
object referred to by the variable <code>objectRef</code> the whole |
prototype chain can enter into the process. In the simple case:- |
</p> |
<pre> |
var val = objectRef.testString; |
</pre> |
<p> |
- the instance of <code>MyObject2</code> referred to by <code>objectRef</code> has |
a property with the name "testString" so it is the value of |
that property, set to "String_Value", that is assigned to the |
variable <code>val</code>. However:- |
</p> |
<pre> |
var val = objectRef.testNumber; |
</pre> |
<p> |
- cannot read a named property form the instance of |
<code>MyObject2</code> itself as it has no such property but the |
variable <code>val</code> is set to the value of <code>8</code> rather |
than undefined because having failed to find a corresponding named |
property on the object itself the interpreter then examines the object |
that is its prototype. Its prototype is the instance of |
<code>MyObject1</code> and it was created with a property named |
"testNumber" with the value <code>8</code> assigned to that property, so |
the property accessor evaluates as the value <code>8</code>. Neither |
<code>MyObject1</code> or <code>MyObject2</code> have defined a |
<code>toString</code> method, but if a property accessor attempts to |
read the value of a <code>toString</code> property from |
<code>objectRef</code>:- |
</p> |
<pre> |
var val = objectRef.toString; |
</pre> |
<p> |
- the <code>val</code> variable is assigned a reference to a function. |
That function is the <code>toString</code> property of |
<code>Object.prototype</code> and is returned because the process of |
examining the prototype of <code>objectRef</code>, when |
<code>objectRef</code> turns out not to have a "toString" |
property, is acting on an object, so when that prototype is found to |
lack the property its prototype is examined in turn. Its prototype |
is <code>Object.prototype</code>, which does have a |
<code>toString</code> method so it is a reference to that function |
object that is returned. |
</p> |
<p> |
Finally:- |
</p> |
<pre> |
var val = objectRef.madeUpProperty; |
</pre> |
<p> |
- returns <code>undefined</code>, because as the process of working up the prototype |
chain finds no properties on any of the object with the name |
"madeUpPeoperty" it eventually gets to the prototype of |
<code>Object.prototype</code>, which is null, and the process ends |
returning <code>undefined</code>. |
</p> |
<p> |
The reading of named properties returns the first value found, on the |
object or then from its prototype chain. The assigning of a value to a |
named property on an object will create a property on the object itself |
if no corresponding property already exists. |
</p> |
<p> |
This means that if a value was assigned as |
<code>objectRef.testNumber = 3</code> a "testNumber" property |
will be created on the instance of <code>MyObject2</code> itself, and |
any subsequent attempts to read that value will retrieve that value as |
set on the object. The prototype chain no longer needs to be examined |
to resolve the property accessor, but the instance of |
<code>MyObject1</code> with the value of <code>8</code> assigned to its |
"testNumber" property is unaltered. The assignment to the |
<code>objectRef</code> object masks the corresponding property in its |
prototype chain. |
</p> |
<p> |
Note: ECMAScript defines an internal <code>[[prototype]]</code> |
property of the internal Object type. This property is not directly accessible with |
scripts, but it is the chain of objects referred to with the |
internal <code>[[prototype]]</code> property that is used in property |
accessor resolution; the object's prototype chain. A public |
<code>prototype</code> property exists to allow the assignment, |
definition and manipulation of prototypes in association with the |
internal <code>[[prototype]]</code> property. The details of the |
relationship between to two are described in ECMA 262 (3rd edition) |
and are beyond the <dfn>scope</dfn> of this discussion. |
</p> |
<h2 id="clIRExSc">Identifier Resolution, Execution Contexts and scope chains</h2> |
<h3 id="clExCon">The Execution Context</h3> |
<p> |
An <dfn>execution context</dfn> is an abstract concept used by the ECMSScript |
specification (ECMA 262 3rd edition) to define the behaviour required |
of ECMAScript implementations. The specification does not say anything |
about how <dfn>execution contexts</dfn> should be implemented but execution |
contexts have associated attributes that refer to specification defined |
structures so they might be conceived (and even implemented) as objects |
with properties, though not public properties. |
</p> |
<p> |
All javascript code is executed in an <dfn>execution context</dfn>. Global code |
(code executed inline, normally as a JS file, or <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> page, loads) gets |
executed in <dfn>global</dfn> <dfn>execution context</dfn>, and each invocation of a function (possibly as a constructor) has an associated |
<dfn>execution context</dfn>. Code executed with the <code>eval</code> function |
also gets a distinct execution context but as <code>eval</code> is |
never normally used by javascript programmers it will not be discussed |
here. The specified details of <dfn>execution contexts</dfn> are to be found in |
section 10.2 of ECMA 262 (3rd edition). |
</p> |
<p> |
When a javascript function is called it enters an <dfn>execution context</dfn>, |
if another function is called (or the same function recursively) a new |
<dfn>execution context</dfn> is created and execution enters that context for the |
duration of the function call. Returning to the original execution |
context when that called function returns. Thus running javascript code |
forms a stack of <dfn>execution contexts</dfn>. |
</p> |
<p> |
When an <dfn>execution context</dfn> is created a number of things happen in a |
defined order. First, in the <dfn>execution context</dfn> of a function, an |
"Activation" object is created. The activation object is |
another specification mechanism. It can be considered as an object |
because it ends up having accessible named properties, but it is not a |
normal object as it has no prototype (at least not a defined prototype) |
and it cannot be directly referenced by javascript code. |
</p> |
<p> |
The next step in the creation of the <dfn>execution context</dfn> for a function |
call is the creation of an <code>arguments</code> object, which is an |
array-like object with integer indexed members corresponding with the |
arguments passed to the function call, in order. It also has |
<code>length</code> and <code>callee</code> properties (which are not |
relevant to this discussion, see the spec for details). A property of |
the Activation object is created with the name "arguments" |
and a reference to the <code>arguments</code> object is assigned to |
that property. |
</p> |
<p> |
Next the <dfn>execution context</dfn> is assigned a <dfn>scope</dfn>. A <dfn>scope</dfn> consists of a |
list (or chain) of objects. Each function object has an internal |
<code>[[scope]]</code> property (which we will go into more detail |
about shortly) that also consists of a list (or chain) of objects. |
The <dfn>scope</dfn> that is assigned to the <dfn>execution context</dfn> of a function call |
consists of the list referred to by the <code>[[scope]]</code> property |
of the corresponding function object with the Activation object added |
at the front of the chain (or the top of the list). |
</p> |
<p> |
Then the process of "variable instantiation" takes place using an object |
that ECMA 262 refers to as the "Variable" object. However, |
the Activation object is used as the Variable object (note this, it is |
important: they are the same object). Named properties of the Variable |
object are created for each of the function's formal parameters, and if |
arguments to the function call correspond with those parameters the |
values of those arguments are assigned to the properties (otherwise the |
assigned value is <code>undefined</code>). Inner function definitions |
are used to create function objects which are assigned to properties of |
the Variable object with names that correspond to the function name |
used in the function declaration. The last stage of variable |
instantiation is to create named properties of the Variable object |
that correspond with all the local variables declared within the |
function. |
</p> |
<p> |
The properties created on the Variable object that correspond with |
declared local variables are initially assigned <code>undefined</code> |
values during variable instantiation, the actual initialisation of |
local variables does not happen until the evaluation of the |
corresponding assignment expressions during the execution of the |
function body code. |
</p> |
<p> |
It is the fact that the Activation object, with its |
<code>arguments</code> property, and the Variable object, with named |
properties corresponding with function local variables, are the same |
object, that allows the identifier <code>arguments</code> to be treated |
as if it was a function local variable. |
</p> |
<p> |
Finally a value is assigned for use with the <code>this</code> keyword. |
If the value assigned refers to an object then property accessors |
prefixed with the <code>this</code> keyword reference properties of |
that object. If the value assigned (internally) is null then the |
<code>this</code> keyword will refer to the global object. |
</p> |
<p> |
The global execution context gets some slightly different handling as |
it does not have arguments so it does not need a defined Activation |
object to refer to them. The global execution context does need a <dfn>scope</dfn> |
and its <dfn>scope chain</dfn> consists of exactly one object, the global object. |
The global execution context does go through variable instantiation, |
its inner functions are the normal top level function declarations that |
make up the bulk of javascript code. The global object is used as the |
Variable object, which is why globally declared functions become |
properties of the global object. As do globally declared variables. |
</p> |
<p> |
The global execution context also uses a reference to the global object |
for the <code>this</code> object. |
</p> |
<h3 id="clScCh">scope chains and [[scope]]</h3> |
<p> |
The <dfn>scope chain</dfn> of the execution context for a function call is |
constructed by adding the execution context's Activation/Variable |
object to the front of the <dfn>scope chain</dfn> held in the function |
object's <code>[[scope]]</code> property, so it is important to |
understand how the internal <code>[[scope]]</code> property is |
defined. |
</p> |
<p> |
In ECMAScript functions are objects, they are created during variable |
instantiation from function declarations, during the evaluation of |
function expressions or by invoking the <code>Function</code> |
constructor. |
</p> |
<p> |
Function objects created with the <code>Function</code> constructor |
always have a <code>[[scope]]</code> property referring to a <dfn>scope |
chain</dfn> that only contains the global object. |
</p> |
<p> |
Function objects created with function declarations or function |
expressions have the <dfn>scope chain</dfn> of the execution context in which |
they are created assigned to their internal <code>[[scope]]</code> |
property. |
</p> |
<p> |
In the simplest case of a global function declaration such as:- |
</p> |
<pre> |
function exampleFunction(formalParameter){ |
... <span class="commentJS">// function body code</span> |
} |
</pre> |
<p> |
- the corresponding function object is created during the variable |
instantiation for the global execution context. The global execution |
context has a <dfn>scope chain</dfn> consisting of only the global object. Thus |
the function object that is created and referred to by the property of |
the global object with the name "exampleFunction" is |
assigned an internal <code>[[scope]]</code> property referring to a |
<dfn>scope chain</dfn> containing only the global object. |
</p> |
<p> |
A similar <dfn>scope chain</dfn> is assigned when a function expression is |
executed in the global context:- |
</p> |
<pre> |
var exampleFuncRef = function(){ |
... <span class="commentJS">// function body code</span> |
} |
</pre> |
<p> |
- except in this case a named property of the global object is created |
during variable instantiation for the global execution context but the |
function object is not created, and a reference to it assigned to the |
named property of the global object, until the assignment expression is |
evaluated. But the creation of the function object still happens in the |
global execution context so the <code>[[scope]]</code> property of the |
created function object still only contains the global object in the |
assigned scope chain. |
</p> |
<p> |
Inner function declarations and expressions result in function objects |
being created within the execution context of a function so they get |
more elaborate scope chains. Consider the following code, which defines |
a function with an inner function declaration and then executes the |
outer function:- |
</p> |
<pre> |
function exampleOuterFunction(formalParameter){ |
function exampleInnerFuncitonDec(){ |
... <span class="commentJS">// inner function body</span> |
} |
... <span class="commentJS">// the rest of the outer function body.</span> |
} |
exampleOuterFunction( 5 ); |
</pre> |
<p> |
The function object corresponding with the outer function declaration |
is created during variable instantiation in the global execution context |
so its <code>[[scope]]</code> property contains the one item scope |
chain with only the global object in it. |
</p> |
<p> |
When the global code executes the call to the |
<code>exampleOuterFunction</code> a new execution context is created for |
that function call and an Activation/Variable object along with it. |
The <dfn>scope</dfn> of that new execution context becomes the chain consisting of |
the new Activation object followed by the chain refereed to by the |
outer function object's <code>[[scope]]</code> property (just the |
global object). Variable instantiation for that new execution context |
results in the creation of a function object that corresponds with the |
inner function definition and the <code>[[scope]]</code> property of |
that function object is assigned the value of the <dfn>scope</dfn> from the |
execution context in which it was created. A <dfn>scope chain</dfn> that contains |
the Activation object followed by the global object. |
</p> |
<p> |
So far this is all automatic and controlled by the structure and |
execution of the source code. The <dfn>scope chain</dfn> of the execution context |
defines the <code>[[scope]]</code> properties of the function objects |
created and the <code>[[scope]]</code> properties of the function |
objects define the <dfn>scope</dfn> for their execution contexts (along with the |
corresponding Activation object). But ECMAScript provides the |
<code>with</code> statement as a means of modifying the scope chain. |
</p> |
<p> |
The <code>with</code> statement evaluates an expression and if that |
expression is an object it is added to the <dfn>scope chain</dfn> of the current |
execution context (in front of the Activation/Variable object). The |
<code>with</code> statement then executes another statement (that may |
itself be a block statement) and then restores the execution context's |
<dfn>scope chain</dfn>to what it was before. |
</p> |
<p> |
A function declaration could not be affected by a <code>with</code> |
statement as they result in the creation of function objects during |
variable instantiation, but a function expression can be evaluated |
inside a <code>with</code> statement:- |
</p> |
<pre> |
<span class="commentJS">/* create a global variable - y - that refers to an object:- */</span> |
var y = {x:5}; <span class="commentJS">// object literal with an - x - property</span> |
function exampleFuncWith(){ |
var z; |
<span class="commentJS">/* Add the object referred to by the global variable - y - to the |
front of he scope chain:- |
*/</span> |
with(y){ |
<span class="commentJS">/* evaluate a function expression to create a function object |
and assign a reference to that function object to the local |
variable - z - :- |
*/</span> |
z = function(){ |
... <span class="commentJS">// inner function expression body;</span> |
} |
} |
... |
} |
<span class="commentJS">/* execute the - exampleFuncWith - function:- */</span> |
exampleFuncWith(); |
</pre> |
<p> |
When the <code>exampleFuncWith</code> function is called the resulting |
execution context has a <dfn>scope chain</dfn> consisting of its Activation object |
followed by the global object. The execution of the <code>with</code> |
statement adds the object referred to by the global variable |
<code>y</code> to the front of that <dfn>scope chain</dfn> during the evaluation |
of the function expression. The function object created by the |
evaluation of the function expression is assigned a |
<code>[[scope]]</code> property that corresponds with the <dfn>scope</dfn> of the |
execution context in which it is created. A <dfn>scope chain</dfn> consisting of |
object <code>y</code> followed by the Activation object from the |
execution context of the outer function call, followed by the global |
object. |
</p> |
<p> |
When the block statement associated with the <code>with</code> |
statement terminates the <dfn>scope</dfn> of the execution context is restored |
(the <code>y</code> object is removed), but the function object has |
been created at that point and its <code>[[scope]]</code> property |
assigned a reference to a <dfn>scope chain</dfn> with the <code>y</code> object |
at its head. |
</p> |
<h3><a name="clIdRes" id="clIdRes">Identifier Resolution</a></h3> |
<p> |
Identifiers are resolved against the scope chain. ECMA 262 categorises |
<code>this</code> as a keyword rather than an identifier, which is not |
unreasonable as it is always resolved dependent on the |
<code>this</code> value in the execution context in which it is used, |
without reference to the scope chain. |
</p> |
<p> |
Identifier resolution starts with the first object in the scope chain. |
It is checked to see if it has a property with a name that corresponds |
with the identifier. Because the <dfn>scope chain</dfn> is a chain of objects |
this checking encompasses the prototype chain of that object (if it |
has one). If no corresponding value can be found on the first object |
in the <dfn>scope chain</dfn> the search progresses to the next object. And so on until |
one of the objects in the chain (or one of its prototypes) has a |
property with a name that corresponds with the identifier or the scope |
chain is exhausted. |
</p> |
<p> |
The operation on the identifier happens in the same way as the use of |
property accessors on objects described above. The object identified |
in the <dfn>scope chain</dfn> as having the corresponding property takes the |
place of the object in the property accessor and the identifier acts |
as a property name for that object. The global object is always at the |
end of the scope chain. |
</p> |
<p> |
As execution contexts associated with function calls will have the |
Activation/Variable object at the front of the chain, identifiers used |
in function bodies are effectively first checked to see whether they |
correspond with formal parameters, inner function declaration names or |
local variables. Those would be resolved as named properties of the |
Activation/Variable object. |
</p> |
<h2><a name="clClose" id="clClose">Closures</a></h2> |
<h3><a name="clAtGb" id="clAtGb">Automatic Garbage Collection</a></h3> |
<p> |
ECMAScript uses automatic garbage collection. The specification |
does not define the details, leaving that to the implementers to sort |
out, and some implementations are known to give a very low priority to |
their garbage collection operations. But the general idea is that if an |
object becomes un-referable (by having no remaining references to it |
left accessible to executing code) it becomes available for garbage |
collection and will at some future point be destroyed and any resources |
it is consuming freed and returned to the system for re-use. |
</p> |
<p> |
This would normally be the case upon exiting an execution context. The |
<dfn>scope chain</dfn> structure, the Activation/Variable object and any objects |
created within the execution context, including function objects, would |
no longer be accessible and so would become available for garbage |
collection. |
</p> |
<h3><a name="clFrmC" id="clFrmC">Forming Closures</a></h3> |
<p> |
A closure is formed by returning a function object that was created |
within an execution context of a function call from that function call |
and assigning a reference to that inner function to a property of another |
object. Or by directly assigning a reference to such a function object |
to, for example, a global variable, a property of a globally accessible |
object or an object passed by reference as an argument to the outer |
function call. e.g:- |
</p> |
<pre> |
function exampleClosureForm(arg1, arg2){ |
var localVar = 8; |
function exampleReturned(innerArg){ |
return ((arg1 + arg2)/(innerArg + localVar)); |
} |
<span class="commentJS">/* return a reference to the inner function defined as - |
exampleReturned -:- |
*/</span> |
return exampleReturned; |
} |
var globalVar = exampleClosureForm(2, 4); |
</pre> |
<p> |
Now the function object created within the execution context of the |
call to <code>exampleClosureForm</code> cannot be garbage collected |
because it is referred to by a global variable and is still accessible, |
it can even be executed with <code>globalVar(n)</code>. |
</p> |
<p> |
But something a little more complicated has happened because the |
function object now referred to by <code>globalVar</code> was created |
with a <code>[[scope]]</code> property referring to a scope chain |
containing the Activation/Variable object belonging to the execution |
context in which it was created (and the global object). Now the |
Activation/Variable object cannot be garbage collected either as the |
execution of the function object referred to by <code>globalVar</code> |
will need to add the whole <dfn>scope chain</dfn> from its <code>[[scope]]</code> |
property to the <dfn>scope</dfn> of the execution context created for each call to |
it. |
</p> |
<p> |
A closure is formed. The inner function object has the free variables |
and the Activation/Variable object on the function's <dfn>scope chain</dfn> is |
the environment that binds them. |
</p> |
<p> |
The Activation/Variable object is trapped by being |
referred to in the <dfn>scope chain</dfn> assigned to the internal |
<code>[[scope]]</code> property of the function object now referred to |
by the <code>globalVar</code> variable. The Activation/Variable object |
is preserved along with its state; the values of its properties. Scope |
resolution in the execution context of calls to the inner function will |
resolve identifiers that correspond with named properties of that |
Activation/Variable object as properties of that object. The value of |
those properties can still be read and set even though the execution |
context for which it was created has exited. |
</p> |
<p> |
In the example above that Activation/Variable object has a state that |
represents the values of formal parameters, inner function definitions |
and local variables, at the time when the outer function returned |
(exited its execution context). The <code>arg1</code> property has the |
value <code>2</code>,the <code>arg2</code> property the value |
<code>4</code>, <code>localVar</code> the value <code>8</code> and an |
<code>exampleReturned</code> property that is a reference to the inner |
function object that was returned form the outer function. (We will be |
referring to this Activation/Variable object as "ActOuter1" in later |
discussion, for convenience.) |
</p> |
<p> |
If the <code>exampleClosureForm</code> function was called again as:- |
</p> |
<pre> |
var secondGlobalVar = exampleClosureForm(12, 3); |
</pre> |
<p> |
- a new execution context would be created, along with a new Activation |
object. And a new function object would be returned, with its own |
distinct <code>[[scope]]</code> property referring to a scope chain |
containing the Activation object form this second execution context, |
with <code>arg1</code> being <code>12</code> and <code>arg2</code> |
being <code>3</code>. (We will be referring to this Activation/Variable |
object as "ActOuter2" in later discussion, for convenience.) |
</p> |
<p> |
A second and distinct closure has been formed by the second execution |
of <code>exampleClosureForm</code>. |
</p> |
<p> |
The two function objects created by the execution of |
<code>exampleClosureForm</code> to which references have been assigned |
to the global variable <code>globalVar</code> and |
<code>secondGlobalVar</code> respectively, return the expression |
<code>((arg1 + arg2)/(innerArg + localVar))</code>. Which applies |
various operators to four identifiers. How these identifiers are |
resolved is critical to the use and value of closures. |
</p> |
<p> |
Consider the execution of the function object referred to by |
<code>globalVar</code>, as <code>globalVar(2)</code>. A new execution |
context is created and an Activation object (we will call it |
"ActInner1"), which is added to the head of the scope chain |
referred to the <code>[[scope]]</code> property of the executed |
function object. ActInner1 is given a property named |
<code>innerArg</code>, after its formal parameter and the argument |
value <code>2</code> assigned to it. The <dfn>scope chain</dfn> for this new |
execution context is: <span class="scopeCh">ActInner1-></span> |
<span class="scopeCh">ActOuter1-></span> |
<span class="scopeCh">global object</span>. |
</p> |
<p> |
Identifier resolution is done against the <dfn>scope chain</dfn> so in order |
to return the value of the expression |
<code>((arg1 + arg2)/(innerArg + localVar))</code> the values of the |
identifiers will be determined by looking for properties, with names |
corresponding with the identifiers, on each object in the scope chain |
in turn. |
</p> |
<p> |
The first object in the chain is ActInner1 and it has a property named |
<code>innerArg</code> with the value <code>2</code>. All of the other |
3 identifiers correspond with named properties of ActOuter1; |
<code>arg1</code> is <code>2</code>, <code>arg2</code> is |
<code>4</code> and <code>localVar</code> is <code>8</code>. The |
function call returns <code>((2 + 4)/(2 + 8))</code>. |
</p> |
<p> |
Compare that with the execution of the otherwise identical function |
object referred to by <code>secondGlobalVar</code>, as |
<code>secondGlobalVar(5)</code>. Calling the Activation object for |
this new execution context "ActInner2", the scope chain |
becomes: <span class="scopeCh">ActInner2-></span> |
<span class="scopeCh">ActOuter2-></span> |
<span class="scopeCh">global object</span>. ActInner2 returns |
<code>innerArg</code> as <code>5</code> and ActOuter2 returns |
<code>arg1</code>, <code>arg2</code> and <code>localVar</code> as |
<code>12</code>, <code>3</code> and <code>8</code> respectively. The |
value returned is <code>((12 + 3)/(5 + 8))</code>. |
</p> |
<p> |
Execute <code>secondGlobalVar</code> again and a new Activation object |
will appear at the front of the <dfn>scope chain</dfn> but ActOuter2 will still |
be next object in the chain and the value of its named properties will |
again be used in the resolution of the identifiers <code>arg1</code>, |
<code>arg2</code> and <code>localVar</code>. |
</p> |
<p> |
This is how ECMAScript inner functions gain, and maintain, access to the formal |
parameters, declared inner functions and local variables of the |
execution context in which they were created. And it is how the |
forming of a closure allows such a function object to keep referring |
to those values, reading and writing to them, for as long as it |
continues to exist. The Activation/Variable object from the execution |
context in which the inner function was created remains on the scope |
chain referred to by the function object's <code>[[scope]]</code> |
property, until all references to the inner function are freed and |
the function object is made available for garbage collection (along |
with any now unneeded objects on its scope chain). |
</p> |
<p> |
Inner function may themselves have inner functions, and the inner |
functions returned from the execution of functions to form closures |
may themselves return inner functions and form closures of their own. |
With each nesting the <dfn>scope chain</dfn> gains extra Activation objects |
originating with the execution contexts in which the inner function |
objects were created. The ECMAScript specification requires a scope |
chain to be finite, but imposes no limits on their length. |
Implementations probably do impose some practical limitation but no |
specific magnitude has yet been reported. The potential for nesting |
inner functions seems so far to have exceeded anyone's desire to |
code them. |
</p> |
<h2><a name="clClDo" id="clClDo">What can be done with Closures?</a></h2> |
<p> |
Strangely the answer to that appears to be anything and everything. |
I am told that closures enable ECMAScript to emulate anything, so the |
limitation is the ability to conceive and implement the emulation. That |
is a bit esoteric and it is probably better to start with something a |
little more practical. |
</p> |
<h3><a name="clSto" id="clSto">Example 1: setTimeout with Function References</a></h3> |
<p> |
A common use for a closure is to provide parameters for the execution |
of a function prior to the execution of that function. For example, |
when a function is to be provided as the first argument to the |
<code>setTimout</code> function that is common in web browser |
environments. |
</p> |
<p> |
<code>setTimeout</code> schedules the execution of a function (or a |
string of javascript source code, but not in this context), provided as |
its first argument, after an interval expressed in milliseconds (as its |
second argument). If a piece of code wants to use |
<code>setTimeout</code> it calls the <code>setTimeout</code> function |
and passes a reference to a function object as the first argument and |
the millisecond interval as the second, but a reference to a function |
object cannot provide parameters for the scheduled execution of that |
function. |
</p> |
<p> |
However, code could call another function that returned a reference to |
an inner function object, with that inner function object being passed |
by reference to the <code>setTimeout</code> function. The parameters to |
be used for the execution of the inner function are passed with the |
call to the function that returns it. <code>setTimout</code> executes |
the inner function without passing arguments but that inner function |
can still access the parameters provided by the call to the outer |
function that returned it:- |
</p> |
<pre> |
function callLater(paramA, paramB, paramC){ |
<span class="commentJS">/* Return a reference to an anonymous inner function created |
with a function expression:- |
*/</span> |
return (function(){ |
<span class="commentJS">/* This inner function is to be executed with - setTimeout |
- and when it is executed it can read, and act upon, the |
parameters passed to the outer function:- |
*/</span> |
paramA[paramB] = paramC; |
}); |
} |
... |
<span class="commentJS">/* Call the function that will return a reference to the inner function |
object created in its execution context. Passing the parameters that |
the inner function will use when it is eventually executed as |
arguments to the outer function. The returned reference to the inner |
function object is assigned to a local variable:- |
*/</span> |
var functRef = callLater(elStyle, "display", "none"); |
<span class="commentJS">/* Call the setTimeout function, passing the reference to the inner |
function assigned to the - functRef - variable as the first argument:- |
*/</span> |
hideMenu=setTimeout(functRef, 500); |
</pre> |
<h3><a name="clObjI" id="clObjI">Example 2: Associating Functions with Object Instance Methods</a></h3> |
<p> |
There are many other circumstances when a reference to a function |
object is assigned so that it would be executed at some future time |
where it is useful to provide parameters for the execution of that |
function that would not be easily available at the time of execution |
but cannot be known until the moment of assignment. |
</p> |
<p> |
One example might be a javascript object that is designed to |
encapsulate the interactions with a particular DOM element. It has |
<code>doOnClick</code>, <code>doMouseOver</code> and |
<code>doMouseOut</code> methods and wants to execute those methods |
when the corresponding events are triggered on the DOM element, but |
there may be any number of instances of the javascript object created |
associated with different DOM elements and the individual object |
instances do not know how they will be employed by the code that |
instantiated them. The object instances do not know how to reference |
themselves globally because they do not know which global variables |
(if any) will be assigned references to their instances. |
</p> |
<p> |
So the problem is to execute an event handling function that has an |
association with a particular instance of the javascript object, and |
knows which method of that object to call. |
</p> |
<p> |
The following example uses a small generalised closure based function |
that associates object instances with element event handlers. |
Arranging that the execution of the event handler calls the specified |
method of the object instance, passing the event object and a reference |
to the associated element on to the object method and returning the |
method's return value. |
</p> |
<pre> |
<span class="commentJS">/* A general function that associates an object instance with an event |
handler. The returned inner function is used as the event handler. |
The object instance is passed as the - obj - parameter and the name |
of the method that is to be called on that object is passed as the - |
methodName - (string) parameter. |
*/</span> |
function associateObjWithEvent(obj, methodName){ |
<span class="commentJS">/* The returned inner function is intended to act as an event |
handler for a DOM element:- |
*/</span> |
return (function(e){ |
<span class="commentJS">/* The event object that will have been parsed as the - e - |
parameter on DOM standard browsers is normalised to the IE |
event object if it has not been passed as an argument to the |
event handling inner function:- |
*/</span> |
e = e||window.event; |
<span class="commentJS">/* The event handler calls a method of the object - obj - with |
the name held in the string - methodName - passing the now |
normalised event object and a reference to the element to |
which the event handler has been assigned using the - this - |
(which works because the inner function is executed as a |
method of that element because it has been assigned as an |
event handler):- |
*/</span> |
return obj[methodName](e, this); |
}); |
} |
<span class="commentJS">/* This constructor function creates objects that associates themselves |
with DOM elements whose IDs are passed to the constructor as a |
string. The object instances want to arrange than when the |
corresponding element triggers onclick, onmouseover and onmouseout |
events corresponding methods are called on their object instance. |
*/</span> |
function DhtmlObject(elementId){ |
<span class="commentJS">/* A function is called that retrieves a reference to the DOM |
element (or null if it cannot be found) with the ID of the |
required element passed as its argument. The returned value |
is assigned to the local variable - el -:- |
*/</span> |
var el = getElementWithId(elementId); |
<span class="commentJS">/* The value of - el - is internally type-converted to boolean for |
the - if - statement so that if it refers to an object the |
result will be true, and if it is null the result false. So that |
the following block is only executed if the - el - variable |
refers to a DOM element:- |
*/</span> |
if(el){ |
<span class="commentJS">/* To assign a function as the element's event handler this |
object calls the - associateObjWithEvent - function |
specifying itself (with the - this - keyword) as the object |
on which a method is to be called and providing the name of |
the method that is to be called. The - associateObjWithEvent |
- function will return a reference to an inner function that |
is assigned to the event handler of the DOM element. That |
inner function will call the required method on the |
javascript object when it is executed in response to |
events:- |
*/</span> |
el.onclick = associateObjWithEvent(this, "doOnClick"); |
el.onmouseover = associateObjWithEvent(this, "doMouseOver"); |
el.onmouseout = associateObjWithEvent(this, "doMouseOut"); |
... |
} |
} |
DhtmlObject.prototype.doOnClick = function(event, element){ |
... <span class="commentJS">// doOnClick method body</span>. |
} |
DhtmlObject.prototype.doMouseOver = function(event, element){ |
... <span class="commentJS">// doMouseOver method body.</span> |
} |
DhtmlObject.prototype.doMouseOut = function(event, element){ |
... <span class="commentJS">// doMouseOut method body.</span> |
} |
</pre> |
<p> |
And so any instances of the <code>DhtmlObject</code> can associate themselves |
with the DOM element that they are interested in without any need |
to know anything about how they are being employed by other code, |
impacting on the global namespace or risking clashes with other |
instances of the <code>DhtmlObject</code>. |
</p> |
<h3><a name="clEncap" id="clEncap">Example 3: Encapsulating Related Functionality</a></h3> |
<p> |
Closures can be used to create additional scopes that can be used to |
group interrelated and dependent code in a way that minimises the risk |
of accidental interaction. Suppose a function is to build a string and |
to avoid the repeated concatenation operations (and the creation of |
numerous intermediate strings) the desire is to use an array to store |
the parts of the string in sequence and then output the results using |
the <code>Array.prototype.join</code> method (with an empty string as its argument). |
The array is going to act as a buffer for the output, but defining it |
locally to the function will result in its re-creation on each |
execution of the function, which may not be necessary if the only |
variable content of that array will be re-assigned on each function |
call. |
</p> |
<p> |
One approach might make the array a global variable so that it can be |
re-used without being re-created. But the consequences of that will be |
that, in addition to the global variable that refers to the function |
that will use the buffer array, there will be a second global property |
that refers to the array itself. The effect is to render the code less |
manageable, as, if it is to be used elsewhere, its author has to remember |
to include both the function definition and the array definition. It |
also makes the code less easy to integrate with other code because |
instead of just ensuring that the function name is unique within the |
global namespace it is necessary to ensure that the Array on which it |
is dependent is using a name that is unique within the global |
namespace. |
</p> |
<p> |
A Closure allows the buffer array to be associated (and neatly |
packaged) with the function that is dependent upon it and |
simultaneously keep the property name to which the buffer array as |
assigned out of the global namespace and free of the risk of name |
conflicts and accidental interactions. |
</p> |
<p> |
The trick here is to create one additional execution context by |
executing a function expression in-line and have that function |
expression return an inner function that will be the function that is |
used by external code. The buffer array is then defined as a local |
variable of the function expression that is executed in-line. That only |
happens once so the Array is only created once, but is available to |
the function that depends on it for repeated use. |
</p> |
<p> |
The following code creates a function that will return a string of |
<span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span>, much of which is constant, but those constant character |
sequences need to be interspersed with variable information provided |
as parameter to the function call. |
</p> |
<p> |
A reference to an inner function object is returned from the in-line |
execution of a function expression and assigned to a global variable |
so that it can be called as a global function. The buffer array is |
defined as a local variable in the outer function expression. It is |
not exposed in the global namespace and does not need to be re-created |
whenever the function that uses it is called. |
</p> |
<pre> |
<span class="commentJS">/* A global variable - getImgInPositionedDivHtml - is declared and |
assigned the value of an inner function expression returned from |
a one-time call to an outer function expression. |
That inner function returns a string of <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> that represents an |
absolutely positioned DIV wrapped round an IMG element, such that |
all of the variable attribute values are provided as parameters |
to the function call:- |
*/</span> |
var getImgInPositionedDivHtml = (function(){ |
<span class="commentJS">/* The - buffAr - Array is assigned to a local variable of the |
outer function expression. It is only created once and that one |
instance of the array is available to the inner function so that |
it can be used on each execution of that inner function. |
Empty strings are used as placeholders for the date that is to |
be inserted into the Array by the inner function:- |
*/</span> |
var buffAr = [ |
'<div id="', |
'', <span class="commentJS">//index 1, DIV ID attribute</span> |
'" style="position:absolute;top:', |
'', <span class="commentJS">//index 3, DIV top position</span> |
'px;left:', |
'', <span class="commentJS">//index 5, DIV left position</span> |
'px;width:', |
'', <span class="commentJS">//index 7, DIV width</span> |
'px;height:', |
'', <span class="commentJS">//index 9, DIV height</span> |
'px;overflow:hidden;\"><img src=\"', |
'', <span class="commentJS">//index 11, IMG URL</span> |
'\" width=\"', |
'', <span class="commentJS">//index 13, IMG width</span> |
'\" height=\"', |
'', <span class="commentJS">//index 15, IMG height</span> |
'\" alt=\"', |
'', <span class="commentJS">//index 17, IMG alt text</span> |
'\"><\/div>' |
]; |
<span class="commentJS">/* Return the inner function object that is the result of the |
evaluation of a function expression. It is this inner function |
object that will be executed on each call to - |
getImgInPositionedDivHtml( ... ) -:- |
*/</span> |
return (function(url, id, width, height, top, left, altText){ |
<span class="commentJS">/* Assign the various parameters to the corresponding |
locations in the buffer array:- |
*/</span> |
buffAr[1] = id; |
buffAr[3] = top; |
buffAr[5] = left; |
buffAr[13] = (buffAr[7] = width); |
buffAr[15] = (buffAr[9] = height); |
buffAr[11] = url; |
buffAr[17] = altText; |
<span class="commentJS">/* Return the string created by joining each element in the |
array using an empty string (which is the same as just |
joining the elements together):- |
*/</span> |
return buffAr.join(''); |
}); <span class="commentJS">//:End of inner function expression.</span> |
})(); |
<span class="commentJS">/*^^- :The inline execution of the outer function expression. */</span> |
</pre> |
<p> |
If one function was dependent on one (or several) other functions, but |
those other functions were not expected to be directly employed by any |
other code, then the same technique could be used to group those |
functions with the one that was to be publicly exposed. Making a |
complex multi-function process into an easily portable and encapsulated |
unit of code. |
</p> |
<h3><a name="clOtE" id="clOtE">Other Examples</a></h3> |
<p> |
Probably one of the best known applications of closures is |
<a href="http://www.crockford.com/javascript/private.html">Douglas |
Crockford's technique for the emulation of private instance variables |
in ECMAScript objects</a>. Which can be extended to all sorts of |
structures of <dfn>scope</dfn> contained nested accessibility/visibility, including |
<a href="http://myweb.tiscali.co.uk/cornford/js_info/private_static.html"> |
the emulation of private static members for ECMAScript objects</a>. |
</p> |
<p> |
The possible application of closures are endless, understanding how |
they work is probably the best guide to realising how they can be |
used. |
</p> |
<h2 id="clAc">Accidental Closures</h2> |
<p> |
Rendering any inner function accessible outside of the body of the |
function in which it was created will form a closure. That makes |
closures very easy to create and one of the consequences is that |
javascript authors who do not appreciate closures as a language feature |
can observe the use of inner functions for various tasks and employ |
inner functions, with no apparent consequences, not realising that |
closures are being created or what the implications of doing that are. |
</p> |
<p> |
Accidentally creating closures can have harmful side effects as the |
following section on the IE memory leak problem describes, but they can |
also impact of the efficiency of code. It is not the closures |
themselves, indeed carefully used they can contribute significantly |
towards the creation of efficient code. It is the use of inner |
functions that can impact on efficiency. |
</p> |
<p> |
A common situation is where inner functions are used is as event |
handlers for DOM elements. For example the following code might be used |
to add an onclick handler to a link element:- |
</p> |
<pre> |
<span class="commentJS">/* Define the global variable that is to have its value added to the |
- href - of a link as a query string by the following function:- |
*/</span> |
var quantaty = 5; |
<span class="commentJS">/* When a link passed to this function (as the argument to the function |
call - linkRef -) an onclick event handler is added to the link that |
will add the value of a global variable - quantaty - to the - href - |
of that link as a query string, then return true so that the link |
will navigate to the resource specified by the - href - which will |
by then include the assigned query string:- |
*/</span> |
function addGlobalQueryOnClick(linkRef){ |
<span class="commentJS">/* If the - linkRef - parameter can be type converted to true |
(which it will if it refers to an object):- |
*/</span> |
if(linkRef){ |
<span class="commentJS">/* Evaluate a function expression and assign a reference to the |
function object that is created by the evaluation of the |
function expression to the onclick handler of the link |
element:- |
*/</span> |
linkRef.onclick = function(){ |
<span class="commentJS">/* This inner function expression adds the query string to |
the - href - of the element to which it is attached as |
an event handler:- |
*/</span> |
this.href += ('?quantaty='+escape(quantaty)); |
return true; |
}; |
} |
} |
</pre> |
<p> |
Whenever the <code>addGlobalQueryOnClick</code> function is called a |
new inner function is created (and a closure formed by its assignment). |
From the efficiency point of view that would not be significant if the |
<code>addGlobalQueryOnClick</code> function was only called once or |
twice, but if the function was heavily employed many distinct function |
objects would be created (one for each evaluation of the inner function |
expression). |
</p> |
<p> |
The above code is not taking advantage of the fact that inner functions |
are becoming accessible outside of the function in which they are being |
created (or the resulting closures). As a result exactly the same effect |
could be achieved by defining the function that is to be used as the |
event handler separately and then assigning a reference to that |
function to the event handling property. Only one function object would |
be created and all of the elements that use that event handler would |
share a reference to that one function:- |
</p> |
<pre> |
<span class="commentJS">/* Define the global variable that is to have its value added to the |
- href - of a link as a query string by the following function:- |
*/</span> |
var quantaty = 5; |
<span class="commentJS">/* When a link passed to this function (as the argument to the function |
call - linkRef -) an onclick event handler is added to the link that |
will add the value of a global variable - quantaty - to the - href - |
of that link as a query string, then return true so that the link |
will navigate to the resource specified by the - href - which will |
by then include the assigned query string:- |
*/</span> |
function addGlobalQueryOnClick(linkRef){ |
<span class="commentJS">/* If the - linkRef - parameter can be type converted to true |
(which it will if it refers to an object):- |
*/</span> |
if(linkRef){ |
<span class="commentJS">/* Assign a reference to a global function to the event |
handling property of the link so that it becomes the |
element's event handler:- |
*/</span> |
linkRef.onclick = forAddQueryOnClick; |
} |
} |
<span class="commentJS">/* A global function declaration for a function that is intended to act |
as an event handler for a link element, adding the value of a global |
variable to the - href - of an element as an event handler:- |
*/</span> |
function forAddQueryOnClick(){ |
this.href += ('?quantaty='+escape(quantaty)); |
return true; |
} |
</pre> |
<p> |
As the inner function in the first version is not being used to exploit |
the closures produced by its use, it would be more efficient not to use |
an inner function, and thus not repeat the process of creating many |
essentially identical function objects. |
</p> |
<p> |
A similar consideration applies to object constructor functions. It is |
not uncommon to see code similar to the following skeleton constructor:- |
</p> |
<pre> |
function ExampleConst(param){ |
<span class="commentJS">/* Create methods of the object by evaluating function expressions |
and assigning references to the resulting function objects |
to the properties of the object being created:- |
*/</span> |
this.method1 = function(){ |
... <span class="commentJS">// method body.</span> |
}; |
this.method2 = function(){ |
... <span class="commentJS">// method body.</span> |
}; |
this.method3 = function(){ |
... <span class="commentJS">// method body.</span> |
}; |
<span class="commentJS">/* Assign the constructor's parameter to a property of the object:- |
*/</span> |
this.publicProp = param; |
} |
</pre> |
<p> |
Each time the constructor is used to create an object, with |
<code>new ExampleConst(n)</code>, a new set of function objects are |
created to act as its methods. So the more object instances that are |
created the more function objects are created to go with them. |
</p> |
<p> |
Douglas Crockford's technique for emulating private members on |
javascript objects exploits the closure resulting form assigning |
references to inner function objects to the public properties of a |
constructed object from within its constructor. But if the methods of |
an object are not taking advantage of the closure that they will form |
within the constructor the creation of multiple function objects for |
each object instantiation will make the instantiation process slower |
and more resources will be consumed to accommodate the extra function |
objects created. |
</p> |
<p> |
In that case it would be more efficient to create the function object |
once and assign references to them to the corresponding properties of |
the constructor's <code>prototype</code> so they may be shared by all |
of the objects created with that constructor:- |
</p> |
<pre> |
function ExampleConst(param){ |
<span class="commentJS">/* Assign the constructor's parameter to a property of the object:- |
*/</span> |
this.publicProp = param; |
} |
<span class="commentJS">/* Create methods for the objects by evaluating function expressions |
and assigning references to the resulting function objects to the |
properties of the constructor's prototype:- |
*/</span> |
ExampleConst.prototype.method1 = function(){ |
... <span class="commentJS">// method body.</span> |
}; |
ExampleConst.prototype.method2 = function(){ |
... <span class="commentJS">// method body.</span> |
}; |
ExampleConst.prototype.method3 = function(){ |
... <span class="commentJS">// method body.</span> |
}; |
</pre> |
<h2><a name="clMem" id="clMem">The Internet Explorer Memory Leak Problem</a></h2> |
<p> |
The Internet Explorer web browser (verified on versions 4 to 6 (6 is |
current at the time of writing)) has a fault in its garbage collection |
system that prevents it from garbage collecting ECMAScript and some |
host objects if those host objects form part of a "circular" |
reference. The host objects in question are any DOM Nodes (including |
the document object and its descendants) and ActiveX objects. If a |
circular reference is formed including one or more of them, then |
none of the objects involved will be freed until the browser is closed |
down, and the memory that they consume will be unavailable to the |
system until that happens. |
</p> |
<p> |
A circular reference is when two or more objects refer to each other in |
a way that can be followed and lead back to the starting point. Such |
as object 1 has a property that refers to object 2, object 2 has a |
property that refers to object 3 and object 3 has a property that |
refers back to object 1. With pure ECMAScript objects as soon as no |
other objects refer to any of objects 1, 2 or 3 the fact that they only |
refer to each other is recognised and they are made available for |
garbage collection. But on Internet Explorer, if any of those objects |
happen to be a DOM Node or ActiveX object, the garbage collection |
cannot see that the circular relationship between them is isolated |
from the rest of the system and free them. Instead they all stay in |
memory until the browser is closed. |
</p> |
<p> |
Closures are extremely good at forming circular references. If a |
function object that forms a closure is assigned as, for example, and |
event handler on a DOM Node, and a reference to that Node is assigned |
to one of the Activation/Variable objects in its <dfn>scope chain</dfn> then a |
circular reference exists. |
<span class="scopeCh">DOM_Node.onevent -></span> |
<span class="scopeCh">function_object.[[scope]] -></span> |
<span class="scopeCh">scope_chain -></span> |
<span class="scopeCh">Activation_object.nodeRef -></span> |
<span class="scopeCh">DOM_Node</span>. |
It is very easy to do, and a bit of browsing around a site that forms |
such a reference in a piece of code common to each page can consume |
most of the systems memory (possibly all). |
</p> |
<p> |
Care can be taken to avoid forming circular references and remedial |
action can be taken when they cannot otherwise be avoided, such as |
using IE's onunload event to null event handling function |
references. Recognising the problem and understanding closures |
(and their mechanism) is the key to avoiding this problem with IE. |
</p> |
<p id="rToc"> |
<a href="faq_notes.html#toc">comp.lang.javascript FAQ notes T.O.C.</a> |
</p> |
<ul style="list-style-type:none;margin-top:2.5em;"> |
<li>Written by <span class="person">Richard Cornford</span>. March 2004.</li> |
<li>With corrections and suggestions by:-<br> |
<ul style="list-style-type:none;"> |
<li><span class="person">Martin Honnen</span>.</li> |
<li><span class="person">Yann-Erwan Perio (Yep)</span>.</li> |
<li><span class="person">Lasse Reichstein Nielsen</span>. (<a href="#clDefN">definition of closure</a>)</li> |
<li><span class="person">Mike Scirocco</span>.</li> |
<li><span class="person">Dr John Stockton</span>.</li> |
<li><span class="person">Garrett Smith</span>.</li> |
</ul> |
</li> |
</ul> |
</body> |
</html> |
Property changes: |
Added: svn:mime-type |
## -0,0 +1 ## |
+text/plain |
\ No newline at end of property |
Index: cljs/faq_notes/clj_posts.html |
=================================================================== |
--- cljs/faq_notes/clj_posts.html (nonexistent) |
+++ cljs/faq_notes/clj_posts.html (revision 43) |
@@ -0,0 +1,1209 @@ |
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" |
+"http://www.w3.org/TR/html4/strict.dtd"> |
+<html lang="en"> |
+<head> |
+<title>Notes on the comp.lang.javascript FAQ</title> |
+<meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> |
+<link href="../../faq.css" rel="stylesheet" type="text/css"> |
+<link href="../notes.css" rel="stylesheet" type="text/css"> |
+<meta name="ROBOTS" content="NOINDEX, NOFOLLOW"> |
+<style type="text/css"> |
+.resourceList LI { |
+ margin-bottom: 0.8em; |
+} |
+</style> |
+</head> |
+<body> |
+ |
+<h1>Posting Questions and Replies to comp.lang.javascript</h1> |
+<div id="faqNav"> |
+ <a href="../../">FAQ</a> > <a href="../">FAQ Notes</a> |
+ |
+</div> |
+<ul> |
+ <li><a href="#ps1Intro">Introduction</a></li> |
+ <li><a href="#ps1Lang">Posting Language</a></li> |
+ <li><a href="#ps1OToc">On and Off Topic Posting</a></li> |
+ <li><a href="#ps1Txt">Plain-Text Only</a></li> |
+ <li><a href="#ps1Post">Interleaved Posting, Bottom Posting and Not Top Posting.</a> |
+ <ul> |
+ <li><a href="#ps1InBPost">Interleaved Posting, Bottom Posting</a></li> |
+ <li><a href="#ps1TopPs">Top Posting (don't)</a></li> |
+ </ul> |
+ </li> |
+ <li><a href="#ps1Trim">What to Trim</a></li> |
+ <li><a href="#ps1Marg">Margins and Line Wrapping</a> |
+ <ul> |
+ <li><a href="#ps1Mar">Margins</a></li> |
+ <li><a href="#ps1Lw">Line Wrapping</a></li> |
+ <li><a href="#ps1LwQu">Line Wrapping in Quoted Material</a></li> |
+ </ul> |
+ </li> |
+ <li><a href="#ps1Code">General Code Postings, and when to use a URL</a></li> |
+ <!-- <li><a href="#ps1Proof">Proof-read your message</a></li> --> |
+ <li><a href="#ps1Quest">Well Asked Questions Get the Best Answers</a> |
+ <ul> |
+ <li><a href="#ps1QSub">Appropriate Use of the Subject Header</a></li> |
+ <li><a href="#ps1QRes">Doing Your Own Research</a></li> |
+ <li><a href="#ps1DontWork">"It doesn't work"</a></li> |
+ <li><a href="#ps1CntX">Explain the Whole Context</a></li> |
+ <li><a href="#ps1PR">Proof-Read your Questions</a></li> |
+ </ul> |
+ </li> |
+ <!-- <li><a href="#ps1notHD">comp.lang.javascript is Not a Helpdesk</a></li> --> |
+ <li><a href="#ps1AddR">Additional Reading</a></li> |
+</ul> |
+ |
+<h2 id="ps1Intro">Introduction</h2> |
+<h3>Social Behavior</h3> |
+ |
+<p> |
+There may be several reasons for making posts to comp.lang.javascirpt |
+but all valid reasons would be intended to elicit responses, preferably |
+including responses form the many experienced and knowledgeable |
+regular contributors to the group. The direction of communication is |
+always one-to-many, which places the onus on the composer of a message |
+to consider the many in their audience above any personal preferences. |
+It is always in the best interest |
+of someone posting to the group to recognise that the people whose |
+responses will be of most value to them may have an attitude toward |
+their behaviour on the group, and to try to ensure that it will not be |
+a bad attitude. |
+</p> |
+ |
+<p> |
+It is also always in the best interest of any poster to the group to do |
+everything within their power to behave in a way that makes it quick |
+and easy for the people they expect to answer their questions to read and follow |
+their posts, understand their questions and problems and comprehend and |
+test posted code. The people with the best answers are the most likely |
+to be busy; too busy to be interested in unraveling a badly expressed |
+problem from a mass of incomprehensibly formatted code amid a |
+conversation that is hard to follow. |
+</p> |
+ |
+<p id="ps1Into_3"> |
+Usenet has been around for a long time now and has developed various |
+conventions of its own. Conventions that have evolved to make |
+communicating in the medium as easy and efficient as possible. They |
+are not necessarily adhered to on all groups but where they are |
+recognised they are definitely preferred. And comp.lang.javascript is |
+a group where most of the regulars recognise many Usenet conventions |
+and so the FAQ outlines them. This document is intended to provide |
+additional detail on the subject. |
+</p> |
+ |
+<p id="ps1Into_4"> |
+Following those conventions and additionally posting with a |
+consideration of the other points made on this page related more |
+specifically to posting in comp.lang.javascript, will maximise the |
+potential for any questions asked and posts made to elicit a useful |
+response. At least in part because they make it quicker and easier for |
+those interested in offering help to do so. |
+</p> |
+ |
+<h2 id="ps1Lang">Posting Language</h2> |
+ |
+<p id="ps1Lang_1"> |
+comp.lang.javascript is the international javascript group. There are |
+language specific javascript groups that could be expected to be |
+carried by news servers within the countries concerned. There is, |
+however, no English language specific javascript group so |
+comp.lang.javascript serves that audience. As a result the vast |
+majority of the conversation within the group is in English, and |
+anyone with a javascript interest but incapable of reading/writing any |
+language but English would choose comp.lang.javascript to read and post |
+in. But there are no rules that say that English is the only language |
+that is to be used. |
+</p> |
+ |
+<p id="ps1Lang_2"> |
+As an international group, comp.lang.javascript has contributors form |
+around the world, many of whom speak/read/write English as a second or |
+third (+) language. So a post made in any other language will stand a |
+chance of falling within the linguistic capabilities of someone. But |
+posts in English should be understandable to everyone, including those |
+for whom English is their only language, and thus receive the most |
+attention. |
+</p> |
+ |
+<p id="ps1Lang_3"> |
+Machine translation into English (and sometimes less skilled human |
+translation) sometimes does not produce results that can be |
+understood/followed by English speakers. When a bad translation into |
+English is the only option it might be better for a poster to precede |
+it with a version in their native language. (Faced with a post in a |
+language that is not understood it is natural to scroll down to see |
+if there is any accompanying javascript code that might explain the |
+problem; a following English translation would be discovered along |
+the way). A reader of the group who understands the language used |
+may be able to contribute improvements to the English translation even |
+if they cannot directly address the question raised. |
+</p> |
+ |
+<p id="ps1Lang_4"> |
+However, even though the most common language used in postings to |
+comp.lang.javascript is English, the fact that the group is |
+international and that English should not be expected to be the first |
+language of contributors to the group means that the English used |
+should be formally correct (to the best of the posters ability). |
+Normal English sentence structure should be observed, particularly in |
+terms of capitalisation (which serves to aid reading for everyone). But |
+above all, shorthand abbreviations should not be used, no matter how |
+common they may be in English speaking cultures, as they would not |
+normally be part of the teaching of English as a foreign language. This |
+applies especially to text-message shorthand as they are very much a |
+product of local culture and circumstances in a way that is not |
+relevant to Usenet as a medium or appropriate to an international |
+newsgroup. |
+</p> |
+ |
+<p id="ps1Lang_5"> |
+In the context of an international forum it is also probably best to |
+avoid references that may be ambiguous outside of a national context. |
+Date formats are an obvious example, with the ISO 8601 <code>YYYY-MM-DD</code> format |
+being more appropriate than any preferred local form. Also, references |
+to national bodies by acronym alone will not necessarily convey |
+sufficient meaning to an international audience. |
+</p> |
+ |
+<h2 id="ps1OToc">On and Off Topic Posting</h2> |
+ |
+<p id="ps1OToc_1"> |
+ECMA 262 is the formal specification for ECMAScript, the |
+standard adopted by javascript implementations and thus the |
+specification for javascript. |
+</p> |
+ |
+<h3>ECMAScript and Browsers</h3> |
+<p id="ps1OToc_2"> |
+As comp.lang.javascript deals with ECMAScript and ECMAScript was |
+designed as a general scripting language for scripting any object |
+model, questions relating to any application of ECMAScript are |
+appropriate. The group has a general bias towards the use of |
+ECMAScript in web browsers in an Internet context, and questions |
+asked without additional qualification will tend to be assumed to |
+be related to that environment and context. As a result it is a |
+very good idea for questions asked that are not related to that |
+default context; Intranet, other hosts, OS automation, etc., to |
+include details of that context. |
+</p> |
+ |
+<h3 id="ps1OToc_3">Javascript (not Java)</h3> |
+<p> |
+Because of the name "JavaScript" being applied to the |
+language early in its existence there is often confusion between |
+javascript and the Java programming language. The two are distinct |
+and very different languages. Questions relating to Java programming, |
+Java Applets, Java Server Pages (JSP), etc., would be better asked in |
+comp.lang.java.* groups. Only the use of ECMAScript to interact with, |
+say, Java Applets would be on topic, and then the questions should |
+relate to the ECMAScript aspect of the problem. |
+</p> |
+ |
+<h3 id="ps1OToc_4">Other Languages</h3> |
+<p> |
+Questions relating exclusively to other scripting languages, |
+ mark-up languages ((x)<span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span>) and style sheets |
+(<span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span>, XSL) are off topic and should be addressed to more |
+appropriate newsgroups. |
+</p> |
+ |
+<h3 id="ps1OToc_5">The FAQ</h3> |
+<p> |
+The comp.lang.javascript newsgroup may also validly become its own |
+subject, particularly the content and maintenance of the FAQ resources. |
+But as a subject that is most appropriately raised and discussed by |
+the individuals who invest their time in the group rather than |
+passers-by. |
+</p> |
+ |
+<h3 id="ps1OToc_6">Usenet Behavior (don't)</h3> |
+<p> |
+Usenet, and particularly appropriate behaviour on and for Usenet, is |
+also often raised. This is largely unwelcome but inevitable. Hopefully |
+this document should contribute to a reduction in that noise by stating |
+the group's attitude towards Usenet postings in greater detail than can |
+be accommodated in the FAQ proper. |
+</p> |
+ |
+<h3 id="ps1OToc_7">Spam (don't)</h3> |
+<p> |
+Other things that are off topic for the group include obvious things |
+like pyramid and get rich quick schemes, commercial advertising (with |
+some rare exceptions mentioned below), job adverts, spurious |
+invitations to visit web sites and anything else that might be |
+reasonably regarded as spam. |
+</p> |
+ |
+<h3>Announcements</h3> |
+<p id="ps1OToc_8"> |
+Announcements of product releases and events of particular relevance |
+to javascript are welcome but, for products no more often than once |
+pre major release, and for events preferably only once and certainly |
+not more often than at two month intervals leading up to the event. |
+Be aware that product announcements (particularly commercial |
+javascript) are likely to attract reviews, which should not be |
+expected to be uncritical. |
+</p> |
+ |
+<h2 id="ps1Txt">Plain-Text Only</h2> |
+ |
+<p id="ps1Txt_1"> |
+Messages are posted in plain-text. There is no requirement for Usenet |
+newsreader software to recognise, support or display any other |
+content type, such as <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span>, and it is felt that messages posted to |
+comp.lang.javascript should be available to any and all newsreader |
+software. Any form of attachments are also ruled out. |
+</p> |
+ |
+<p id="ps1Txt_2"> |
+It has also been observed that while most of the world may be happy |
+to use the newsreader bundled with their operating system, experts in |
+various aspects of computing will often go out and acquire software |
+that they believe superior for the task they have for it. Meaning that |
+the people best equipped to offer help in the group are also the people |
+least likely to be using the newsreader that you are familiar with. |
+The expectation should be that others are using software that conforms |
+to the applicable standards and not that they are using software with |
+any particular set of additional or "enhanced" features, no |
+matter how common they may seem. Plain-text posts will be readable by |
+everyone, other content types may be subject to more comment on the |
+inappropriateness of the content type than answers intended to address |
+the question raised. |
+</p> |
+ |
+<h2 id="ps1Post">Interleaved Posting, Bottom Posting and Not Top Posting</h2> |
+ |
+<h3 id="ps1InBPost">Interleaved Posting, Bottom Posting</h3> |
+ |
+<p id="ps1InBPost_1"> |
+The posting style in messages intended as responses to other messages |
+is an area where a long established pattern of behaviour has been |
+recognised as most appropriate for the medium and become an established |
+convention. |
+</p> |
+ |
+<p id="ps1InBPost_2"> |
+Material quoted from the previous message is attributed to its author |
+and indented with a marker character (usually >). It is trimmed down |
+to just sufficient to provide the context in which the response is made |
+(marking the fact that an edit has been made with one of several |
+common notations, such as: <snip>, [snip], [ ... ], etc.) and |
+the responding text is placed <em>below</em> the quoted material |
+to which it is responding, separated from it by a blank line. |
+</p> |
+ |
+<p id="ps1InBPost_3"> |
+If the response addresses several points from the previous message |
+then the parts of the quoted text providing the context for each |
+point are separated by the responses to each point. Producing an |
+interleaved post. If the quoted material is in one block then the |
+response text goes after it at the bottom. Producing a bottom post. |
+</p> |
+ |
+<p id="ps1InBPost_4"> |
+It is not possible to distinguish between bottom posting and an |
+interleaved post that is only responding to one point in the previous |
+message. Personally, I prefer interleaved responses but neither will |
+result in an adverse reaction. The important points are that quoted |
+material should be trimmed to the minimum that provides sufficient |
+context and that responses should follow that quoted material. |
+The conversation, and its chronological sequence, flows in the normal |
+direction of reading; top to bottom. |
+</p> |
+ |
+<p id="ps1InBPost_5"> |
+Failing to quote any of the preceding message is not necessarily |
+wrong, so long as the text posted makes sense without the preceding |
+message, for example, by summarising the points being responded to. |
+But generally it is easier to provide the context for a response with |
+a quoted snippet. But responses need to have a context. |
+</p> |
+ |
+<h3 id="ps1TopPs">Top Posting (don't)</h3> |
+ |
+<p id="ps1TopPs_1"> |
+Top posting is placing the response above the quoted material that is |
+being responded to. The chronological sequence of conversation becomes |
+the reverse of the normal top to bottom reading sequence and, without |
+additional text summarising the points being responded to, it is |
+difficult to determine exactly which points such a reply relates to. |
+</p> |
+ |
+<p id="ps1TopPs_2"> |
+The worst possible style of response posting is top posting over a full |
+verbatim quote of the previous message, but even placing a response |
+above a trimmed quote is wrong. It renders the conversational aspects |
+of a response backwards, requiring people to scroll up and down to |
+reconstruct the context of the response and generally making it hard |
+work to understand the message. The answers come before the questions |
+and the comments precede their subjects. |
+</p> |
+ |
+<p id="ps1TopPs_3"> |
+People do attempt to justify top posting. A common excuse made by top |
+posters is that their newsreader places the cursor at the top of the |
+message so that is the natural place to write the reply. That excuse |
+is worthless as newsreader software does not dictate the style of |
+posting and a cursor that starts at the top of a post does not have |
+to stay there. Indeed it should not be expected to stay there, as the |
+first responsibility of the respondent is to trim the quoted material |
+down to just what is necessary to provide context for their response. |
+A process that may reasonably be achieved by moving down through the |
+quoted material deleting whatever is unneeded and marking those edits. |
+And having done that the cursor will be at or near the bottom. |
+</p> |
+ |
+<p id="ps1TopPs_4"> |
+The other common excuse for top posting is that avoids excessive |
+scrolling in order to find the response. The need for excessive |
+scrolling in an interleaved or bottom posted message is most likely |
+an indicator that the quoted material has not been suitably, or |
+sufficiently, trimmed. But a top posters apparent desire to avoid |
+scrolling is of no value to the regular users of Usenet who are |
+accustomed to interleaved/bottom posting. They may take the top |
+post as a preamble and still scroll down to see if any specific |
+points have been responded to, not discovering that hey have wasted |
+their time until they get to the bottom. A very short top post, |
+without a blank line separating it from the following attribution |
+line, may easily be missed by someone expecting the quoted material |
+to come first, meaning that they do not discover where to look until |
+they have scrolled to the bottom, and then they need to back scroll |
+to the top. The perception is wrong, top posting results in much more |
+unnecessary scrolling than it avoids, and that misperception impacts on |
+regular user of Usenet; the very people whose help and co-operation is |
+being sought. |
+</p> |
+ |
+<p id="ps1TopPs_5"> |
+<strong>Never top post to comp.lang.javascript</strong>. There is an |
+established convention in posting styles on Usenet, and |
+comp.lang.javascript. It is most efficient for everyone if newcomers |
+follow that convention even if it seems strange to do so at first. In |
+the long run it makes life easier for everyone, and the sooner that is |
+recognised the less likelihood there is of being taken for a fool. |
+</p> |
+ |
+<h2 id="ps1Trim">What to Trim</h2> |
+ |
+<p id="ps1Trim_1"> |
+As a general guide, quoted material should almost never exceed the |
+text posted in response. Exceptions might occur when both consist |
+of exactly one line or sentence, or maybe a short code block that |
+only needs a one line comment. |
+</p> |
+ |
+<p id="ps1Trim_2"> |
+Deciding what to trim is a matter of judgment and takes a bit of |
+practice to get right. There are few hard and fast rules and the best |
+general advice has got to be to observe the behaviour of others |
+positing to the group. |
+</p> |
+ |
+<p id="ps1Trim_3"> |
+It is necessary to preserve the context in |
+which a response is made but some things can be trimmed automatically: |
+Signatures (if present); the section at the end of a post (and they |
+should always, and only, be at the end, no matter where any individual |
+newsreader may try to insert them) which starts with a separator |
+consisting of "dash dash space newline(aka return)" on a |
+line of its own, followed by material that is not directly |
+related to the post but may be of more general interest or related |
+to the individual who makes the post, should <strong>always</strong> |
+be trimmed from quoted material. It is never normally relevant to a |
+posted response, but even if a signature is being directly commented |
+upon it is vital to remove the signature separator at least, and best |
+to trim anything it contains that is not being commented upon. |
+</p> |
+ |
+<p id="ps1Trim_4"> |
+In comp.lang.javascript there is rarely much point in quoting |
+javascript code that is not being directly commented upon or |
+corrected. The indenting character (and possible line wrapping) |
+renders any quoted code syntactically invalid so anyone wanting |
+to use the code will have to go back to the message in which it |
+originally appeared anyway. That would certainly apply to a post |
+wishing to thank someone who had provided a complete scripted |
+demonstration for their efforts. |
+</p> |
+ |
+<p id="ps1Trim_5"> |
+No matter what gets trimmed the material that stays should not be |
+altered in terms of spelling, words used and their order, etc. A |
+quote should be a quote not an interpretation. |
+</p> |
+ |
+<h2 id="ps1Marg">Margins and Line Wrapping</h2> |
+<h3 id="ps1Mar">Margins</h3> |
+ |
+<p id="ps1Mar_1"> |
+A line in a plane text Usenet post could be quite long, and some |
+newsreaders would automatically wrap that line to the window in |
+which it was being displayed. Others would provide horizontal scroll |
+bars (usually undesirable) and yet others may not be able to access |
+characters beyond the 80<sup>th</sup> (unlikely these days). There |
+are no rules for handling excessively long lines but Usenet is old |
+and has lived through the time when 80 character wide displays were |
+commonplace. Along the way it became the convention that Usenet posts |
+should have lines no longer than between 70 and 80 characters. And |
+that avoids the need for any specified requirement in the handling of |
+long lines by newsreader software. Lines in that range are unlikely to |
+need to be wrapped for display and will not normally generate |
+horizontal scroll bars. It is also the case that humans generally |
+(and sometimes strongly) prefer to read text that is no more than about |
+80 character per line. |
+</p> |
+ |
+<p id="ps1Mar_2"> |
+It is widely recommended that newsreader software should be configured |
+to automatically wrap at about 72 characters when posting, which works |
+well for posted text but can be problematic for posting long URLs and |
+particularly in our context, posted source code. Others suggest that |
+software should not be allowed to wrap posted code at all and that the |
+poster should always do it manually. In either case it is the |
+individual composing the post that is responsible for, and should be in |
+control of, the wrapping of the content to ensure that it is suitable |
+for posting to the group. |
+</p> |
+ |
+<h3 id="ps1Lw">Line Wrapping</h3> |
+ |
+<p id="ps1Lw_1"> |
+Most URLs are less than 72 character long anyway but some, such as |
+references to Usenet articles in the archives at groups.google.com, |
+are longer. Some newsreader software is smart enough to recognise a |
+URL and not apply its default margin settings to them but in any event |
+delimiting them with "<URL:" at the beginning and |
+">" at the end (preferably with the URL separated from |
+the delimiters with spaces) should be sufficient to indicate to a |
+reader that a line wrapped URL will need some remedial action. |
+</p> |
+ |
+<p id="ps1Lw_2"> |
+Restricting margins to 72 characters usually does not need to affect |
+the functionality of posted code either. Allowing a newsreader to |
+line wrap posted code as it sees fit will usually render that code |
+faulty (and difficult to read) and that will make it difficult for |
+a reader to differentiate between problems within the original code |
+and problems arising form line wrapping. |
+</p> |
+ |
+<h3 id="ps1Lw_3">Code Reformatting</h3> |
+<p> |
+Javascript (ECMAScript) is extremely tolerant of white space (including |
+line breaks) within its source code. There are in fact only a couple of |
+places where white space characters are not allowed. For example, a |
+line break may not be placed between the <code>return</code> keyword |
+and any expression that is to be returned. As a result it is almost |
+always possible to spread a line of javascript that would exceed the |
+newsreaders wrapping margin across two or more lines without affecting |
+its functionality or syntactic correctness. |
+</p> |
+ |
+<p id="ps1Lw_4"> |
+One of the main reasons that Javascript is so tolerant of white space |
+is to allow the structure of the source code (how the code is laid out |
+in a text editor/post) to convey additional meaning (usually related to |
+structure of the code/function/program) and maximise clarity for human |
+readers. The breaking of long lines of source code across several lines |
+should be done in a way that does not detract from the clarity of the |
+code. |
+</p> |
+ |
+<h4 id="ps1Lw_5">Blocks</h4> |
+<p> |
+The main source code structuring consideration that adds clarity is |
+block indenting. A block is defined with curly brackets <code>{</code> |
+and <code>}</code> and represents a block statement (which may contain |
+zero or more other statements). It is normal to indent the statements |
+within a block by one tab character, though that tab is usually set to |
+4 or fewer spaces (two spaces is frequently recommended) width as the |
+normal default 8 spaces width is a bit |
+too deep for most practical uses. However, <em>tab character should not be |
+used for indention in Usenet posts at all</em> as newsreader default tab |
+settings may often be 8 characters but may also be zero characters, |
+defeating the purpose indentation in posted code entirely. Good text |
+editors will usually offer a facility to convert tabs to any number of |
+spaces, which can be used to prepare code for posting. Indenting in |
+code posted to Usenet should be done with space characters. 4 or fewer |
+(two is often recommended) per level of indentation. |
+</p> |
+ |
+<p id="ps1Lw_6"> |
+There are various common styles of block indenting, of which I prefer |
+to leave the opening <code>{</code> at the end of the control statement |
+(on the same line) and list the block contents as one statement per |
+line, indented by 4 <em>or fewer</em> spaces, with the closing |
+<code>}</code> on a new line indented so that it lines up vertically |
+with the start of the control statement, e.g.:- |
+</p> |
+ |
+<pre id="ps1Lw_ex1"> |
+function doSomething(oArea){ |
+ var nArea = oArea.nWidth * oArea.nHeight; |
+ var result = true; |
+ if(!nArea){ |
+ removeRegion(oArea); |
+ result = false; |
+ }else if(oArea.nWidth < oArea.nHeight){ |
+ result = oArea.reShape(nArea); |
+ } |
+ return result; |
+} |
+ |
+<span class="commentJS">/* The same function may also be commonly formatted:- */</span> |
+ |
+function doSomething(oArea) |
+{ |
+ var nArea = oArea.nWidth * oArea.nHeight; |
+ var result = true; |
+ if(!nArea) |
+ { |
+ removeRegion(oArea); |
+ result = false; |
+ } |
+ else if(oArea.nWidth < oArea.nHeight) |
+ { |
+ result = oArea.reShape(nArea); |
+ } |
+ return result; |
+} |
+ |
+<span class="commentJS">/* -or:- */</span> |
+ |
+function doSomething(oArea) |
+ { |
+ var nArea = oArea.nWidth * oArea.nHeight; |
+ var result = true; |
+ if(!nArea) |
+ { |
+ removeRegion(oArea); |
+ result = false; |
+ } |
+ else if(oArea.nWidth < oArea.nHeight) |
+ { |
+ result = oArea.reShape(nArea); |
+ } |
+ return result; |
+ } |
+</pre> |
+ |
+<p id="ps1Lw_7"> |
+It is not that important which style of block indenting is used, |
+everyone has their own preferred style, but it is important that |
+<em>a</em> style of block indenting is used in code posted to the |
+group. In all cases the indenting serves to make the structure of |
+the function apparent in the structure of the source code. It is |
+an aid to human readers of the code and saves a great deal of time |
+for anyone attempting to offer help and so needing to understand |
+the code. |
+</p> |
+ |
+<p id="ps1Lw_8"> |
+Sometimes the line wrapping problem can be avoided by reducing the |
+number of space characters by which the blocks are indented. However, |
+if a statement must be broken across several lines |
+it could be indented to a different level than it's own start, and it |
+could also be indented at a different level to its block contents (if |
+any). If block indenting is at, say, 4 space intervals then indenting a |
+broken line at 1 to 3 characters should serve to make it clear that |
+it is indenting separate from the general block structure of the |
+code, e.g.:- |
+</p> |
+ |
+<pre id="ps1Lw_ex2"> |
+function getRootElement_OrDefault(deflt){ |
+ if((typeof document.compatMode == "string")&& |
+ (document.compatMode.indexOf("CSS") != -1)&& |
+ (document.documentElement)){ <span class="commentJS">//<< broken statement</span> |
+ return document.documentElement; |
+ }else if(document.body){ |
+ return document.body; |
+ }else{ |
+ return deflt; |
+ } |
+} |
+ |
+</pre> |
+ |
+<p id="ps1Lw_9"> |
+Another alternative for formatting statements broken across lines |
+might be to disregard the indenting on the left and line the code |
+that belongs to the broken statement up on the right hand side. |
+e.g.:- |
+</p> |
+ |
+<pre id="ps1Lw_ex3"> |
+this.position = function(){ |
+ var twiceSize; |
+ if(--delay <= 0){ |
+ step(); |
+ if(((z+=fv) >= planeDepth)|| |
+ ((dy+dm) > windowCenterY)|| |
+ ((dx+dm) > windowCenterX)|| |
+ (v < 0)){ <span class="commentJS">//right aligned broken statement</span> |
+ this.reset(); |
+ step(); |
+ } |
+ div.top = (sy+(py*dy)-dm)+cssUnitsOrZero; |
+ div.left = (sx+(px*dx)-dm)+cssUnitsOrZero; |
+ divClip.height = (twiceSize = (dm << 1)+cssUnitsOrZero); |
+ divClip.width = twiceSize; |
+ } |
+ next.position(); |
+}; |
+</pre> |
+ |
+<p id="ps1Lw_10"> |
+Thus the ability to insert line breaks liberally throughout javascript |
+source code allows almost all code to be formatted in a fully |
+functional, well structured and clear way within the restricted |
+margins appropriate in posts to comp.lang.javascript. Efforts put into |
+preparing posted code to be clear and comprehensible to its Usenet |
+audience will be rewarded. But it is important to start any formatting |
+required with the actual code in use, rather than attempting to |
+re-type it, in order not to introduce errors that are not present in |
+the original and so have nothing to do with the original problem. |
+Having prepared the formatting of the code to suite Usenet it is |
+important to re-test it to ensure that it is still as functional, that |
+no errors have been introduced and that it still exhibits whatever |
+behaviour it was that motivated the post in the first place. |
+</p> |
+ |
+<h3 id="ps1LwQu">Line Wrapping in Quoted Material</h3> |
+ |
+<p id="ps1LwQu_1"> |
+If a news post has been wrapped at, say, 72 characters and it is |
+responded to then the indenting character used to mark quoted material |
+will add to the length of that line. Maybe pushing it over the length |
+at which the reply will be wrapped. The result, if posted without |
+adjustment, may look something like this:- |
+</p> |
+ |
+<pre id="ps1LwQu_ex1"> |
+An example OP wrote: |
+> A long line of text quoted from the previous post, that was wrapped |
+at |
+> 72 characters in that post but has been extended to 74 characters |
+long |
+> lines because of the addition of the indenting characters that mark |
+it |
+> as a quotation, but has been re-wrapped to 72 characters in the |
+posted |
+> follow-up that is quoting it. |
+ |
+The comment posted in response to the material quoted above. Originally |
+wrapped in the response at 72 characters. |
+</pre> |
+ |
+<p id="ps1LwQu_2"> |
+The effect is that the words "at", "long", |
+"it" and "posted" are no |
+longer marked as part of the quotation but instead appear to be badly |
+formatted and meaningless comments on that quoted material. Which has |
+itself gained the appearance of being incompetently trimmed. The effect |
+escalates with additional responses, loosing more meaning and becoming |
+less and less clear as to whom any particular part of the text is |
+attributable. |
+</p> |
+ |
+<pre id="ps1LwQu_ex2"> |
+The First Responder wrote: |
+> An example OP wrote: |
+>> The First Responder wrote: |
+>>> An example OP wrote: |
+>>>> A long line of text quoted from the previous post, that was |
+wrapped |
+>>> at |
+>>>> 72 characters in that post but has been extended to 74 characters |
+>>> long |
+>>>> lines because of the addition of the indenting characters that |
+mark |
+>>> it |
+>>>> as a quotation, but has been re-wrapped to 72 characters in the |
+>>> posted |
+>>>> follow-up that is quoting it. |
+>>> |
+>>> The comment posted in response to the material quoted above. |
+>> Originally |
+>>> wrapped in the response at 72 characters. |
+> |
+>> This response is the OP's reply to the comments on the original |
+>> post. It quoted the previous posts in full and was wrapped at 72 |
+>> characters. |
+> |
+>And the original responder added this. |
+ |
+The conversation ended with the OP thanking the responder for their |
+comments. (but who said what?) |
+</pre> |
+ |
+<p id="ps1LwQu_3"> |
+The solution is to be aware that this may happen and re-wrap that |
+quoted material so that it is not effected by the automatic line |
+wrapping when a post is sent, or to increase the wrapping margins by |
+the number of characters inserted to mark a quotation (so long as the |
+result does not exceed 80 characters). Some newsreader software can |
+handle this automatically, and add on software exists for other |
+products (such as OE), but ultimately the responsibility for properly |
+marking and attributing quoted material belongs with the individual |
+making the post. |
+</p> |
+ |
+<p id="ps1LwQu_4"> |
+If the two had taken the effect of the progressive lengthening of lines |
+in quoted material into account the result would have looked like |
+this:- |
+</p> |
+ |
+<pre id="ps1LwQu_ex3"> |
+The First Responder wrote: |
+> An example OP wrote: |
+>> The First Responder wrote: |
+>>> An example OP wrote: |
+>>>> A long line of text quoted from the previous post, that was |
+>>>> wrapped at 72 characters in that post but has been extended to 74 |
+>>>> characters long lines because of the addition of the indenting |
+>>>> characters that mark it as a quotation, but has been re-wrapped to |
+>>>> 72 characters in the posted follow-up that is quoting it. |
+>>> |
+>>> The comment posted in response to the material quoted above. |
+>>> Originally wrapped in the response at 72 characters. |
+> |
+>> This response is the OP's reply to the comments on the original |
+>> post. It quoted the previous posts in full and was wrapped at 72 |
+>> characters. |
+> |
+>And the original responder added this. |
+ |
+The conversation ended with the OP thanking the responder for their |
+comments. |
+</pre> |
+ |
+<p id="ps1LwQu_5"> |
+It would have been clear form the number of quote indicating characters |
+who exactly had said what and the quoted material would have been |
+easier to read and understand. Though in practice there probably should |
+have been much more trimming along the way as the whole conversation |
+would probably not have been needed to show the context of each |
+response. |
+</p> |
+ |
+ |
+<h2 id="ps1Code">General Code Postings, and when to use a URL</h2> |
+ |
+<p id="ps1Code_1"> |
+Often a question cannot usefully be answered without being accompanied |
+with javascript source code. Even a very detailed explanation of a |
+process will not tend to narrow the possibilities down to just one |
+method, and deciding whether a reported effect is due to a |
+characteristic of an execution environment or code that is taking the |
+wrong approach cannot be done without seeing the source code. |
+</p> |
+ |
+<p id="ps1Code_2"> |
+In some cases small snippets of code, say an individual function's |
+definition, might be sufficient. Though it is usually necessary to know |
+how such a function is being called and what arguments it is using. But |
+ideally questions should be accompanied by an easily testable example |
+that demonstrates the characteristics that provoked the question. |
+</p> |
+ |
+<p id="ps1Code_3"> |
+Easily testable because there is no better aid to debugging than being |
+able to reproduce a problem. Time spent turning a snippet of code into |
+testable web page (or whatever) is not rewarded if the results do not |
+exhibit the problem described. And asking many people to repeat that |
+process when it could have been done by the questioner once, in a way |
+that guaranteed a demonstration of the problem, is not a good use of |
+the group or the time of its participants. |
+</p> |
+ |
+<p id="ps1Code_4"> |
+However, posting the entire source code of a web page that happens to |
+include the problematic script is rarely the answer. For one thing web |
+pages often import <span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span>, other script files and graphics (the latter |
+simply could not be posted to the group). But web pages also often |
+consist of large amounts of <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span>, which would require careful |
+re-wrapping to fit within the posting margins and be left correct, and |
+much of the <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> would be irrelevant to the post. |
+</p> |
+ |
+<p id="ps1Code_5"> |
+There is also the question of bandwidth in transmission and capacity in |
+storage. Every post is stored, at least for a time, on every news server |
+carrying the group world wide and usually also on the hard disk of |
+everyone who reads the group. And to get from server to server, and |
+eventually to the many readers, it must be transmitted, consuming |
+bandwidth related to the size of the post. The more irrelevant |
+information that any post contains the more those resources are wasted |
+(this is also a reason for appropriate trimming of quoted material). |
+</p> |
+ |
+<p id="ps1Code_6"> |
+The comp.lang.javascript FAQ makes the injunction that posting code of |
+more than 500 lines is unacceptable. That is the theoretical upper |
+limit that should never be exceeded, but a post of even half that |
+length would have to be exceptional. |
+</p> |
+ |
+<p id="ps1Code_7"> |
+The easiest way of demonstrating a problem in context without posting |
+excessive amounts of code and <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> is to make a page that exhibits the |
+problem available online and post the URL of that page along with the |
+question. Possibly accompanied by a snippet of the code highlighting |
+the suspected cause. |
+</p> |
+ |
+<p id="ps1Code_8"> |
+The circumstances under which making a page that demonstrates the |
+problem available online is advisable are, whenever that page is |
+interacting within a frameset, or whenever images play a significant |
+part in a problem. As those circumstances are time consuming to |
+reproduce from posted code and cannot always be recreated for testing. |
+But any request to have people examine excessive amounts of code |
+and/or <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> should also be made available on line instead of in a post. |
+</p> |
+ |
+<p id="ps1Code_9"> |
+Unfortunately not all participants in the group are viewing Usenet in a |
+way that makes easily following a URL to an example page viable. Often |
+downloading new Usenet posts as a batch and then viewing them off line. |
+Re-connecting to view an example page is not always desirable (due to |
+local conditions, slow modem connections and potential associated |
+expense) and can be unrewarding if the page viewed is a mush of <abbr title="What You See Is What You Get">WYSIWYG</abbr> |
+generated bloated <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span>-like noise. Where it takes a considerable time |
+to even locate the cause of what may still turn out to be a trivial |
+problem. |
+</p> |
+ |
+<p id="ps1Code_10"> |
+Then again there are people using the group who much prefer examples to |
+be available online. Either way what is not wanted is for questions to |
+be asked without making the corresponding code available in some way, |
+or the posting of excessive amounts of code. |
+</p> |
+ |
+<p id="ps1Code_11"> |
+A good compromise, and a valuable debugging technique, is the creation |
+of a demonstration test-case page. A page containing no more than |
+sufficient <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> and javascript to allow others to reproduce the problem |
+and see the relevant code without anything extraneous. Such a test-case |
+page would almost always be short enough to post with the question |
+(properly formatted) and could additionally be made available online for |
+the convenience of those that would prefer to use a URL to load it |
+directly into a browser for testing. |
+</p> |
+ |
+<p id="ps1Code_12"> |
+Creating such a test-case page can be valuable in itself as attempting |
+to reduce a problem from a larger context to an isolated demonstration |
+will often reveal the cause of that problem along the way. The act of |
+cutting out what initially seems irrelevant often reveals that the |
+problem in fact results form an unexpected interaction with something |
+that was assumed to be unconnected. And if the problem can be |
+demonstrated in isolation then it is relatively easy for readers of the |
+question to reproduce the problem and identify its cause, resulting in |
+less discussion of superfluous issues and usually a quicker resolution |
+of the question. |
+</p> |
+ |
+<p id="ps1Code_13"> |
+It is very important to test test-case pages prior to posting them to |
+ensure that they do indeed exhibit the characteristic that motivated |
+the question. At least if you want to be taken seriously in future. And |
+when such a test-case is posted to the group it should be formatted and |
+indented to facilitate the essayist reading of the code and testing by |
+no more than copying the page into a text editor and saving it locally |
+as a <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> file. |
+</p> |
+ |
+ |
+<h2 id="ps1Quest">Well Asked Questions Get the Best Answers</h2> |
+ |
+<p id="ps1Quest_1"> |
+Beyond any considerations of posting style and formatting, the quality |
+of questions asked has a considerable impact upon the number and |
+quality of answers given, and the time taken to elicit a useful |
+response. Vague questions are likely to be answered with requests for |
+clarification. Terse general questions are likely to elicit yes or no |
+answers (often both). And questions that have been asked, and well |
+answered, before (especially frequently or in the recent past) are as |
+likely as not to be ignored. |
+</p> |
+ |
+<p id="ps1Quest_2"> |
+When asking a question it would almost always be a good idea to put |
+yourself in the position of the reader of the question, read it back to |
+yourself and ask yourself; Does this question actually ask what I |
+want to know, and is it sufficiently clear and detailed to elicit that |
+answer? |
+</p> |
+ |
+<p id="ps1Quest_3"> |
+The following is a short list of recurring features of badly asked |
+questions:- |
+</p> |
+ |
+ |
+<h3 id="ps1QSub">Appropriate Use of the Subject Header</h3> |
+ |
+<p id="ps1QSub_1"> |
+Another Usenet convention holds that the Subject header should not be |
+expected to be available to a reader of any message. While it is |
+unlikely that there are any newsreaders currently in use in which the |
+user is not presented with the Subject header, it remains |
+inappropriate to ask a question in the subject header (or to assume |
+that its contents will provide supplementary information about a |
+question) and doing so is as likely to invoke a lesson in Usenet |
+etiquette as an answer to the question. But a question is not really |
+a subject anyway. The subject should describe what the question is |
+in relation to, and is useful for archive searching and |
+categorisation rather than as a means of communication. |
+</p> |
+ |
+<p id="ps1QSub_2"> |
+Any question asked should always appear in the body of the post, and in |
+a form that does not assume the availability of the Subject header to |
+the reader. |
+</p> |
+ |
+<p id="ps1QSub_3"> |
+The Subject header should also not be wasted. Placing "please |
+help" and the like in the subject header is unwelcome and |
+counterproductive. Take the opportunity to construct a Subject |
+header that states the type of the problem, what it relates to. |
+Subject headers that state the real subject will attract the |
+attention of people with a special interest in that subject, |
+exactly those people best able to offer help. |
+</p> |
+ |
+<h3 id="ps1QRes">Doing Your Own Research</h3> |
+ |
+<p id="ps1QRes_1"> |
+Many questions, at least the questions literally asked, could quickly |
+be answered with a little research, and the chances are that regulars |
+on the group know how easily those answers could have been found with |
+a little effort. The group is not intended to be a vending machine for |
+trivial information about javascript, that wouldn't be an interesting |
+group to participate in. |
+</p> |
+ |
+<p id="ps1QRes_2"> |
+On the other hand, evidence that some effort has been put into |
+researching a problem before asking a question tends to be looked upon |
+kindly (though claiming to have exhausted all lines of research when that |
+is obviously not the case would have the opposite effect). |
+</p> |
+ |
+<p id="ps1QRes_3"> |
+The obvious first place to look for answers is in |
+<a href="../clj_faq.html">the group's FAQ</a>. That will |
+mean reading all of the FAQ because the answers to some problems (or |
+sufficient clues to those answers) will be found within the answers to |
+seemingly unrelated questions, or the resources linked to from the FAQ. |
+That in itself is a fairly big task, but if not undertaken will just |
+result in questions being answered with references back to the FAQ. |
+There is also a Usenet convention that whenever a newsgroup |
+(particularly technical groups) provides a FAQ resource, that FQA should |
+be read by anyone wishing to post to the group <em>prior</em> to making |
+their first post. So reading the FAQ is <em>not optional</em> anyway. |
+</p> |
+ |
+<p id="ps1QRes_4"> |
+The Internet provides a massive resource for researching virtually any |
+question, and particularly questions relating to computing. Search |
+engines are the primary research tool and among those Google search is |
+extremely useful. But when a question relates to javascript in a way |
+that indicates that it may be suitable to be asked on |
+comp.lang.javascript the best place to research the question is |
+<a href="http://groups.google.com/groups?q=comp.lang.javascript"> |
+groups.google.com</a>, as that resource holds a searchable archive of |
+almost all postings ever made to the group. There is an |
+<a href="http://groups.google.com/advanced_group_search?group=comp.lang.javascript"> |
+"Advanced Search" page</a>, where entering |
+"comp.lang.javascript" in the Newsgroup field and suitable keywords |
+in the other fields provided will return all messages posted to the group |
+that match the search criteria. As most questions will have been asked |
+before many answers can be obtained from the groups.google.com archives. |
+But be aware of the date of posts found as the archive goes back to |
+1996 and some things have changed over the intervening years. |
+</p> |
+ |
+<p id="ps1QRes_5"> |
+Reading back over recent posts to the group, for a minimum of a week |
+and preferably more than a month will avoid the need to re-ask a |
+question that has been asked and answered in recent memory. Not all |
+news servers will hold all messages over that period so |
+<a href="http://groups.google.com/groups?q=comp.lang.javascript"> |
+the group archives at groups.google.com</a> can be useful in this |
+respect also. |
+</p> |
+ |
+<h3 id="ps1DontWork">"It doesn't work"</h3> |
+ |
+<p id="ps1DontWork_1"> |
+People don't tend to question code that |
+does exactly what they want it to do. Sometimes they may ask how it |
+does exactly what they want but generally it is superfluous to state |
+that code that does not address a situation doesn't work. |
+</p> |
+ |
+<p id="ps1DontWork_2"> |
+To start with computer code always does exactly what is asked of it, |
+that is the nature of computers. So it does "work", it is |
+just that it doesn't do what is wanted of it. And that is a problem |
+because code that does not do something that is wanted of it cannot |
+itself explain what that something was. |
+</p> |
+ |
+<p id="ps1DontWork_3"> |
+It is the task of a human designer to decide what is wanted form code, |
+to specify the task. The reader of a question can do no more than |
+guess as to the task unless the questioner explains the specification |
+for the code, and that specification needs to be specific. That |
+information is needed when assessing what would qualify as |
+"works" in the context of the question. |
+</p> |
+ |
+<p id="ps1DontWork_4"> |
+But in addition to knowing what would qualify as working it is also |
+necessary to know in what way code is failing to work. There is a big |
+difference between code that appears to do nothing when executed, code |
+that does something undesirable in addition to what is expected of it |
+and code that does something else entirely. |
+</p> |
+ |
+<p id="ps1DontWork_5"> |
+Instead of stating the self-evident "It doesn't work", the |
+posters of questions should attempt to provide answers to:- |
+</p> |
+ |
+<dl id="ps1DontWork_df"> |
+ <dt id="ps1DontWork_p1">1. What you have done?</dt> |
+ |
+ <dd id="ps1DontWork_d1">Providing access the code (possibly in the form of a test |
+ case), explaining the steps required to initiate the |
+ undesirable effect and the environments (usually web |
+ browsers) where the problem has been observed, so that it |
+ can be reproduced and tested. |
+ <dd> |
+ |
+ <dt id="ps1DontWork_p2">2. What you expected to happen?</dt> |
+ |
+ <dd id="ps1DontWork_d2">Both specifically in the case of the code under discussion and |
+ generally with regard to the design specification. |
+ <dd> |
+ |
+ <dt id="ps1DontWork_p3">3. What really happened?</dt> |
+ |
+ <dd id="ps1DontWork_d3">What was observed to happen and any error messaged generated. |
+ So that when (and if) the problem is reproduced in testing it |
+ is possible to identify it as the effect under discussion. |
+ <dd> |
+</dl> |
+ |
+<h3 id="ps1CntX">Explain the Whole Context</h3> |
+ |
+<p id="ps1CntX_1"> |
+The context in which a question is asked or a problem exists is very |
+important to the type of answers given or solutions proposed. Beyond |
+the obvious required details such as the execution host(s), scripts |
+used and how they are used, There are details like whether a project |
+is for the Internet or an Intranet. Because on comp.lang.javascript |
+the default assumption is that the context will be browser scripting |
+for the Internet, many approaches/solutions are ruled out, but some may |
+be completely reasonable and practical in the more restricted context |
+of a company Intranet (or on more controlled hosts such as the windows |
+scripting host or server-side javascript). |
+</p> |
+ |
+<p id="ps1CntX_2"> |
+There have been many occasions where many answers to questions have |
+stressed the unsuitability of particular approaches to use on the |
+Internet only for the original questioner to respond by explaining |
+that they don't apply because their context is an Intranet. That |
+wastes the time of everyone who composed a response to the original |
+question; if they had been in possession of the real context from the |
+outset they could have got on with proposing/discussing solutions that |
+were appropriate, or moved on to something more interesting. |
+</p> |
+ |
+<p id="ps1CntX_3"> |
+But the full context of a question goes beyond such details and answers |
+the question: why? Often a question will be asking about a particular |
+approach that, to the questioner, is a perceived solution to a problem. |
+The answer to the question why would explain what that problem is, and |
+knowing what that problem is will allow respondents to assess the |
+suitability of the approach at addressing the problem and possibly |
+suggest other, and superior, solutions. There is very little that is |
+genuinely new and unique, whatever the wider problem is the chances are |
+that someone will be familiar with it and be in a position to identify |
+the best solution (even if that is just confirming that the proposed |
+solution is the best available). Questions raised on the group should |
+always include the answer to the question: why? |
+</p> |
+ |
+<h3 id="ps1PR">Proof-Read your Questions</h3> |
+ |
+<p id="ps1PR_1"> |
+Proof-read your message two or three times before posting. Ensure that |
+it is as error free as possible and does convey the question and |
+provide all of the necessary information in a way that will not be |
+subject to misinterpretation. |
+</p> |
+ |
+<!-- <h2><a name="ps1notHD" id="ps1notHD">comp.lang.javascript is Not a Helpdesk</a></h2> |
+ |
+<p id="ps1notHD_1"> |
+The comp.lang.javascript newsgroup is a discussion forum on the subject |
+of ECMAScript (javascript). It is not a helpdesk and its participants |
+make their contributions voluntarily in their own time and with their |
+own resources for whatever reasons they see fit. Just because the |
+majority of the discussion starts with someone posting a question, |
+often asking for help with some aspect of javascript, does not mean |
+that anyone should have any expectation of receiving any help or |
+assistance from any ensuing discussion. So, although help and |
+assistance is often (if not usually) offered during the following |
+discussion, that is just a positive side effect of the group and not |
+its reason for existing. |
+</p> |
+ |
+<p id="ps1notHD_2"> |
+The real benefit of the group goes to its regular participants, who, |
+in participating in the discussion, attempting to address the diverse |
+situations and problems raised and exposing their code to critical |
+public scrutiny, get to hone their code authoring and script design |
+skills and knowledge with the active assistance and critical feed-back |
+of their peers. |
+</p> |
+ |
+<p id="ps1notHD_3"> |
+The whole process would not work as well if people did not post |
+questions relating to a diversity of problem situations to the group |
+in the hope of getting some assistance, and people would never do that |
+if there were not a good chance of actually getting assistance. But the |
+people posting such questions need to remember that they have no right |
+to assistance, and cannot demand, or even expect it. Instead, wanting |
+to take advantage of the potentially beneficial side effect of the |
+existence of the group, they should encourage the group to give them |
+assistance, by making doing so as easy and convenient as possible. The |
+preceding document is intended to make it as clear as possible how to |
+go about achieving that aim. |
+</p> --> |
+ |
+<h2 id="ps1AddR">Additional Reading</h2> |
+ |
+<ul class="resourceList" id="ps1AddR_l"> |
+ <li><a href="http://www.cs.tut.fi/~jkorpela/usenet/dont.html">The seven don'ts of Usenet<br>http://www.cs.tut.fi/~jkorpela/usenet/dont.html</a></li> |
+ <li><a href="http://oakroadsystems.com/genl/unice.htm">Playing Nice on Usenet<br>http://oakroadsystems.com/genl/unice.htm</a></li> |
+ <li><a href="http://www.netmeister.org/news/learn2quote2.html">How do I quote correctly in Usenet? - Quoting and Answering<br>http://www.netmeister.org/news/learn2quote2.html</a></li> |
+ <li><a href="http://www.xs4all.nl/%7ewijnands/nnq/nquote.html">Quoting Style in Newsgroup Postings<br>http://www.xs4all.nl/%7ewijnands/nnq/nquote.html </a></li> |
+ <li><a href="http://www.cs.tut.fi/~jkorpela/usenet/xpost.html">Why and how to crosspost<br>http://www.cs.tut.fi/~jkorpela/usenet/xpost.html</a></li> |
+ <li><a href="http://kb.indiana.edu/data/affn.html?cust=12244">How can I post a message to more than one Usenet newsgroup?<br>http://kb.indiana.edu/data/affn.html?cust=12244</a></li> |
+ <li><a href="http://catb.org/%7Eesr/faqs/smart-questions.html">How To Ask Questions The Smart Way<br>http://catb.org/%7Eesr/faqs/smart-questions.html</a></li> |
+ <li><a href="http://www.chiark.greenend.org.uk/~sgtatham/bugs.html">How to Report Bugs Effectively<br>http://www.chiark.greenend.org.uk/~sgtatham/bugs.html</a></li> |
+</ul> |
+</body> |
+</html> |
/cljs/faq_notes/clj_posts.html |
---|
Property changes: |
Added: svn:mime-type |
## -0,0 +1 ## |
+text/plain |
\ No newline at end of property |
Index: cljs/faq_notes/type_convert.html |
=================================================================== |
--- cljs/faq_notes/type_convert.html (nonexistent) |
+++ cljs/faq_notes/type_convert.html (revision 43) |
@@ -0,0 +1,1319 @@ |
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" |
+"http://www.w3.org/TR/html4/strict.dtd"> |
+<html lang="en"> |
+<head> |
+ <title>Javascript Type-Conversion</title> |
+ <meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> |
+<link href="../../faq.css" rel="stylesheet" type="text/css"> |
+<link href="../notes.css" rel="stylesheet" type="text/css"> |
+<style type="text/css"> |
+td { |
+ text-align:center; |
+} |
+pre.st { |
+ background-color:#EEEEFF; |
+ margin:0px; |
+ padding:0px; |
+ text-align:left; |
+ border:0px none #EEEEFF; |
+} |
+caption { |
+ font-weight:bold; |
+} |
+th, td { |
+ vertical-align:baseline; |
+ background-color:#EEEEFF; |
+ color:#000000; |
+ white-space:nowrap; |
+ padding:2px |
+} |
+.true { |
+ color:#003444; |
+ background-color:#E0FFE0; |
+} |
+.false { |
+ color:#003444; |
+ background-color:#FFE0E0; |
+} |
+table { |
+ margin:1em 2.5em; |
+} |
+ </style> |
+ </head> |
+ <body> |
+ |
+<h1>Javascript Type-Conversion</h1> |
+<div id="faqNav"> |
+ <a href="/faq/">FAQ</a> > <a href="/faq/notes/">FAQ Notes</a> |
+</div> |
+ |
+<ul> |
+ <li><a href="#tcInt">Introduction</a></li> |
+ <li><a href="#tcBool">Converting to Boolean</a></li> |
+ <li><a href="#tcString">Converting to String</a></li> |
+ <li><a href="#tcNumber">Converting to Number</a></li> |
+ <li><a href="#tcParse">Parsing to Number</a> |
+ <ul> |
+ <li><a href="#tcParseFl">parseFloat</a></li> |
+ <li><a href="#tcParseIn">parseInt</a></li> |
+ <li><a href="#tcPrIntRx">parseInt with a radix argument</a></li> |
+ </ul> |
+ </li> |
+ <li><a href="#tcToInt32">ToInt32</a></li> |
+ <li><a href="#tcUserIn">Converting User Input</a> |
+ <ul> |
+ <li><a href="#tcRegEx">Regular expression examples</a></li> |
+ </ul> |
+ </li> |
+</ul> |
+ |
+<h2 id="tcInt">Introduction</h2> |
+ |
+<p id="tcInt_1"> |
+Javascript (ECMAScript) is a loosely typed language. That does not mean |
+that it has no data types just that the value of a variable or a Javascript |
+object property does not need to have a particular type of value assigned |
+to it, or that it should always hold the same type of value. Javascript |
+also freely type-converts values into a type suitable for (or required by) |
+the context of their use. |
+</p> |
+ |
+<p id="tcInt_2"> |
+Javascript being loosely typed and willing to type-convert still does not |
+save the programmer from needing to think about the actual type of values |
+that they are dealing with. A very common error in browser scripting, for |
+example, is to read the value property of a form control into which the |
+user is expected to type a number and then add that value to another |
+number. Because the value properties of form controls are strings (even if |
+the character sequence they contain represents a number) the attempt to |
+add that string to a value, even if that value happens to be a number, |
+results in the second value being type-converted into a string and |
+concatenated to the end of the first string value from the from control. |
+</p> |
+ |
+<p id="tcInt_3"> |
+That problem arises from the dual nature of the <code>+</code> operator |
+used for both numeric addition and string concatenation. With which the |
+nature of the operation performed is determined by the context, where |
+only if both operands are numbers to start with will the <code>+</code> |
+operator perform addition. Otherwise it converts all of its operands to |
+strings and does concatenation. |
+</p> |
+ |
+<p id="tcInt_4"> |
+The following discussion is illustrated with Javascript generated tables |
+of values resulting from the conversion operations. The headers of those |
+tables display the values as represented in the Javascript source code |
+used rather than their internal representation. So, for example |
+<code>123e-2</code> as a number was the character sequence typed into |
+the source code, the interpreter reads that and generates the |
+number value 1.23 from it for internal use. The various values used for |
+the tests have been chosen to illustrate aspects of type |
+converting, those aspects may not apply to all of the tables presented. |
+However, all of the test values are included in all of the tables (except |
+where no type converting occurs) for full comparison. The bodies of the |
+tables list the results of the various type conversion operations. |
+</p> |
+ |
+<p id="tcInt_5"> |
+If you are accepting/using this page's <span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> style suggestions the type |
+of the values at various stages is illustrated by the colour of the text |
+used. The following key shows those type/colour relationships, they are |
+derived from the string values returned by the <code>typeof</code> |
+operator (which returns <code>"object"</code> |
+for the <code>null</code> type when in reality <code>null</code> is |
+distinct from objects). |
+</p> |
+ |
+<table id="tcInt_key"> |
+ <tbody> |
+ <tr><th>Key</th></tr> |
+ <tr><td class="st">string</td></tr> |
+ <tr><td class="nm">number</td></tr> |
+ <tr><td class="bl">boolean</td></tr> |
+ <tr><td class="ob">object</td></tr> |
+ <tr><td class="fn" style="text-align:center;">function</td></tr> |
+ <tr><td class="ob">null</td></tr> |
+ <tr><td class="un">undefined</td></tr> |
+ </tbody> |
+</table> |
+ |
+<p id="tcInt_6"> |
+The boolean values of results also have a coloured background to highlight |
+ <code>true</code> or <code>false</code>. |
+</p> |
+ |
+<h2 id="tcBool">Converting to Boolean</h2> |
+ |
+<p id="tcBool_1"> |
+When evaluating the expression of an <code>if</code> statement the Javascript |
+interpreter will type-convert the result of that expression to boolean |
+in order to make its decision. Also various operators internally |
+type-convert their operands to boolean in order to determine what |
+action to take. These include the logical operators like AND |
+(<code>&&</code>), OR (<code>||</code>) and NOT (<code>!</code>). The NOT |
+operator type-converts its operand to boolean and if that value is |
+boolean true it returns false and if false it returns true. As the |
+result of a NOT operation is a boolean value that is the inverse of |
+the type-converted true-ness of its operand, two NOT operations |
+together will return a boolean value that is equivalent to the result |
+of type-converting the operand to boolean:- |
+</p> |
+ |
+<pre id="tcBool_ex1"> |
+var boolValue = !!x; |
+</pre> |
+ |
+<p id="tcBool_2"> |
+That technique has been used to generate the following tables. |
+</p> |
+ |
+<p id="tcBool_3"> |
+An alternative method of generating a boolean value that represents |
+the type-converted true-ness of a value is to pass that value to |
+the <code>Boolean</code> constructor called as a function:- |
+</p> |
+ |
+<pre id="tcBool_ex2"> |
+var boolValue = Boolean(x); |
+</pre> |
+ |
+<table> |
+ <caption>Double NOT (!!col) : Numeric Values.</caption> |
+ <thead> |
+ <tr> |
+ <th></th> |
+ <th class="nm">-1.6</th> |
+ <th class="nm">-0</th> |
+ <th class="nm">+0</th> |
+ <th class="nm">1</th> |
+ <th class="nm">1.6</th> |
+ <th class="nm">8</th> |
+ <th class="nm">16</th> |
+ <th class="nm">16.8</th> |
+ <th class="nm">123e-2</th> |
+ <th class="nm">-Infinity</th> |
+ <th class="nm">+Infinity</th> |
+ <th class="nm">NaN</th> |
+ </tr> |
+ </thead> |
+ <tbody> |
+ <tr> |
+ <th>!!col</th> |
+ <td class="true">true</td> |
+ <td class="false">false</td> |
+ <td class="false">false</td> |
+ <td class="true">true</td> |
+ <td class="true">true</td> |
+ <td class="true">true</td> |
+ <td class="true">true</td> |
+ <td class="true">true</td> |
+ <td class="true">true</td> |
+ <td class="true">true</td> |
+ <td class="true">true</td> |
+ <td class="false">false</td> |
+ </tr> |
+ </tbody> |
+</table> |
+ |
+<p id="tcBool_4"> |
+When numbers are converted to boolean, zero becomes false and all other |
+numbers are true. With the excepting of the special numeric value |
+<code>NaN</code> (Not a Number) which is used when another type is |
+converted to a number but that conversion does not result in a |
+meaningful number. <code>NaN</code> is always false. The values of |
+positive and negative infinity, while not finite numbers, are non-zero |
+numeric values and always type-convert to boolean <code>true</code>. |
+</p> |
+ |
+<table> |
+ <caption>Double NOT (!!col) : String Values.</caption> |
+ <thead> |
+ <tr> |
+ <th></th> |
+ <th class="st">""<br>(empty<br>string)</th> |
+ <th class="st">"-1.6"</th> |
+ <th class="st">"0"</th> |
+ <th class="st">"1"</th> |
+ <th class="st">"1.6"</th> |
+ <th class="st">"8"</th> |
+ <th class="st">"16"</th> |
+ <th class="st">"16.8"</th> |
+ <th class="st">"123e-2"</th> |
+ <th class="st">"010"<br>(Octal)</th> |
+ <th class="st">"0x10"<br>(Hex)</th> |
+ <th class="st">"0xFF"<br>(Hex)</th> |
+ <th class="st">"-010"</th> |
+ <th class="st">"-0x10"</th> |
+ <th class="st">"xx"</th> |
+ </tr> |
+ </thead> |
+ <tbody> |
+ <tr> |
+ <th>!!col</th> |
+ <td class="false">false</td> |
+ <td class="true">true</td> |
+ <td class="true">true</td> |
+ <td class="true">true</td> |
+ <td class="true">true</td> |
+ <td class="true">true</td> |
+ <td class="true">true</td> |
+ <td class="true">true</td> |
+ <td class="true">true</td> |
+ <td class="true">true</td> |
+ <td class="true">true</td> |
+ <td class="true">true</td> |
+ <td class="true">true</td> |
+ <td class="true">true</td> |
+ <td class="true">true</td> |
+ </tr> |
+ </tbody> |
+</table> |
+ |
+<p id="tcBool_5"> |
+Type conversion rules are even simpler for string to boolean conversion |
+as all non-empty strings always become true and empty strings become |
+false. |
+</p> |
+ |
+<table> |
+ <caption>Double NOT (!!col) : Other Values</caption> |
+ <thead> |
+ <tr> |
+ <th></th> |
+ <th class="un">undefined</th> |
+ <th class="ob">null</th> |
+ <th class="bl">true</th> |
+ <th class="bl">false</th> |
+ <th class="ob">new Object()</th> |
+ <th class="fn">function(){<br> return;<br>}</th> |
+ </tr> |
+ </thead> |
+ <tbody> |
+ <tr> |
+ <th>!!col</th> |
+ <td class="false">false</td> |
+ <td class="false">false</td> |
+ <td class="true">true</td> |
+ <td class="false">false</td> |
+ <td class="true">true</td> |
+ <td class="true">true</td> |
+ </tr> |
+ </tbody> |
+</table> |
+ |
+<p id="tcBool_6"> |
+For the other types, <code>undefined</code> and <code>null</code> are |
+converted to false, boolean values are not converted and objects and |
+functions are always true. |
+</p> |
+ |
+<p id="tcBool_7"> |
+This is the most valuable aspect of type-converting to boolean as it |
+allows a script to distinguish between properties in an environment |
+that may be undefined or may refer to an object. Treating an undefined |
+(or null) value as if it was an object will produce errors. So when |
+there is a doubt (as there usually is where web browsers are concerned) |
+then code can avoid generating errors by wrapping the code that wants |
+to access an object in an <code>if</code> test. Supplying the suspect |
+reference to the object as the expression. The expression will be type |
+converted to boolean and result in <code>false</code> if the object |
+does not exist and <code>true</code> if it does. |
+</p> |
+ |
+<pre id="tcBool_ex3"> |
+if(document.documentElement){ |
+ scrollX = document.documentElement.scrollLeft; |
+} |
+</pre> |
+ |
+<p id="tcBool_8"> |
+The double NOT operation also allows the setting of boolean flags that |
+can be used to indicate the presence of various objects:- |
+</p> |
+ |
+<pre id="tcBool_ex4"> |
+var hasDocEl = !!document.documentElement; |
+... |
+if(hasDocEl){ |
+ scrollX = document.documentElement.scrollLeft; |
+} |
+</pre> |
+ |
+<h2 id="tcString">Converting to String</h2> |
+ |
+<p id="tcString_1"> |
+As mentioned above, type conversion to a string most often results |
+from the action of the + operator whenever one of its operators in |
+not a number. The easiest way of getting the string that results |
+from type-conversion is to concatenate a value to an empty string. |
+That technique has been used to generate the following tables. |
+</p> |
+ |
+<p id="tcString_2"> |
+An alternative method of converting a value into a string is to |
+pass it as an argument to the <code>String</code> constructor |
+called as a function:- |
+</p> |
+ |
+<pre id="tcString_ex1"> |
+var stringValue = String(x); |
+</pre> |
+ |
+<table> |
+ <caption>type-convert to string ("" + col) : Numeric Values.</caption> |
+ <thead> |
+ <tr> |
+ <th></th> |
+ <th class="nm">-1.6</th> |
+ <th class="nm">-0</th> |
+ <th class="nm">+0</th> |
+ <th class="nm">1</th> |
+ <th class="nm">1.6</th> |
+ <th class="nm">8</th> |
+ <th class="nm">16</th> |
+ <th class="nm">16.8</th> |
+ <th class="nm">123e-2</th> |
+ <th class="nm">-Infinity</th> |
+ <th class="nm">+Infinity</th> |
+ <th class="nm">NaN</th> |
+ </tr> |
+ </thead> |
+ <tbody> |
+ <tr> |
+ <th>"" + col</th> |
+ <td class="st">-1.6</td> |
+ <td class="st">0</td> |
+ <td class="st">0</td> |
+ <td class="st">1</td> |
+ <td class="st">1.6</td> |
+ <td class="st">8</td> |
+ <td class="st">16</td> |
+ <td class="st">16.8</td> |
+ <td class="st">1.23</td> |
+ <td class="st">-Infinity</td> |
+ <td class="st">Infinity</td> |
+ <td class="st">NaN</td> |
+ </tr> |
+ </tbody> |
+</table> |
+ |
+<p id="tcString_3"> |
+Notice that the number generated from the source code <code>123e-2</code> |
+has resulted in the string <code>"1.23"</code> because that is |
+the string representation of the internal number created from the source |
+code. However, Javascript's internal number representations take the form |
+of IEEE double precision floating point numbers and that means that they |
+cannot represent all numbers with precision. The results of mathematical |
+operations may only produce close approximations and when they are |
+converted to strings the string represents the approximation and may be |
+unexpected and undesirable. It is often necessary to use custom functions |
+to generate string representations of numbers in an acceptable format, |
+the type-conversion mechanism is rarely suited to generating numeric output |
+intended for presentation. |
+</p> |
+ |
+<table> |
+ <caption>type-convert to string ("" + col) : Other Values.</caption> |
+ <thead> |
+ <tr> |
+ <th></th> |
+ <th class="un">undefined</th> |
+ <th class="ob">null</th> |
+ <th class="bl">true</th> |
+ <th class="bl">false</th> |
+ <th class="ob">new Object()</th> |
+ <th class="fn">function(){<br> return;<br>}</th> |
+ </tr> |
+ </thead> |
+ <tbody> |
+ <tr> |
+ <th>"" + col</th> |
+ <td class="st">undefined</td> |
+ <td class="st">null</td> |
+ <td class="st">true</td> |
+ <td class="st">false</td> |
+ <td class="st">[object Object]</td> |
+ <td><pre class="st">function(){ |
+ return; |
+}</pre></td> |
+ </tr> |
+ </tbody> |
+</table> |
+ |
+<p id="tcString_4"> |
+When objects or functions are type-converted to strings their |
+<code>toString</code> method is called. These default to |
+<code>Object.prototype.toString</code> and |
+<code>Function.prototype.toString</code> but may be overloaded |
+with a function assigned to a "toString" property of |
+the object/function. Type-converting a function to a string does |
+not necessarily result in the function's source code. The behaviour |
+of <code>Function.prototype.toString</code> is implementation |
+depended and varies quite a lot, as do the results from |
+"host objects" and methods (the objects and methods |
+provided by the environment, such as DOM elements). |
+</p> |
+ |
+<h2 id="tcNumber">Converting to Number</h2> |
+ |
+<p id="tcNumber_1"> |
+Converting values to numbers, especially strings to numbers, is an |
+extremely common requirement and many methods can be used. Any |
+mathematical operator except the concatenation/addition operator |
+will force type-conversion. So conversion of a string to a number |
+might entail performing a mathematical operation on the string |
+representation of the number that would not affect the resulting |
+number, such as subtracting zero or multiplying by one. |
+</p> |
+ |
+<pre id="tcNumber_ex1"> |
+var numValue = stringValue - 0; |
+<span class="commentJS">/* or */</span> |
+var numValue = stringValue * 1; |
+<span class="commentJS">/* or */</span> |
+var numValue = stringValue / 1; |
+</pre> |
+ |
+<p id="tcNumber_2"> |
+However, the unary <code>+</code> operator also type-converts its |
+operand to a number and because it does not do any additional |
+mathematical operations it is the fastest method for type-converting |
+a string into a number. |
+</p> |
+ |
+<p id="tcNumber_2b"> |
+Incidentally, the unary <code>-</code> (minus) operator also |
+type-converts its operand (if necessary) in addition to |
+subsequently negating its value. |
+</p> |
+ |
+<pre id="tcNumber_ex2"> |
+var numValue = (+stringValue); |
+ |
+<span class="commentJS">/* The preceding unary + expression has been parenthesised. That is |
+ unnecessary but is often felt to make the code easier to comprehend |
+ and make it clear which operations are being applied. Especially |
+ avoiding confusion with pre and post increment and addition |
+ operations. Compare:- |
+ |
+var n = anyNumVar++ + +stringVar + ++anotherNumVar; |
+ |
+ - with - |
+ |
+var n = (anyNumVar++) + (+stringVar) + (++anotherNumVar); |
+ ^^ ^ ^^ |
+ (post increment) + (unary plus) + (pre increment) |
+*/</span> |
+</pre> |
+ |
+<p id="tcNumber_3"> |
+While unary <code>+</code> is the fastest method for converting a |
+string to a number a final method is available that uses the |
+Javascript type-conversion algorithms. The <code>Number</code> |
+constructor can be called with the string value as its argument |
+and its return value is a number representing the result of the |
+type-conversion. |
+</p> |
+ |
+<pre id="tcNumber_ex3"> |
+var numValue = Number(stringValue); |
+</pre> |
+ |
+<p id="tcNumber_4"> |
+The Number constructor is the slowest of the type-converting |
+methods but when speed is not an overriding consideration its |
+use does produce the clearest source code. |
+</p> |
+ |
+<p id="tcNumber_5"> |
+The following tables show the results of type-conversion to a number using |
+the unary <code>+</code> operator. Though all of the preceding |
+alternative method produce the same results as they all use exactly the |
+same algorithm to do the conversion. |
+</p> |
+ |
+<table> |
+ <caption>type-convert to number (+col) : String Values.</caption> |
+ <thead> |
+ <tr> |
+ <th></th> |
+ <th class="st">""<br>(empty<br>string)</th> |
+ <th class="st">"-1.6"</th> |
+ <th class="st">"0"</th> |
+ <th class="st">"1"</th> |
+ <th class="st">"1.6"</th> |
+ <th class="st">"8"</th> |
+ <th class="st">"16"</th> |
+ <th class="st">"16.8"</th> |
+ <th class="st">"123e-2"</th> |
+ <th class="st">"010"<br>(Octal)</th> |
+ <th class="st">"0x10"<br>(Hex)</th> |
+ <th class="st">"0xFF"<br>(Hex)</th> |
+ <th class="st">"-010"</th> |
+ <th class="st">"-0x10"</th> |
+ <th class="st">"xx"</th> |
+ </tr> |
+ </thead> |
+ <tbody> |
+ <tr> |
+ <th>+col</th> |
+ <td class="nm">0</td> |
+ <td class="nm">-1.6</td> |
+ <td class="nm">0</td> |
+ <td class="nm">1</td> |
+ <td class="nm">1.6</td> |
+ <td class="nm">8</td> |
+ <td class="nm">16</td> |
+ <td class="nm">16.8</td> |
+ <td class="nm">1.23</td> |
+ <td class="nm">10</td> |
+ <td class="nm">16</td> |
+ <td class="nm">255</td> |
+ <td class="nm">-10</td> |
+ <td class="nm">NaN</td> |
+ <td class="nm">NaN</td> |
+ </tr> |
+ </tbody> |
+</table> |
+ |
+<p id="tcNumber_6"> |
+The important considerations when converting strings to numbers with |
+the type-converting methods is the results from strings that do not |
+represent numbers. The empty string is converted into the number zero, |
+depending on the application this can be harmless or disastrous, but |
+it is important to be aware that it is going to happen. In other |
+contexts strings that follow the Javascript format for octal number |
+(leading zero) can be problematic but type conversion treats them |
+as base 10 anyway. However, strings that follow the format for |
+hexadecimal numbers (leading <code>0x</code> or <code>0X</code>) |
+are read as hexadecimal. Strings that cannot be read as a number |
+type-convert to <code>NaN</code>, which can be tested for with |
+the <code>isNaN</code> function. Strings representing numbers in an |
+exponential format (<code>"123e-2"</code>) are understood |
+along with leading minus signs. |
+</p> |
+ |
+<table> |
+ <caption>type-convert to number (+col) : Other Values.</caption> |
+ <thead> |
+ <tr> |
+ <th></th> |
+ <th class="un">undefined</th> |
+ <th class="ob">null</th> |
+ <th class="bl">true</th> |
+ <th class="bl">false</th> |
+ <th class="ob">new Object()</th> |
+ <th class="fn">function(){<br> return;<br>}</th> |
+ </tr> |
+ </thead> |
+ <tbody> |
+ <tr> |
+ <th>+col</th> |
+ <td class="nm">NaN</td> |
+ <td class="nm">0</td> |
+ <td class="nm">1</td> |
+ <td class="nm">0</td> |
+ <td class="nm">NaN</td> |
+ <td class="nm">NaN</td> |
+ </tr> |
+ </tbody> |
+</table> |
+ |
+<p id="tcNumber_7"> |
+Objects and functions always type-convert to <code>NaN</code> numbers, as do |
+<code>undefined</code> values but it is worth noting that <code>null</code> |
+type-converts to zero. Probably because it is being type-converted to boolean |
+first and then to number and, as is clear from the boolean results |
+above, <code>null</code> would become boolean <code>false</code> which |
+would then become numeric zero. There is almost no need to type convert |
+these types of values into numbers. How they convert is only really |
+relevant to a consideration of the accidental result of converting a |
+value that is expected to be a string but actually turns out to be one |
+of these (and/or performing an mathematical operation with one of these as an operand). |
+</p> |
+ |
+<h2 id="tcParse">Parsing to Number</h2> |
+ |
+<p id="tcParse_1"> |
+An alternative method of converting a string into a number is to use |
+one of the global functions designed to parse a string and return a |
+number. The <code>parseFloat</code> function accepts a string argument |
+and returns a floating point number resulting from parsing that string. |
+Non-string arguments are first type-converted to a string as described |
+above. |
+</p> |
+ |
+<p id="tcParse_2"> |
+The string parsing functions read the string character by character until |
+they encounter a character that cannot be part of the number, at which |
+point they stop and return a number based on the characters that they |
+have seen that can be part of the number. This feature of their action |
+can be usefully exploited, for example, given a string representing a |
+<span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> length value such as <code>"34.5em"</code> |
+<code>parseFloat</code> would be able to ignore the <code>"em"</code> |
+because those characters cannot be combined with the preceding set to |
+produce a valid number. The returned number would be 34.5, the numeric |
+part of the <span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> string stripped of its units. |
+</p> |
+ |
+<h3 id="tcParseFl">parseFloat</h3> |
+ |
+<table> |
+ <caption>parseFloat(col) : String Values.</caption> |
+ <thead> |
+ <tr> |
+ <th></th> |
+ <th class="st">""<br>(empty<br>string)</th> |
+ <th class="st">"-1.6"</th> |
+ <th class="st">"0"</th> |
+ <th class="st">"1"</th> |
+ <th class="st">"1.6"</th> |
+ <th class="st">"8"</th> |
+ <th class="st">"16"</th> |
+ <th class="st">"16.8"</th> |
+ <th class="st">"123e-2"</th> |
+ <th class="st">"010"<br>(Octal)</th> |
+ <th class="st">"0x10"<br>(Hex)</th> |
+ <th class="st">"0xFF"<br>(Hex)</th> |
+ <th class="st">"-010"</th> |
+ <th class="st">"-0x10"</th> |
+ <th class="st">"xx"</th> |
+ </tr> |
+ </thead> |
+ <tbody> |
+ <tr> |
+ <th>parseFloat(col)</th> |
+ <td class="nm">NaN</td> |
+ <td class="nm">-1.6</td> |
+ <td class="nm">0</td> |
+ <td class="nm">1</td> |
+ <td class="nm">1.6</td> |
+ <td class="nm">8</td> |
+ <td class="nm">16</td> |
+ <td class="nm">16.8</td> |
+ <td class="nm">1.23</td> |
+ <td class="nm">10</td> |
+ <td class="nm">0</td> |
+ <td class="nm">0</td> |
+ <td class="nm">-10</td> |
+ <td class="nm">0</td> |
+ <td class="nm">NaN</td> |
+ </tr> |
+ </tbody> |
+</table> |
+ |
+<p id="tcParseFl_1"> |
+With <code>parseFloat</code> empty strings return <code>NaN </code> |
+along with strings that cannot be subject to numeric interpretation. |
+The exponential format is understood and the leading zero in the |
+octal format does not hinder the string's interpretation as a |
+decimal number. Hexadecimal strings are interpreted as the number |
+zero because the following <code>"x"</code> cannot be |
+interpreted as part of a number so parsing stops after the leading zero. |
+</p> |
+ |
+<table> |
+ <caption>parseFloat(col) : Other Values.</caption> |
+ <thead> |
+ <tr> |
+ <th></th> |
+ <th class="un">undefined</th> |
+ <th class="ob">null</th> |
+ <th class="bl">true</th> |
+ <th class="bl">false</th> |
+ <th class="ob">new Object()</th> |
+ <th class="fn">function(){<br> return;<br>}</th> |
+ </tr> |
+ </thead> |
+ <tbody> |
+ <tr> |
+ <th>parseFloat(col)</th> |
+ <td class="nm">NaN</td> |
+ <td class="nm">NaN</td> |
+ <td class="nm">NaN</td> |
+ <td class="nm">NaN</td> |
+ <td class="nm">NaN</td> |
+ <td class="nm">NaN</td> |
+ </tr> |
+ </tbody> |
+</table> |
+ |
+<p id="tcParseFl_2"> |
+Non-string values are first converted into a string that is employed |
+by <code>parseFloat</code>. As that type-conversion to a string would |
+not normally result in a string that could be interpreted as a number |
+the result is <code>NaN</code>. Objects and functions may have custom |
+<code>toString</code> methods that may return strings that could be |
+interpreted as numbers but that would be an unusual requirement. |
+</p> |
+ |
+<h3 id="tcParseIn">parseInt</h3> |
+ |
+<p id="tcParseIn_1"> |
+The <code>parseInt</code> function works in a similar way to |
+<code>parseFloat</code> except that it is trying to interpret its |
+string argument into an integer and as a result recognises fewer |
+character as possible candidates to be part of that number. |
+</p> |
+ |
+<p id="tcParseIn_2"> |
+<code>parseInt</code> is occasionally used as a means of turning a |
+floating point number into an integer. It is very ill suited to that |
+task because if its argument is of numeric type it will first be |
+converted into a string and then parsed as a number, very inefficient. |
+This can produce very wrong results with numbers such as |
+<code>2e-200</code>, for which the next smaller integer is zero, but |
+with which <code>parseInt</code> returns <code>2</code>. Also, because |
+of the number format used by javascript, numbers are often represented |
+by near approximations. So, for example, 1/2 + 1/3 + 1/6 = |
+0.9999999999999999, which isn't quite one and parseInt would return |
+zero if asked to act on the result of the operation. |
+</p> |
+ |
+<p id="tcParseIn_3"> |
+For rounding |
+numbers to integers one of <code>Math.round</code>, <code>Math.ceil</code> |
+and <code>Math.floor</code> are preferable, and for a desired result |
+that can be expressed as a 32 bit signed integer the bitwise operation |
+described below might also suit. |
+</p> |
+ |
+<table> |
+ <caption>parseInt(col) : Numeric Values.</caption> |
+ <thead> |
+ <tr> |
+ <th></th> |
+ <th class="nm">-1.6</th> |
+ <th class="nm">-0</th> |
+ <th class="nm">+0</th> |
+ <th class="nm">1</th> |
+ <th class="nm">1.6</th> |
+ <th class="nm">8</th> |
+ <th class="nm">16</th> |
+ <th class="nm">16.8</th> |
+ <th class="nm">123e-2</th> |
+ <th class="nm">-Infinity</th> |
+ <th class="nm">+Infinity</th> |
+ <th class="nm">NaN</th> |
+ </tr> |
+ </thead> |
+ <tbody> |
+ <tr> |
+ <th>parseInt(col)</th> |
+ <td class="nm">-1</td> |
+ <td class="nm">0</td> |
+ <td class="nm">0</td> |
+ <td class="nm">1</td> |
+ <td class="nm">1</td> |
+ <td class="nm">8</td> |
+ <td class="nm">16</td> |
+ <td class="nm">16</td> |
+ <td class="nm">1</td> |
+ <td class="nm">NaN</td> |
+ <td class="nm">NaN</td> |
+ <td class="nm">NaN</td> |
+ </tr> |
+ </tbody> |
+</table> |
+ |
+<p id="tcParseIn_4"> |
+When it is acting on number the effect of the initial type-conversion |
+of the argument to a string is evident in the results. Note that the |
+value <code>123e-2</code> is internally the number <code>1.23</code> |
+and that type converts into the string <code>"1.23"</code>, |
+so that entry in the table above might look odd but it is correct. |
+</p> |
+ |
+<table> |
+ <caption>parseInt(col) : String Values.</caption> |
+ <thead> |
+ <tr> |
+ <th></th> |
+ <th class="st">""<br>(empty<br>string)</th> |
+ <th class="st">"-1.6"</th> |
+ <th class="st">"0"</th> |
+ <th class="st">"1"</th> |
+ <th class="st">"1.6"</th> |
+ <th class="st">"8"</th> |
+ <th class="st">"16"</th> |
+ <th class="st">"16.8"</th> |
+ <th class="st">"123e-2"</th> |
+ <th class="st">"010"<br>(Octal)</th> |
+ <th class="st">"0x10"<br>(Hex)</th> |
+ <th class="st">"0xFF"<br>(Hex)</th> |
+ <th class="st">"-010"</th> |
+ <th class="st">"-0x10"</th> |
+ <th class="st">"xx"</th> |
+ </tr> |
+ </thead> |
+ <tbody> |
+ <tr> |
+ <th>parseInt(col)</th> |
+ <td class="nm">NaN</td> |
+ <td class="nm">-1</td> |
+ <td class="nm">0</td> |
+ <td class="nm">1</td> |
+ <td class="nm">1</td> |
+ <td class="nm">8</td> |
+ <td class="nm">16</td> |
+ <td class="nm">16</td> |
+ <td class="nm">123</td> |
+ <td class="nm">8</td> |
+ <td class="nm">16</td> |
+ <td class="nm">255</td> |
+ <td class="nm">-8</td> |
+ <td class="nm">-16</td> |
+ <td class="nm">NaN</td> |
+ </tr> |
+ </tbody> |
+</table> |
+ |
+<p id="tcParseIn_5"> |
+Strings in octal and hexadecimal number formats do represent integers |
+and <code>parseInt</code> is capable of interpreting them in accordance |
+with the rules for Javascript source code, even when they have leading |
+minus signs. |
+</p> |
+ |
+<table> |
+ <caption>parseInt(col) : Other Values.</caption> |
+ <thead> |
+ <tr> |
+ <th></th> |
+ <th class="un">undefined</th> |
+ <th class="ob">null</th> |
+ <th class="bl">true</th> |
+ <th class="bl">false</th> |
+ <th class="ob">new Object()</th> |
+ <th class="fn">function(){<br> return;<br>}</th> |
+ </tr> |
+ </thead> |
+ <tbody> |
+ <tr> |
+ <th>parseInt(col)</th> |
+ <td class="nm">NaN</td> |
+ <td class="nm">NaN</td> |
+ <td class="nm">NaN</td> |
+ <td class="nm">NaN</td> |
+ <td class="nm">NaN</td> |
+ <td class="nm">NaN</td> |
+ </tr> |
+ </tbody> |
+</table> |
+ |
+<p id="tcParseIn_6"> |
+As <code>parseInt</code> type-converts its non-string arguments to |
+strings it always produces the same results for <code>boolean</code>, |
+<code>null</code>, <code>undefined</code>, object and function |
+arguments as <code>parseFloat</code> (assuming objects and functions |
+do not have custom <code>toString</code> methods). |
+</p> |
+ |
+<h3 id="tcPrIntRx">parseInt with a radix argument</h3> |
+ |
+<p id="tcPrIntRx_1"> |
+It is rarely desirable to allow <code>parseInt</code> to deduce the |
+base in which the number is represented from the string as leading zeros are |
+rarely intended to indicate data in octal format (particularly with |
+user input). To deal with this problem <code>parseInt</code> recognises |
+a second, radix, argument that can be used to specify the base in which the |
+string is to be interpreted. Specifying a second argument of 10 causes |
+<code>parseInt</code> to interpret the strings as only base 10. |
+</p> |
+ |
+<table> |
+ <caption>parseInt(col, 10) : String Values.</caption> |
+ <thead> |
+ <tr> |
+ <th></th> |
+ <th class="st">""<br>(empty<br>string)</th> |
+ <th class="st">"-1.6"</th> |
+ <th class="st">"0"</th> |
+ <th class="st">"1"</th> |
+ <th class="st">"1.6"</th> |
+ <th class="st">"8"</th> |
+ <th class="st">"16"</th> |
+ <th class="st">"16.8"</th> |
+ <th class="st">"123e-2"</th> |
+ <th class="st">"010"<br>(Octal)</th> |
+ <th class="st">"0x10"<br>(Hex)</th> |
+ <th class="st">"0xFF"<br>(Hex)</th> |
+ <th class="st">"-010"</th> |
+ <th class="st">"-0x10"</th> |
+ <th class="st">"xx"</th> |
+ </tr> |
+ </thead> |
+ <tbody> |
+ <tr> |
+ <th>parseInt(col, 10)</th> |
+ <td class="nm">NaN</td> |
+ <td class="nm">-1</td> |
+ <td class="nm">0</td> |
+ <td class="nm">1</td> |
+ <td class="nm">1</td> |
+ <td class="nm">8</td> |
+ <td class="nm">16</td> |
+ <td class="nm">16</td> |
+ <td class="nm">123</td> |
+ <td class="nm">10</td> |
+ <td class="nm">0</td> |
+ <td class="nm">0</td> |
+ <td class="nm">-10</td> |
+ <td class="nm">0</td> |
+ <td class="nm">NaN</td> |
+ </tr> |
+ </tbody> |
+</table> |
+ |
+<p id="tcPrIntRx_2"> |
+The string in octal format is now interpreted as base 10 and the |
+hexadecimal strings can now only be zero as parsing has to stop |
+when the <code>"x"</code> is encountered. |
+</p> |
+ |
+<p id="tcPrIntRx_3"> |
+Number bases 2 to 36 can be used with <code>parseInt</code>. The |
+following is base 16. |
+</p> |
+ |
+<table> |
+ <caption>parseInt(col, 16) : String Values.</caption> |
+ <thead> |
+ <tr> |
+ <th></th> |
+ <th class="st">""<br>(empty<br>string)</th> |
+ <th class="st">"-1.6"</th> |
+ <th class="st">"0"</th> |
+ <th class="st">"1"</th> |
+ <th class="st">"1.6"</th> |
+ <th class="st">"8"</th> |
+ <th class="st">"16"</th> |
+ <th class="st">"16.8"</th> |
+ <th class="st">"123e-2"</th> |
+ <th class="st">"010"<br>(Octal)</th> |
+ <th class="st">"0x10"<br>(Hex)</th> |
+ <th class="st">"0xFF"<br>(Hex)</th> |
+ <th class="st">"-010"</th> |
+ <th class="st">"-0x10"</th> |
+ <th class="st">"xx"</th> |
+ </tr> |
+ </thead> |
+ <tbody> |
+ <tr> |
+ <th>parseInt(col, 16)</th> |
+ <td class="nm">NaN</td> |
+ <td class="nm">-1</td> |
+ <td class="nm">0</td> |
+ <td class="nm">1</td> |
+ <td class="nm">1</td> |
+ <td class="nm">8</td> |
+ <td class="nm">22</td> |
+ <td class="nm">22</td> |
+ <td class="nm">4670</td> |
+ <td class="nm">16</td> |
+ <td class="nm">16</td> |
+ <td class="nm">255</td> |
+ <td class="nm">-16</td> |
+ <td class="nm">-16</td> |
+ <td class="nm">NaN</td> |
+ </tr> |
+ </tbody> |
+</table> |
+ |
+<p id="tcPrIntRx_4"> |
+The hexadecimal <code>0x</code> format is recognised again with the |
+base 16 interpretation. |
+</p> |
+ |
+<p id="tcPrIntRx_5"> |
+Finally base 3:- |
+</p> |
+ |
+<table> |
+ <caption>parseInt(col, 3) : Numeric Values.</caption> |
+ <thead> |
+ <tr> |
+ <th></th> |
+ <th class="nm">-1.6</th> |
+ <th class="nm">-0</th> |
+ <th class="nm">+0</th> |
+ <th class="nm">1</th> |
+ <th class="nm">1.6</th> |
+ <th class="nm">8</th> |
+ <th class="nm">16</th> |
+ <th class="nm">16.8</th> |
+ <th class="nm">123e-2</th> |
+ <th class="nm">-Infinity</th> |
+ <th class="nm">+Infinity</th> |
+ <th class="nm">NaN</th> |
+ </tr> |
+ </thead> |
+ <tbody> |
+ <tr> |
+ <th>parseInt(col, 3)</th> |
+ <td class="nm">-1</td> |
+ <td class="nm">0</td> |
+ <td class="nm">0</td> |
+ <td class="nm">1</td> |
+ <td class="nm">1</td> |
+ <td class="nm">NaN</td> |
+ <td class="nm">1</td> |
+ <td class="nm">1</td> |
+ <td class="nm">1</td> |
+ <td class="nm">NaN</td> |
+ <td class="nm">NaN</td> |
+ <td class="nm">NaN</td> |
+ </tr> |
+ </tbody> |
+</table> |
+ |
+<p id="tcPrIntRx_6"> |
+The consequences of the type-converting of numeric arguments to |
+strings is evident again. The number <code>8</code> is coming out |
+as <code>NaN</code> because the <code>"8"</code> character |
+cannot be interpreted as base 3, leaving an empty sequence of |
+acceptable characters and producing the same result as an empty string. |
+</p> |
+ |
+<table> |
+ <caption>parseInt(col, 3) : String Values.</caption> |
+ <thead> |
+ <tr> |
+ <th></th> |
+ <th class="st">""<br>(empty<br>string)</th> |
+ <th class="st">"-1.6"</th> |
+ <th class="st">"0"</th> |
+ <th class="st">"1"</th> |
+ <th class="st">"1.6"</th> |
+ <th class="st">"8"</th> |
+ <th class="st">"16"</th> |
+ <th class="st">"16.8"</th> |
+ <th class="st">"123e-2"</th> |
+ <th class="st">"010"<br>(Octal)</th> |
+ <th class="st">"0x10"<br>(Hex)</th> |
+ <th class="st">"0xFF"<br>(Hex)</th> |
+ <th class="st">"-010"</th> |
+ <th class="st">"-0x10"</th> |
+ <th class="st">"xx"</th> |
+ </tr> |
+ </thead> |
+ <tbody> |
+ <tr> |
+ <th>parseInt(col, 3)</th> |
+ <td class="nm">NaN</td> |
+ <td class="nm">-1</td> |
+ <td class="nm">0</td> |
+ <td class="nm">1</td> |
+ <td class="nm">1</td> |
+ <td class="nm">NaN</td> |
+ <td class="nm">1</td> |
+ <td class="nm">1</td> |
+ <td class="nm">5</td> |
+ <td class="nm">3</td> |
+ <td class="nm">0</td> |
+ <td class="nm">0</td> |
+ <td class="nm">-3</td> |
+ <td class="nm">0</td> |
+ <td class="nm">NaN</td> |
+ </tr> |
+ </tbody> |
+</table> |
+ |
+<h2 id="tcToInt32">ToInt32</h2> |
+ |
+<p id="tcToInt32_1"> |
+<code>ToInt32</code> is an <em>internal</em> function only available to the |
+Javascript implementation and cannot be called directly from scripts |
+in the way that <code>parseInt</code> can. It is a bit unusual to mention it in |
+connection with converting Javascript values to numbers but it can |
+be used in a limited set of circumstances. The bitwise operators such |
+as bitwise OR (<code>|</code>) and bitwise AND (<code>&</code>) operate on |
+numbers so they type-convert their operands to numbers. However, they |
+also only operate on 32 bit signed integers so given the (possibly |
+type-converted) numeric value they call the <em>internal</em> |
+<code>ToInt32</code> function with that number as its argument and |
+use the returned value as their operand. That returned value is always |
+a 32 bit signed integer. |
+</p> |
+ |
+<p id="tcToInt32_2"> |
+The effect can be like <code>parseInt</code> combined with type-converting |
+to numbers. While the result is limited in range to 32 bits, it is |
+<em>always</em> numeric and never <code>NaN</code>, or ± |
+<code>Infinity</code>. |
+</p> |
+ |
+<p id="tcToInt32_3"> |
+As with using mathematical operators in operations that have no effect on |
+the value of any resulting number it is possible to perform a bitwise |
+operation that will not affect the value returned from the call to |
+<code>ToInt32</code>. The tables below were generated using a bitwise |
+OR zero operation. |
+</p> |
+ |
+<table> |
+ <caption> ToInt32 (col|0) : Numeric Values.</caption> |
+ <thead> |
+ <tr> |
+ <th></th> |
+ <th class="nm">-1.6</th> |
+ <th class="nm">-0</th> |
+ <th class="nm">+0</th> |
+ <th class="nm">1</th> |
+ <th class="nm">1.6</th> |
+ <th class="nm">8</th> |
+ <th class="nm">16</th> |
+ <th class="nm">16.8</th> |
+ <th class="nm">123e-2</th> |
+ <th class="nm">-Infinity</th> |
+ <th class="nm">+Infinity</th> |
+ <th class="nm">NaN</th> |
+ </tr> |
+ </thead> |
+ <tbody> |
+ <tr> |
+ <th>col|0</th> |
+ <td class="nm">-1</td> |
+ <td class="nm">0</td> |
+ <td class="nm">0</td> |
+ <td class="nm">1</td> |
+ <td class="nm">1</td> |
+ <td class="nm">8</td> |
+ <td class="nm">16</td> |
+ <td class="nm">16</td> |
+ <td class="nm">1</td> |
+ <td class="nm">0</td> |
+ <td class="nm">0</td> |
+ <td class="nm">0</td> |
+ </tr> |
+ </tbody> |
+</table> |
+ |
+<p id="tcToInt32_4"> |
+<code>NaN</code> and ±<code>Infinity</code> become zero and |
+floating point values are <em>truncated</em> to integers. |
+</p> |
+ |
+<table> |
+ <caption> ToInt32 (col|0) : String Values.</caption> |
+ <thead> |
+ <tr> |
+ <th></th> |
+ <th class="st">""<br>(empty<br>string)</th> |
+ <th class="st">"-1.6"</th> |
+ <th class="st">"0"</th> |
+ <th class="st">"1"</th> |
+ <th class="st">"1.6"</th> |
+ <th class="st">"8"</th> |
+ <th class="st">"16"</th> |
+ <th class="st">"16.8"</th> |
+ <th class="st">"123e-2"</th> |
+ <th class="st">"010"<br>(Octal)</th> |
+ <th class="st">"0x10"<br>(Hex)</th> |
+ <th class="st">"0xFF"<br>(Hex)</th> |
+ <th class="st">"-010"</th> |
+ <th class="st">"-0x10"</th> |
+ <th class="st">"xx"</th> |
+ </tr> |
+ </thead> |
+ <tbody> |
+ <tr> |
+ <th>col|0</th> |
+ <td class="nm">0</td> |
+ <td class="nm">-1</td> |
+ <td class="nm">0</td> |
+ <td class="nm">1</td> |
+ <td class="nm">1</td> |
+ <td class="nm">8</td> |
+ <td class="nm">16</td> |
+ <td class="nm">16</td> |
+ <td class="nm">1</td> |
+ <td class="nm">10</td> |
+ <td class="nm">16</td> |
+ <td class="nm">255</td> |
+ <td class="nm">-10</td> |
+ <td class="nm">0</td> |
+ <td class="nm">0</td> |
+ </tr> |
+ </tbody> |
+</table> |
+ |
+<p id="tcToInt32_5"> |
+String values that would type-convert to <code>NaN</code> are returned |
+as zero from <code>ToInt32</code>. |
+</p> |
+ |
+<table> |
+ <caption> ToInt32 (col|0) : Other Values.</caption> |
+ <thead> |
+ <tr> |
+ <th></th> |
+ <th class="un">undefined</th> |
+ <th class="ob">null</th> |
+ <th class="bl">true</th> |
+ <th class="bl">false</th> |
+ <th class="ob">new Object()</th> |
+ <th class="fn">function(){<br> return;<br>}</th> |
+ </tr> |
+ </thead> |
+ <tbody> |
+ <tr> |
+ <th>col|0</th> |
+ <td class="nm">0</td> |
+ <td class="nm">0</td> |
+ <td class="nm">1</td> |
+ <td class="nm">0</td> |
+ <td class="nm">0</td> |
+ <td class="nm">0</td> |
+ </tr> |
+ </tbody> |
+</table> |
+ |
+<p id="tcToInt32_6"> |
+Even <code>undefined</code>, objects and functions are converted to zero value |
+numbers by this operation. Note though that boolean <code>true</code> is |
+converted to the number 1. |
+</p> |
+ |
+ |
+<h2 id="tcUserIn">Converting User Input</h2> |
+ |
+<p id="tcUserIn_1"> |
+Most of the mechanisms for getting input from the user, |
+<code><input type="text"></code> and |
+<code>prompt</code> for example, provide their results in the form |
+of strings. If the user is expected to input a number they still |
+might enter anything (at the least they may just make a typo). If |
+the string needs to be converted into a number for later operations |
+one of the methods mentioned above can be chosen based on what best |
+suits the nature of the input expected but some of the results |
+generated with erroneous input may be difficult to detect and handle. |
+</p> |
+ |
+<p id="tcUserIn_2"> |
+Prior to converting a string to a number it may be advantageous |
+to use a Regular Expression to test the contents of the string |
+to ensure that they conform to an acceptable format. That would |
+serve to eliminate some of the string values that may otherwise |
+suffer from the quirks of the string to number converting |
+processes when applied to unexpected string values. |
+</p> |
+ |
+ |
+<h3 id="tcRegEx">Regular expression examples</h3> |
+ |
+<pre id="tcRegExEm"> |
+/^\d+$/ <span class="commentJS">//All-digit</span> |
+/^\s*[-+]?\d+\s*$/ <span class="commentJS">//Unbroken Signed integer & spaces</span> |
+/^\d{1,5}$/ <span class="commentJS">//1 to 5 digits</span> |
+/^\d+\.\d\d$/ <span class="commentJS">//Money</span> |
+/^\d+(\.\d{2})$/ <span class="commentJS">//Money</span> |
+/^\d{1,3}(,\d\d\d)*\.\d\d$/ <span class="commentJS">//comma-separated money - 12,432.57</span> |
+ |
+ <span class="commentJS">// optional comma-separated money - 12,432.57 or 12432.57</span> |
+/^\d{1,3}(,\d\d\d)*\.\d\d$|^\d+\.\d\d$/ |
+ |
+</pre> |
+</body> |
+</html> |
/cljs/faq_notes/type_convert.html |
---|
Property changes: |
Added: svn:mime-type |
## -0,0 +1 ## |
+text/plain |
\ No newline at end of property |
Index: cljs/faq_notes/form_access.html |
=================================================================== |
--- cljs/faq_notes/form_access.html (nonexistent) |
+++ cljs/faq_notes/form_access.html (revision 43) |
@@ -0,0 +1,729 @@ |
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" |
+ "http://www.w3.org/TR/html4/strict.dtd"> |
+<html lang="en"> |
+<head><title>Referencing Forms and Form Controls</title> |
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> |
+<link href="../../faq.css" rel="stylesheet" type="text/css"> |
+<link href="../notes.css" rel="stylesheet" type="text/css"> |
+<style type="text/css"> |
+.commentJS { |
+ background-color: #FFFFCC; |
+ color: #004800; |
+} |
+P CODE { |
+ background-color: #FFFFDD; |
+ color: #000000; |
+ padding: 0ex; |
+ margin: 0ex; |
+} |
+</style> |
+</head> |
+<body> |
+ |
+<h1 id="faHead">Referencing Forms and Form Controls</h1> |
+<div id="faqNav"> |
+ <a href="../../">FAQ</a> > <a href="../">FAQ Notes</a> |
+</div> |
+ |
+<ul> |
+ <li><a href="#faInt">Introduction</a> |
+ <ul> |
+ <li><a href="#faInF">Forms</a></li> |
+ <li><a href="#faInC">Form Controls</a></li> |
+ </ul> |
+ </li> |
+ <li><a href="#faShrt">Shortcut Accessors</a></li> |
+ <li><a href="#faComMis">The Most Common Mistake</a></li> |
+ <li><a href="#faAnon">Anonymous Form References</a></li> |
+ <li><a href="#faBut">Radio Button and Other Control Collections</a></li> |
+ <li><a href="#faEff">Efficient use of Form Accessors</a></li> |
+</ul> |
+ |
+<h2 id="faInt">Introduction</h2> |
+<h3 id="faInF">Forms</h3> |
+<p id="faInF_1"> |
+When the W3C defined the <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> DOM specification much of what they included |
+represented a formalisation of existing browser behaviour. In particular they |
+defined "convenience" properties on the <code>HTMLDocument</code> |
+interface that reproduce document level collections common in preceding |
+browsers. Of specific interest here is the <code>document.forms</code> |
+collection, which makes all of the <code>FORM</code> elements on a page |
+available as (zero based) indexed members of the collection. Allowing, |
+for example, the second <code>FORM</code> element on a page to be |
+referenced as:- |
+</p> |
+ |
+<pre id="faInF_ex1"> |
+var formElement = document.forms[1]; |
+</pre> |
+ |
+<p id="faInF_2"> |
+<span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> (but not necessarily XHTML) <code>FORM</code> elements are allowed |
+<code>NAME</code> attributes and the <code>document.forms</code> collection |
+also makes <code>FORM</code> elements with <code>NAME</code> attributes |
+available as named members, under a property name that corresponds with |
+value of the <code>NAME</code> attribute. So given a form with the |
+attribute <code>name="myForm"</code> the form can be referenced |
+as:- |
+</p> |
+ |
+<pre id="faInF_ex2"> |
+var formElement = document.forms.myForm; |
+ |
+<span class="commentJS">/* - or - */</span> |
+ |
+var formElement = document.forms["myForm"]; |
+ |
+<span class="commentJS">/* The latter, bracket notation, does not impose the same restrictions |
+ on the character sequence used for the name as is imposed by the |
+ preceding dot notation, which is restricted to only using character |
+ sequences that would fulfill the ECMAScript definition of an |
+ identifier. |
+ |
+ Bracket notation is often preferred when accessing form elements as |
+ it helps to document itself by making it clear in the source code |
+ which property names originate in the <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> rather than the DOM. |
+*/</span> |
+</pre> |
+ |
+<p id="faInF_3"> |
+The <code>document.forms</code> collection had exhibited this behaviour |
+in all of the preceding browsers that implemented it (which included |
+all the browsers that understood what a form was) and as a result |
+represents the most cross-browser method of accessing <code>FORM</code> |
+elements. It is both W3C <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> DOM standard compliant and |
+back-compatible with pre-existing browsers. |
+</p> |
+ |
+<p id="faInF_4"> |
+The W3C went on to require <code>FORM</code> elements with |
+<code>ID</code> attributes to also be made available as named properties |
+of the <code>document.forms</code> collection. That represented a |
+formalisation of behaviour already exhibited in IE 4 but not by |
+Netscape 4 (and earlier). Referencing <code>ID</code>ed <code>FORM</code> |
+elements as named properties of the <code>document.forms</code> collection |
+should work reliably in all W3C <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> DOM compliant browsers but some |
+back-compatibility will be sacrificed if <code>ID</code>s are used instead |
+of <code>NAME</code>s (though not nearly as much as would be lost if |
+<code>ID</code>ed form elements were referenced using the |
+<code>document.getElementById</code> method). |
+</p> |
+ |
+<p id="faInF_5"> |
+When writing <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> that conforms to a DTD that allows <code>FORM</code> |
+elements to have <code>NAME</code> attributes it is possible to also give |
+the <code>FORM</code> element an <code>ID</code> attribute that corresponds |
+with its <code>NAME</code> attribute (so long as the <code>ID</code> is |
+unique on the page). The form will appear as a member of the |
+<code>document.forms</code> collection under a property name that |
+corresponds with the value of the <code>NAME</code> and <code>ID</code> |
+attributes (as they are identical). |
+</p> |
+ |
+<h3 id="faInC">Form Controls</h3> |
+ |
+<p id="faInC_1"> |
+Traditionally browsers that implemented the <code>document.forms</code> |
+collection also made the controls within a form available as a |
+collection accessible as a property of the <code>FORM</code> element |
+under the name <code>elements</code>. The W3C <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> DOM also formalised |
+this collection in the <code>HTMLFormElement</code> interface. |
+Controls within a form can be referenced as integer indexed members of |
+that collection:- |
+</p> |
+ |
+<pre id="faInC_ex1"> |
+var formElement = document.forms["myForm"]; |
+var controlElement = formElement.elements[2]; <span class="commentJS">//Third control in the form.</span> |
+</pre> |
+ |
+<p id="faInC_2"> |
+Controlls with <code>NAME</code> attributes are again made available as |
+named properties of the collection. So a control with |
+<code>name="myControl"</code> can be referenced as:- |
+</p> |
+ |
+<pre id="faInC_ex2"> |
+var controlElement = formElement.elements["myControl"]; |
+</pre> |
+ |
+<p id="faInC_3"> |
+Again the W3C also specified that controls with <code>ID</code> |
+attributes should be made available as named members of the |
+elements collection under their <code>ID</code>s (with the same |
+implications for back-compatibility with really ancient browsers). |
+All official (x)<span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> DTDs allow form controls to have |
+<code>NAME</code> attributes because without a <code>NAME</code> |
+attribute the value of the control cannot be sent as a |
+name/value pair when the form is submitted. |
+</p> |
+ |
+<h2 id="faShrt">Shortcut Accessors</h2> |
+ |
+<p id="faShrt_1"> |
+In addition to making named <code>FORM</code> elements available |
+as named properties of the <code>document.forms</code> collection |
+web browsers also make them available as named properties of the |
+<code>document</code> object. So:- |
+</p> |
+ |
+<pre id="faShrt_ex1"> |
+var formElement = document.myForm; |
+</pre> |
+ |
+<p id="faShrt_2"> |
+-will reference the same FORM element as:- |
+</p> |
+ |
+<pre id="faShrt_ex2"> |
+var formElement = document.forms.myForm; |
+</pre> |
+ |
+<p id="faShrt_3"> |
+And the same is true using bracket notation:- |
+</p> |
+ |
+<pre id="faShrt_ex3"> |
+var formElement = document["myForm"]; |
+ |
+<span class="commentJS">/* instead of:- */ </span> |
+ |
+var formElement = document.forms["myForm"]; |
+</pre> |
+ |
+<p id="faShrt_4"> |
+The W3C did not include this shortcut in the <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> DOM specifications |
+so code that uses it cannot be described as standards compliant and, |
+while nobody has been able to name an <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> browser where the shortcut |
+accessors do not work when referencing named <code>FORM</code> |
+elements, it would still be possible for a future standards compliant |
+browser not to support the shortcut accessors. |
+</p> |
+ |
+<p id="faShrt_5"> |
+<code>FORM</code> elements that only have <code>ID</code> attributes |
+cannot be accessed as properties of the <code>document</code> object |
+under a property name that corresponds with their <code>ID</code> |
+attributes. While by W3C <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> DOM specification (but not necessarily |
+on older browsers) <code>ID</code>ed forms are made available as named |
+properties of the <code>document.forms</code> collections under a |
+property name that corresponds with the <code>ID</code>. |
+</p> |
+ |
+<p id="faShrt_6"> |
+Form controls can be referenced as named properties of the |
+<code>FORM</code> element that contains them with a shortcut accessor:- |
+</p> |
+ |
+<pre id="faShrt_ex4"> |
+var formControl = formElement.myControl; |
+ |
+<span class="commentJS">/* instead of :- */</span> |
+ |
+var formControl = fromElement.elements.myControl; |
+</pre> |
+ |
+<p id="faShrt_7"> |
+But in the case of form controls, elements with <code>ID</code> |
+attributes may be available as properties of the <code>FORM</code> |
+element under a property name that corresponds with their |
+<code>ID</code> (at least on more recent browsers). |
+</p> |
+ |
+<p id="faShrt_8"> |
+The main argument in favour of using shortcut accessors (apart from the |
+reduced amount of typing) is that they are resolved fractionally quicker |
+than accessors that employ the <code>forms</code> and |
+<code>elements</code> collections. That follows from the fact that fewer |
+object references need to be resolved before the reference to the element |
+of interest is returned. |
+</p> |
+ |
+<p id="faShrt_9"> |
+While arguments against the shortcut accessors point out that named image, |
+embed and other elements are also made available as named properties of |
+the <code>document</code> object, making it ambiguous when reading the |
+source code whether the shortcut accessor is referring to a form or some |
+other named property of the <code>document</code> object. When a |
+<code>FORM</code> element is referenced as a member of the |
+<code>document.forms</code> collection, or a control as a member of the |
+<code>elements</code> collection, there can be no doubt while reading |
+the source code as to the type of element that is the subject of the |
+reference. |
+</p> |
+ |
+<h2 id="faComMis">The Most Common Mistake</h2> |
+ |
+<p id="faComMis_1"> |
+The most common mistake made when defining the form <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> that a script |
+will interact with follows from the existence of the shortcut accessors |
+for form controls. It is to give the control a <code>NAME</code> (or |
+possibly <code>ID</code>) that corresponds with an existing property of |
+<code>FORM</code> elements. And the most common example of that is an |
+<code>INPUT</code> element of <code>type="submit"</code> with |
+the <code>NAME</code> "submit". Because the named controls are |
+made available as named properties of the <code>FORM</code> element this |
+<code>INPUT</code> element is made available under the property name |
+<code>"submit"</code>. Unfortunately <code>FORM</code> elements |
+already have a property with the name <code>"submit"</code>, it |
+is the <code>submit</code> method that can be used to submit the form |
+with a script. The misguided choice of name for the <code>INPUT</code> |
+element effectively renders the form's <code>submit</code> method |
+unscriptable. And the same is true for all controls with names that |
+correspond any with existing <code>FORM</code> element properties. |
+</p> |
+ |
+<p id="faComMis_2"> |
+Because ECMAScript is case sensitive it may only be necessary to |
+capitalise the name of the <code>INPUT</code> element to avoid the |
+conflict. However, it would probably be safest to adopt a naming |
+convention for form controls that ensured that they do not |
+correspond with existing properties of the <code>FORM</code> elements |
+regardless of case. Especially as theW3C <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> specification implies |
+that referring to named properties of collections can be case |
+insensitive in <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> DOMs. In reality I don't know of any |
+implementations in which it is but it would be better to err on the side |
+of caution. |
+</p> |
+ |
+<p id="faComMis_3"> |
+Another naming conflict that should be avoided would arise if named form |
+controls had names that correspond with the string representations of |
+integers, such as <code>name="1"</code> as they will almost |
+certainly result in inconsistent results (across browsers) when being |
+referenced because the controls are already available by integer index |
+and it would become unclear which control was being referenced by |
+accessors such as <code>formElement.elements[1]</code> or |
+<code>formElement.elements["1"]</code> (by the ECMAScript |
+specification the preceding two property accessors are equivalent). |
+Unless the control with the name <code>"1"</code> also |
+happened to be the control with the index 1. This problem would also |
+apply to <code>FORM</code> elements within the <code>document.forms</code> |
+collection. |
+</p> |
+ |
+<p id="faComMis_4"> |
+Generally it is best to only give form controls names that cannot |
+conflict with existing properties of <code>FORM</code> elements |
+and <code>FORM</code> elements names that cannot conflict with |
+existing properties of the <code>document.forms</code> collection |
+or the <code>document</code> object. |
+</p> |
+ |
+<h2 id="faAnon">Anonymous Form References</h2> |
+ |
+<p id="faAnon_1"> |
+Because <code>FORM</code> elements are available as integer indexed |
+member of the <code>document.forms</code> collection it is not |
+necessary to know the name of a form (or for the form to have a |
+name) to acquire a reference to it. While being able to refer to a |
+form anonymously with its index might seem like a good approach |
+towards making more general/flexible functions for form validation |
+and the like, in practice referring to forms by their index actually |
+makes code less flexible and harder to maintain. As soon as the |
+number or layout of forms on a page is changed their indexes also |
+change, requiring that all of the references by index be located |
+and altered. |
+</p> |
+ |
+<p id="faAnon_2"> |
+One method of avoiding having to know the name of a <code>FORM</code> |
+element within a function that is to act upon a form is to pass a |
+reference to the form object as an argument in the function call. |
+This is easiest achieved from the code provided as the value for |
+an event handling attribute because that code is used by the browser |
+to create an event handling function that is assigned as a method of |
+the element to which it is attached. And in any function executed as |
+a method of an object the <code>this</code> keyword is a reference to |
+the object with which the execution of the method is associated. The |
+most common need to anonymously pass a reference to a <code>FORM</code> |
+element is as an argument to a form validation function in the |
+onsubmit handler of the form. In that case the event handling function |
+is a method of the <code>FORM</code> element so the <code>this</code> |
+keyword refers to the form directly:- |
+</p> |
+ |
+<pre id="faAnon_ex1"> |
+function validateForm(formRef){ |
+ <span class="commentJS">/* Use the reference to the form passed as the formal |
+ parameter - formRef - to acquire a reference to the form |
+ control with the name "textField": |
+ */</span> |
+ var el = formRef && formRef.elements["textField"]; |
+ <span class="commentJS">/* If the control reference exists return its value property |
+ converted to a boolean value (false if empty, true otherwise) |
+ else return true so the form is submitted and the server can |
+ do the validation: |
+ */</span> |
+ return !el || Boolean(el.value); |
+} |
+... |
+<form action="http://example.com/somePage.asp" |
+ onsubmit="return validateForm(this);"> |
+ <input type="text" name="textField" value=""> |
+ <input type="submit" name="Submit_Button" value="Submit"> |
+</form> |
+</pre> |
+ |
+<p id="faAnon_3"> |
+Because the above function receives the reference to the form that it |
+is to validate as an argument when it is called it can also be used |
+with any number of other forms. Although in the case of the above |
+function each of those forms would need to contain a field called |
+<code>"textField"</code>, but that string name could also be |
+passed as an argument, making the function more general. |
+</p> |
+ |
+<p id="faAnon_4"> |
+Form control objects all have a property named |
+<code>"form"</code> that holds a reference to the |
+<code>FORM</code> element that contains them. As a result the event |
+handling functions attached to form controls can pass an anonymous |
+reference to the form that contains them as <code>this.form</code>. |
+Obviously a function called from an event handler on a control can |
+be passes an anonymous reference to the control itself as |
+<code>this</code>. |
+</p> |
+ |
+<h2 id="faBut">Radio Button and Other Control Collections</h2> |
+ |
+<p id="faBut_1"> |
+Radio button controls work to provide a selection of one item of many |
+when each of the radio button alternatives has the same |
+<code>NAME</code> attribute. But it is also possible to give other types |
+of control the same <code>NAME</code> attribute. |
+</p> |
+ |
+<p id="faBut_2"> |
+When controls that share the same <code>NAME</code> attribute they can |
+still be accessed as integer indexed members of the <code>FORM</code> |
+element's <code>elements</code> collection but when the member of the |
+<code>elements</code> collection is accessed using the <code>NAME</code> |
+attribute value as a property name browsers return a collection all of |
+the elements with the corresponding NAME attributes. So given the <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span>:- |
+</p> |
+ |
+<pre id="faBut_ex1"> |
+<form name="testForm" action="http://example.com/somePage.jsp"> |
+ <ul style="list-style-type:none;"> |
+ <li><input type="radio" name="radioSet" value="R1">option 1</li> |
+ <li><input type="radio" name="radioSet" value="R2">option 2</li> |
+ <li><input type="radio" name="radioSet" value="R3">option 3</li> |
+ <li><input type="radio" name="radioSet" value="R4">option 4</li> |
+ </ul> |
+ <input type="submit" name="Submit_Button" value="Send"> |
+</form> |
+</pre> |
+ |
+<p id="faBut_3"> |
+A property accessor referring to the member of the <code>FORM</code>'s |
+<code>elements</code> collection by the property name |
+<code>"radioSet"</code> will not return a reference to any one |
+of the named radio buttons but instead returns a collection of all of the |
+like-named radio controls. |
+</p> |
+ |
+<p id="faBut_4"> |
+The individual radio buttons within that collection are referred to as |
+integer indexed members of that collection. So to find the button that |
+is checked one might loop through all of the members of the collection |
+of like-named radio buttons and copy a reference to the button with its |
+<code>checked</code> property set to boolean <code>true</code>. |
+</p> |
+ |
+ |
+<pre id="faBut_ex2"> |
+var radioCollection, checkedButton; |
+var frm = document.forms["testForm"]; |
+if(frm){ |
+ radioCollection = frm.elements["radioSet"]; |
+ if(radioCollection){ |
+ <span class="commentJS">/* The collection of like-named radio buttons has a length |
+ property and that is used to limit a for loop:- |
+ */</span> |
+ for(var c = 0;c < radioCollection.length;c++){ |
+ <span class="commentJS">/* The individual radio buttons are accessed as indexed |
+ members of the collection using the loop counter - c |
+ - from the for loop: |
+ */</span> |
+ if(radioCollection[c].checked){ |
+ <span class="commentJS">/* When a radio button element is found with its |
+ checked property set to boolean true a reference |
+ to that element is assigned to the local variable |
+ - checkedButton - and the loop is terminated with |
+ the - break - statement as only one button in a set |
+ of like-named radio buttons will be checked at a |
+ time, so any remaining buttons in the collection |
+ must have checked properties set to false: |
+ */</span> |
+ checkedButton = radioCollection[c]; |
+ break; |
+ } |
+ } |
+ if(checkedButton){ |
+ <span class="commentJS">/* Do something with the reference to the checked radio |
+ button (if any). |
+ */</span> |
+ ... |
+ } |
+ } |
+} |
+</pre> |
+ |
+<p id="faBut_5"> |
+It is not unusual when a form is generated by a server-side script that |
+some forms may have one or more like-named controls. If there is only |
+one control inserted in the form then accessing a member of the |
+<code>elements</code> collection with its name will return a reference |
+to that one control, but if there are multiple elements the returned |
+reference will be to a collection of such controls. While it may be |
+possible to branch client-side code that wants to interact with those |
+controls to handle the two alternatives it adds an unnecessary |
+maintenance burden to do so. |
+</p> |
+ |
+<p id="faBut_6"> |
+As handling a returned collection usually involves looping through that |
+collection the simplest way of implementing the client-side code to |
+deal with both collections and individual controls being returned by |
+named properties of the <code>elements</code> collection is to |
+normalise the references to individual controls so that they can be |
+handled as if they were a collection. Looping through a collection |
+involves using the <code>length</code> property of the collection to |
+limit the loop statement and accessing the controls within the |
+collection by integer index. This is exactly the way in which code |
+would loop through the elements of an <code>Array</code>. To allow a |
+script to handle both possibilities with the same code the return of |
+a reference to an individual control could be detected and then that |
+reference used to create a one-element <code>Array</code>. Subsequent |
+code would then treat the <code>Array</code> as if it was a collection and |
+loop through it, but as there is only one element the loop body would |
+only be executed once. |
+</p> |
+ |
+<pre id="faBut_ex3"> |
+var radioCollection, checkedButton; |
+var frm = document.forms["testForm"]; |
+if(frm){ |
+ radioCollection = frm.elements["radioSet"]; |
+ if(radioCollection){ |
+ <span class="commentJS">/* But the returned reference might not be a collection in this |
+ case. Instead it may be a reference to just one control if |
+ there is only one in this form with the name "radioSet". |
+ If it is a reference to an individual control it is going to |
+ be necessary to normalise it. Because radio button controls |
+ do not have - length - properties and collections do that is |
+ the property that is going to be tested: |
+ */</span> |
+ if(typeof radioCollection.length != "number"){ |
+ <span class="commentJS">/* The length property is not a number so this cannot be a |
+ collection and must be normalised so that the following |
+ loop statement can handle it correctly. Normalisation is |
+ done by making a reference to the control currently |
+ referred to by the - radioCollection - local variable |
+ into the first (and only) element of a new Array object |
+ and then assigning a reference to that array to the - |
+ radioCollection - local variable: |
+ */</span> |
+ radioCollection = [radioCollection]; |
+ } |
+ <span class="commentJS">/* The execution of the body of the - for - loop is limited by |
+ the - length - property of the collection/Array. |
+ */</span> |
+ for(var c = 0;c < radioCollection.length;c++){ |
+ <span class="commentJS">/* The individual radio buttons are accessed as indexed |
+ members of the collection/Array using the loop counter |
+ - c - from the for loop: |
+ */</span> |
+ if(radioCollection[c].cheked){ |
+ checkedButton = radioCollection[c]; |
+ break; |
+ } |
+ } |
+ if(checkedButton){ |
+ <span class="commentJS">/* do something with the reference to the checked radio |
+ button (if any). |
+ */</span> |
+ ... |
+ } |
+ } |
+} |
+</pre> |
+ |
+<p id="faBut_7"> |
+While it is normal for radio button controls to be like-named it is |
+also possible for all other controls to be included in a form with |
+multiple controls of the same type and like names. The same |
+referencing techniques can be used with any type of control (even |
+mixed types with like-names, though that is almost never done). But |
+deciding whether a reference needs to be normalised by making it into |
+the only element of an <code>Array</code> by testing the |
+<code>length</code> property of that reference to see if it doesn't |
+exist will not work with <code>SELECT</code> elements as they have |
+a <code>length</code> property of their own. It also would not help |
+to be testing some other characteristic of a collection, such as their |
+<code>item</code> method, as <code>SELECT</code> elements usually have |
+all of the methods and properties of a collection as they are |
+implemented as collections of <code>OPTION</code> elements. |
+</p> |
+ |
+<p id="faBut_8"> |
+Turning the problem around and testing a returned reference to see if |
+it has the characteristics of a <code>SELECT</code> element (such as |
+an <code>options</code> property) would be better but some collection |
+implementations have all of the properties and methods of the first |
+element within that collection as well as the properties and methods |
+of a collection (e.g. on IceBrowser 5). That means that it would not be |
+easy to distinguish a collection of <code>SELECT</code> controls |
+from an individual <code>SELECT</code> control. However, a more |
+elaborate test might go:- |
+</p> |
+ |
+<pre id="faBut_ex4"> |
+<span class="commentJS">/* Normalise a reference that may be an individual from control |
+ (including SELECT elements) or may be a collection of controls |
+ into a form that can be handled in a - for - loop controlled with |
+ its - length - property and accessed by integer index. |
+*/</span> |
+if((typeof contrlCollection.length != "number")|| <span class="commentJS">//no length propety:</span> |
+ <span class="commentJS">/* or:- */</span> |
+ ((contrlCollection.options)&& <span class="commentJS">//it has an options colleciton and:</span> |
+ ((!contrlCollection[0])|| <span class="commentJS">//no object at index 0 - not a collection</span> |
+ <span class="commentJS">/* or:- */</span> |
+ (contrlCollection[0] == contrlCollection.options[0])))){ |
+ <span class="commentJS">/* The object at index 0 in contrlCollection is the same object |
+ as is at index 0 in contrlCollection.options so this must be |
+ an individual SELECT element not a collection of them because a |
+ collection of SELECT elements would not have an OPTION element |
+ at index zero. |
+ */</span> |
+ contrlCollection = [contrlCollection]; |
+} |
+</pre> |
+ |
+<h2 id="faEff">Efficient use of Form Accessors</h2> |
+ |
+<p id="faEff_1"> |
+Code that interacts with <code>FORM</code> elements and controls |
+through the <code>document.forms</code> collection and the form's |
+<code>elements</code> collection usually does not do enough work to |
+make the efficiency of the code significant. But with large forms with |
+many controls an inefficiently coded validation function (or some other |
+interaction, like keeping running totals) can negatively impact on the |
+user's experience. It can also be argued that considering the |
+efficiency of implementation is a worthwhile habit even when it would |
+make no perceivable difference. |
+</p> |
+ |
+<p id="faEff_2"> |
+A significant aspect of the efficient coding of form interacting code |
+is the re-resolving of references to various objects. This trivial |
+example code copies the values of 5 <code>INPUT</code> elements to |
+local variables:- |
+</p> |
+ |
+<pre id="faEff_ex1"> |
+var txt1 = document.forms["formName"].elements["field1"].value; |
+var txt2 = document.forms["formName"].elements["field2"].value; |
+var txt3 = document.forms["formName"].elements["field3"].value; |
+var txt4 = document.forms["formName"].elements["field4"].value; |
+var txt5 = document.forms["formName"].elements["field5"].value; |
+</pre> |
+ |
+<p id="faEff_3"> |
+The shortcut accessors require the resolution of fewer object |
+references:- |
+</p> |
+ |
+<pre id="faEff_ex2"> |
+var txt1 = document["formName"]["field1"].value; |
+var txt2 = document["formName"]["field2"].value; |
+var txt3 = document["formName"]["field3"].value; |
+var txt4 = document["formName"]["field4"].value; |
+var txt5 = document["formName"]["field5"].value; |
+</pre> |
+ |
+<p id="faEff_4"> |
+But their use still involves re-resolving the form object each time a |
+form control is accessed. It would be unnecessary to re-resolve this |
+reference if a reference to the form was assigned to a local variable:- |
+</p> |
+ |
+<pre id="faEff_ex3"> |
+<span class="commentJS">/* Assign a reference to the form object to the local variable - frm - |
+ and then make subsequent control references relative to that local |
+ variable: |
+*/</span> |
+var frm = document.forms["formName"]; |
+var txt1 = frm.elements["field1"].value; |
+var txt2 = frm.elements["field2"].value; |
+var txt3 = frm.elements["field3"].value; |
+var txt4 = frm.elements["field4"].value; |
+var txt5 = frm.elements["field5"].value; |
+</pre> |
+ |
+<p id="faEff_5"> |
+The effect would be much the same as when a reference to the form |
+object has been passed to a function and control references are |
+accessed relative to the parameter holding the form reference. |
+</p> |
+ |
+<p id="faEff_6"> |
+It is still not optimum to be re-resolving the <code>elements</code> |
+collection, and it is practical to assign a reference to that object |
+to a local variable instead of a reference to the form object:- |
+</p> |
+ |
+<pre id="faEff_ex4"> |
+<span class="commentJS">/* Assign a reference to the form's elements collection to the local |
+ variable - frmEls - and then make subsequent control references |
+ relative to that local variable: |
+*/</span> |
+var frmEls = document.forms["formName"].elements; |
+var txt1 = frmEls["field1"].value; |
+var txt2 = frmEls["field2"].value; |
+var txt3 = frmEls["field3"].value; |
+var txt4 = frmEls["field4"].value; |
+var txt5 = frmEls["field5"].value; |
+</pre> |
+ |
+<p id="faEff_7"> |
+With the original long form accessor the resolution starts with |
+resolving the <code>"document"</code> identifier. The |
+identifier is first looked for among the local variables of the |
+function (as a named property of the internal |
+"Variable" object, by ECMA specification), |
+when it is not found the scope chain is searched, object by object |
+down the chain, for a property with the corresponding name. When the |
+scope resolution for <code>"document"</code> gets to the |
+global object (at the end of the scope chain) it will find a property |
+called <code>"document"</code>, a reference to the |
+<code>document</code> object, and the first identifier in the accessor |
+will have been resolved. The next identifier is |
+<code>"forms"</code> and that is located as a property of the |
+<code>document</code>. Then the <code>"formName"</code> |
+property is identified in the <code>forms</code> collection. Next the |
+<code>"elements"</code> property of the form is located, |
+followed by the control name in that object and finally the |
+<code>"value"</code> property of the control is returned. |
+</p> |
+ |
+<p id="faEff_8"> |
+When a reference to the <code>elements</code> collection is assigned to |
+a local variable the first identifier in the property accessor is |
+identified as that local variable, the control name is identified as a |
+property of the <code>elements</code> collection referenced and the |
+<code>"value"</code> property of the control is returned. |
+</p> |
+ |
+<p id="faEff_9"> |
+Obviously there is a big difference in the amount of work involved in |
+acquiring the <code>value</code> of the control in each case. That |
+difference will not be that significant if only a couple of values are |
+accessed, but even with as few as half a dozen control property |
+accesses the second approach will obviously be much more efficient, |
+and with increasing numbers of controls the difference could easily |
+become apparent to the user. |
+</p> |
+</body> |
+</html> |
/cljs/faq_notes/form_access.html |
---|
Property changes: |
Added: svn:mime-type |
## -0,0 +1 ## |
+text/plain |
\ No newline at end of property |
Index: cljs/faq_notes/not_browser_detect.html |
=================================================================== |
--- cljs/faq_notes/not_browser_detect.html (nonexistent) |
+++ cljs/faq_notes/not_browser_detect.html (revision 43) |
@@ -0,0 +1,1413 @@ |
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" |
+ "http://www.w3.org/TR/html4/strict.dtd"> |
+<html lang="en"> |
+<head> |
+<title>Browser Detection (and What to Do Instead)</title> |
+<meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> |
+<link href="../../faq.css" rel="stylesheet" type="text/css"> |
+<link href="../notes.css" rel="stylesheet" type="text/css"> |
+<style type="text/css"> |
+.tip { |
+ border: 1px solid #ccc; |
+ padding: 1ex; |
+ background: #fcfcfc; |
+ font-size: 90%; |
+} |
+</style> |
+</head> |
+<body> |
+<h1 id="bdTop">Browser Detection (and What to Do Instead)</h1> |
+<div id="faqNav"> |
+ <a href="../../">FAQ</a> > <a href="../">FAQ Notes</a> |
+</div> |
+<p> |
+By Richard Cornford, edited by Garrett Smith |
+</p> |
+<ul> |
+ <li><a href="#bdIntro">Introduction</a></li> |
+ <li><a href="#bdValid">Avoiding Structural Differences in the Browser DOMs</a></li> |
+ <li><a href="#bdDif">Browsers Differences</a></li> |
+ <li><a href="#bdFailS">Failed Strategies: Browser Detecting</a> |
+ <ul> |
+ <li><a href="#bdUAS">Assumptions Based on navigator.userAgent</a></li> |
+ <li><a href="#bdOI">Assumptions Based on DOM Objects: Object inference</a></li> |
+ </ul> |
+ </li> |
+ <li><a href="#bdFD">A Strategy That Works: Object/Feature Detecting.</a> |
+ <ul> |
+ <li><a href="#bdGEID">Example 1: IDed Element Retrieval</a></li> |
+ <li><a href="#bdScroll">Example 2: Scroll Values</a></li> |
+ <li><a href="#bdReplace">Example 3: String.prototype.replace</a></li> |
+ </ul> |
+ </li> |
+ <li><a href="#bdDesPb">The Javascript Design Problem</a></li> |
+ |
+</ul> |
+ |
+<h2 id="bdIntro">Introduction</h2> |
+ |
+<p id="bdIntro_1"> |
+Under normal circumstances computer programs are written for a known |
+environment. The programmer knows what to expect; which APIs will be |
+available, how they will behave and so on. Java is an ideal example |
+of this, providing a theoretically consistent set of APIs and language |
+features across all Java Virtual Machine (JVM) implementations. But |
+this is also true in most other circumstances. The programmer of C++ |
+for the Windows operating system will know what MFC classes are |
+available and how to program them. Their expectations will be |
+rewarded, if they posses the required knowledge. |
+</p> |
+ |
+<p id="bdIntro_2"> |
+Client side javascript for the Internet, on the other hand, has the |
+possibly unique problem of having to be authored with no specific |
+knowledge of the environment in which it will be executed. The |
+client side environment will usually be a web browser and web |
+browsers do tend to have many common features (and increasingly |
+standardised features) but the author cannot know which version of |
+which browser will be making any HTTP request (or |
+whether it is a browser at all). It is not even possible to tell in |
+advance whether the User Agent will be capable of executing |
+javascript; all of those that can include a user configurable option |
+to disable it anyway. |
+</p> |
+ |
+<p id="bdIntro_3"> |
+Javascript authors for the Internet must realise that they are dealing |
+with the unknown and that any, and all, scripts will fail to execute |
+somewhere. The challenge is to get the most from your javascript when |
+it is available but to cope with their failure in a meaningful way. |
+</p> |
+ |
+<p id="bdIntro_4"> |
+I once read a description of a variant on the game of chess, played |
+at military academies. Two players sit at separate boards set up with |
+only their own pieces, out of sight of each other, and a referee |
+supervises the game. Each player makes their move in turn and the |
+referee is responsible for informing them how the other's move impacts |
+on their own pieces and how the other's disposition of pieces impact |
+on their intended move. The player is informed only when one of their |
+own pieces is taken, when one of their moves is affected by |
+interacting with one of their opponents pieces (i.e. a player may want |
+to move a bishop across the board but the referee may inform them that |
+their move was stopped four squares early when the bishop took a pawn |
+from the other side) and when one of their opponents pieces is |
+sitting on a square adjacent to one of their own. |
+</p> |
+ |
+<p id="bdIntro_5"> |
+The game is used to teach battlefield strategy. To win a player must |
+probe and test to deduce his opponent's disposition, while planing and |
+executing a response that will achieve the desired checkmate. It is |
+this sort of strategy that needs to be added to the normal programming |
+problems in order that javascript may cope with its unknown execution |
+environment, with the significant difference that the strategy, its |
+tests and all of the paths of execution must be fully planed out before |
+the code can even starts executing. |
+</p> |
+ |
+<h2 id="bdValid">Avoiding Structural Differences in the Browser DOMs</h2> |
+ |
+<p id="bdValid_1"> |
+While the point of this article is to introduce techniques for handling |
+the differences between web browsers and their DOM implementations it |
+is also possible to avoid some types of differences especially related |
+to the structure of the DOM that is being scripted. |
+</p> |
+ |
+<p id="bdValid_2"> |
+If I was asked to recommend one action most likely to promote the |
+authoring of cross-browser scripts it would be: <strong><em>Start |
+from a basis of valid <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language" |
+>HTML</abbr></span></em></strong>.</p> |
+ |
+<p id="bdValid_3"> |
+Browsers presented with invalid <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> will usually attempt to error |
+correct it in order to do the best possible job of displaying it. |
+Some browsers, particularly IE, are tolerant of all sorts of strange |
+formulations of mark-up. Valid <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> has a tree-like structure, elements |
+may completely contain others but they cannot overlap, and there are |
+rules about which elements may appear in which contexts. The DOM that |
+is to be scripted also has a tree-like structure and there is a very |
+simple relationship between the tree-like structure of valid <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> and |
+the DOM constructed from it. So any browser presented with valid <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> |
+will be able to directly translate that <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> into a corresponding DOM |
+using well specified rules, resulting in a DOM that is of predictable |
+and consistent structure on all of the browsers that can build a DOM. |
+</p> |
+ |
+<p id="bdValid_4"> |
+Invalid <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> will not translate as naturally into a DOM, or even a |
+tree-like structure. If the browser is going to build a DOM with the |
+source provided it is going to have to apply error correcting rules |
+and attempt to build the best DOM it can. But the error correcting |
+rules are not standardised, not even published. So different browsers |
+have no choice but apply different rules and that directly results in |
+the building of DOMs with different (and in extremes, radically |
+different) structures. |
+</p> |
+ |
+<p id="bdValid_5"> |
+As a result, using invalid <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> directly produces differences in the |
+DOMs produced by different browsers. No matter how good the application |
+of techniques for dealing with the differences between browsers, it |
+does not make sense to do anything that will provoke more differences |
+than are unavoidable. |
+</p> |
+ |
+<p id="bdValid_6"> |
+The authoring of invalid <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span>, justified because "It works in |
+browser XYZ", gives the authors of accompanying scripts the |
+impression that cross-browser scripting is harder than it is. If they |
+had started with valid <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> they would never have encountered any of |
+the structural inconsistencies that invalid <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> can provoke. |
+</p> |
+ |
+<h2 id="bdDif">Browsers Differences</h2> |
+ |
+<p id="bdDif_1"> |
+As browsers have evolved they have offered more features to javascript. |
+Different manufactures have adopted the features of other browsers, |
+while adding new features, that may in turn have been adopted by (or |
+will be adopted by) their competitors. Various sets of common features |
+have emerged and some have been formalised by the W3C into a sequence |
+of standard DOM specifications. Along the way an increasing number of |
+javascript capable browsers have emerged. In addition to desktop PC |
+browsers, javascript capable browsers exist for a whole range of |
+devices; PDAs, mobile telephones (Microwave ovens, refrigerators). |
+</p> |
+ |
+<p id="bdDif_2"> |
+Unfortunately it is the case that the browsers on the smaller devices |
+cannot offer the range of features available to a desktop PC and even |
+as the technology improves and features are added to the smaller |
+browsers the problem will not improve as browsers will become available |
+on a wider range of devices while the desktop PC browsers will continue |
+to march ahead of them. |
+</p> |
+ |
+<p id="bdDif_3"> |
+Over the years various strategies have been attempted to tackle this |
+problem and some have failed miserably. |
+</p> |
+ |
+<h2 id="bdFailS">Failed Strategies: Browser Detecting</h2> |
+ |
+<h3 id="bdUAS">Assumptions Based on navigator.userAgent</h3> |
+ |
+<p id="bdUAS_1"> |
+One of the most popular strategies for handling the differences between |
+web browsers was browser detecting based on the User Agent string. |
+Browsers possessing a <code>navigator</code> object also provide a |
+property of that object: <code>navigator.userAgent</code> containing a |
+string that (in theory) identifies the browser. Its application went |
+something like:- |
+</p> |
+ |
+<pre id="bdUAS_ex1"> |
+<span class="commentJS">/* Warning: never use this script, or any script based on, or resembling, it. |
+*/</span> |
+var userAgent = self.navigator.userAgent; |
+var appName = self.navigator.appName; |
+var isOpera = false; |
+var isOpera5 = false; |
+var isOpera6p = false; |
+var isIE = false; |
+var isIE4 = false; |
+var isIE5p = false; |
+var isMozilla1p = false; |
+var isNet4 = false; |
+var isNet5p = false; |
+var operaVersion = 0; |
+var ieVersion = 0; |
+var appVersion = self.navigator.appVersion; |
+var brSet = false; |
+ |
+function brSetup(){ |
+ for(var c = 3;c < appVersion.length;c++){ |
+ var chr = appVersion.charAt(c); |
+ if(isNaN(chr)){ |
+ appVersion = appVersion.substring(0, c); |
+ break; |
+ } |
+ } |
+ if((userAgent.indexOf('webtv') < 0) && |
+ (userAgent.indexOf('hotjava') < 0)){ |
+ if(userAgent.indexOf('Opera') >= 0){ |
+ var ind = (userAgent.indexOf('Opera')+6); |
+ if(((ind+1) < userAgent.length)&&(ind >= 6)){ |
+ isOpera = true; |
+ var bsVersion = parseInt(userAgent.substring(ind, ind+1)); |
+ if(!isNaN(bsVersion)){ |
+ operaVersion = bsVersion; |
+ if(operaVersion >= 6){ |
+ isOpera6p = true; |
+ }else if(operaVersion >= 5){ |
+ isOpera5 = true; |
+ } |
+ } |
+ } |
+ }else if(appName.indexOf('Microsoft Internet Explorer') >= 0){ |
+ var ind = (userAgent.indexOf('MSIE')+5); |
+ if(((ind+1) < userAgent.length)&&(ind >= 5)){ |
+ isIE = true; |
+ var bsVersion = parseInt(userAgent.substring(ind, ind+1)); |
+ if(!isNaN(bsVersion)){ |
+ ieVersion = bsVersion; |
+ if(ieVersion >= 5){ |
+ isIE5p = true; |
+ }else if(ieVersion >= 4){ |
+ isIE4 = true; |
+ } |
+ } |
+ } |
+ }else if(appName.indexOf('Netscape') >= 0){ |
+ if((self.navigator.vendor)&& |
+ (self.navigator.vendor.indexOf('Netscape') >= 0)&& |
+ (userAgent.indexOf('Gecko') >= 0)){ |
+ isNet5p = true; |
+ }else if((userAgent.indexOf('Netscape') < 0)&& |
+ (userAgent.indexOf('Gecko') >= 0)&& |
+ (appVersion >= 5)){ |
+ isMozilla1p = true; |
+ }else if((appVersion < 5)&& |
+ (userAgent.indexOf('compatible') < 0)){ |
+ isNet4 = true; |
+ } |
+ } |
+ } |
+ brSet = true; |
+} |
+</pre> |
+ |
+<p id="bdUAS_2"> |
+This version also uses some other properties of the |
+<code>navigator</code> object; <code>appName</code> and |
+<code>appVersion</code>. |
+</p> |
+ |
+<p id="bdUAS_3"> |
+Superficially this type of script seems to be saying quite a lot about |
+what browser is executing the script. Knowing that the |
+<code>isIE5p</code> variable is boolean <code>true</code> seems to be |
+a reasonable indicator that the browser in question is Internet |
+Explorer Version 5 or above and from that all of the available features |
+on the IE 5+ DOM could be assumed to exist. |
+</p> |
+ |
+<p id="bdUAS_4"> |
+Unfortunately, if this type of script ever was an effective determiner |
+of the browser type, it is not now. The first problem is that you cannot |
+write this type of script to take into account all web browsers. The |
+script above is only interested in Internet Explorer, Netscape and |
+(some) Mozilla derived browsers and Opera. Any other browser will not |
+be identified, and that will include a number of W3C DOM conforming |
+fully dynamic visual browsers quite capable of delivering on even quite |
+demanding code. |
+</p> |
+ |
+<p id="bdUAS_5"> |
+The second problem is that scripts like this one, and server-side |
+counter-parts (reading the HTTP User Agent header) were used to |
+<em>exclude</em> browsers that did not fall into a set of browsers |
+known to the author, regardless of whether those browsers were |
+capable of displaying the offending site or not. |
+</p> |
+ |
+<p id="bdUAS_6"> |
+As more browsers were written, their authors discovered that if they |
+honestly reported their type and version in their User Agent string |
+they would likely be excluded from sites that they would |
+otherwise be quite capable of displaying. To get around this problem |
+browsers began spoofing the more popular versions, sending HTTP User |
+Agent headers, and reporting <code>navigator.userAgent</code> strings, |
+that were indistinguishable from, say, IE. |
+</p> |
+ |
+<p id="bdUAS_7"> |
+As a result, when the above script reports <code>isIE5p</code> as true, it is |
+possible that the browser that is executing the script is one of |
+numerous current browsers. Many of those browsers support sufficient |
+features found on IE5+ to allow most scripts to execute but the |
+trueness of <code>isIE5p</code> is not a valid indicator that the |
+browser will support <em>all</em> of the IE 5+ DOM. |
+</p> |
+ |
+<p id="bdUAS_8"> |
+Now you might decide that a browser that lies about its identity |
+deserves what it gets (though they started lying in order to make |
+themselves usable in the face of near-sighted <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> and script authors) |
+but is worth bearing in mind that the IE 5 |
+<code>navigator.userAgent</code> string is: |
+<code>"Mozilla/4.0 (compatible; MSIE 5.01; Windows NT)"</code>. |
+IE 5 is in fact spoofing Netscape 4, and Microsoft started to do that |
+for precisely the same reasons that motivate many current browsers to |
+send User Agent headers, and report <code>navigator.userAgent</code> |
+strings that are indistinguishable form those of Microsoft browsers. |
+</p> |
+ |
+<p id="bdUAS_9"> |
+No browser manufacture wants (or ever has wanted) their browser to be |
+needlessly excluded from displaying a web site that it is perfectly |
+capable of handling just because the author of that site does not |
+know it by name. And to prevent that they have followed Microsoft and |
+taken action that has rendered the <code>userAgent</code> string (and |
+the HTTP User Agent header) meaningless. |
+</p> |
+ |
+<p id="bdUAS_10"> |
+We are now at a point where the contents of the User Agent strings |
+bear no relationship at all to the capabilities and features of the |
+browser that is reporting it. The situation has gone so far that a |
+number of javascript experts have stated that a standard quality test |
+for an unknown script would include searching the source code of the |
+script for the string <code>"userAgent"</code> and dismissing |
+the script out of hand if that string is found. |
+</p> |
+ |
+<h3 id="bdOI">Assumptions Based on DOM Objects: Object inference</h3> |
+ |
+<p id="bdOI_1"> |
+A second browser detecting strategy uses the objects present in various |
+browser DOMs and make the assumption that the presence (or absence) of |
+one or more objects indicates that a browser is a particular type or |
+version. I quickly found this example of typical code of this type:- |
+</p> |
+ |
+<pre id="bdOI_ex1"> |
+<span class="commentJS">/* Warning: never use this script, or any script based on, or resembling, it. |
+*/</span> |
+var isDOM=(document.getElementById)?true:false; |
+var isIE4=(document.all&&!isDOM)?true:false; |
+var isIE5p=(document.all&&isDOM)?true:false; |
+var isIE=(document.all)?true:false; |
+var isOP=(window.opera)?true:false; |
+var isNS4=(document.layers)?true:false; |
+</pre> |
+ |
+<p id="bdOI_2"> |
+Javascript performs automatic type conversion so when a boolean result |
+is expected from an expression that evaluates to a non-boolean value |
+that non-boolean value is (internally) converted to a boolean value |
+(using the rules defined in the ECMAScript specification) and that |
+boolean is used as the result. |
+</p> |
+ |
+<p id="bdOI_3"> |
+Take the first line:- |
+</p> |
+ |
+<pre id="bdOI_ex2"> |
+var isDOM=(document.getElementById)?true:false; |
+</pre> |
+ |
+<p id="bdOI_4"> |
+The conditional expression requires that the expression preceding the <code>?</code> |
+have a boolean result. The <code>document.getElementById</code> |
+property accessor can resolve as one of two values depending on whether |
+the <code>getElementById</code> function is supported in the browser in |
+question. If it is supported then the accessor resolves as a function |
+object, and is type converted to boolean <code>true</code>. If |
+<code>getElementById</code> is not supported the accessor resolves as |
+undefined, and undefined type converts to boolean |
+<code>false</code>. Thus the expression preceding the question mark |
+resolves as <code>true</code> or <code>false</code> and based on that |
+result <code>true</code> or <code>false</code> are assigned to the |
+variable <code>isDOM</code>. |
+</p> |
+ |
+<div class="tip"> |
+ <h4>Boolean Conversion Tip: !!</h4> |
+ <p id="bdOI_5"> |
+ Incidentally, this code is not the optimum method of assigning a boolean |
+ value based on the type converted to boolean result of a property accessor. |
+ It is better to use the javascript NOT operator ( <code>!</code> ) twice |
+ or to pass the object reference as the argument to the <code>Boolean</code> |
+ constructor called as a function. The not operator will type convert its |
+ operand to boolean and then invert it so <code>false</code> becomes |
+ <code>true</code> and <code>true</code> becomes <code>false</code>. |
+ Passing that result as the operand for a second not operator inverts |
+ the boolean again so a reference to a function object results in boolean |
+ <code>true</code> and an undefined reference results in boolean |
+ <code>false</code>. The <code>Boolean</code> constructor called as a |
+ function converts its argument to boolean and returns that value. The |
+ statement would become:- |
+ </p> |
+ |
+ <pre id="bdOI_ex3"> |
+ var isDOM = !!document.getElementById; |
+ |
+ <span class="commentJS">/* - or - */</span> |
+ |
+ var isDOM = Boolean(document.getElementById); |
+ </pre> |
+ |
+ <p id="bdOI_6"> |
+ Which is shorter and faster than the original version and certainly |
+ more direct. |
+ </p> |
+</div> |
+ |
+<h4>Inductive Generalization Fallacy</h4> |
+<p id="bdOI_7"> |
+The problem with this type of browser detecting script is that it is |
+used to make assumptions about the browser's capabilities that are |
+rarely valid. For example, this <code>isDOM</code> result, based on |
+the browser's support for <code>document.getElementById</code>, is |
+often used as the basis for the assumption that the browser has a |
+fully dynamic DOM with methods such as |
+<code>document.createElement</code>, <code>replaceChild</code> and |
+<code>appendChild</code>. Browsers do not live up to that expectation, |
+some are not that dynamic and while they may implement some of the Core |
+DOM level 1 methods such as <code>getElementById</code> They do not |
+necessarily implement large parts of the various DOM standards, |
+including all of the dynamic <code>Node</code> manipulation methods. |
+</p> |
+ |
+<p id="bdOI_8"> |
+The result of the <code>isIE5p</code> test is intended to indicate that |
+the browser is Internet Explorer 5.0 or above. However, Opera 7, |
+IceBrowser 5.4, Web Browser 2.0 (palm OS), Konquerer, Safari, NetFront, |
+iCab and others will all produce a <code>true</code> value in |
+<code>isIE5p</code> because they implement <code>getElementById</code> |
+and the <code>document.all</code> collection. As a result, code that |
+assumes that it will have <em>all</em> of the capabilities of IE 5.0+ |
+available to it when <code>isIE5p</code> is <code>true</code> will as |
+often as not be mistaken. |
+</p> |
+ |
+<p id="bdOI_9"> |
+This problem applies to all of the tests above with the possible |
+exception of the <code>window.opera</code> test. I am unaware of a |
+second browser type that has implemented an <code>opera</code> object |
+on the window object. But then Opera 7 is a radically different, and |
+much more dynamic browser that its preceding versions, though they all |
+possess a <code>window.opera</code> object. |
+</p> |
+ |
+<p id="bdOI_10"> |
+To get around the problem that multiple browsers implement the same |
+features (even if they start off unique to one browser) script authors |
+have attempted to find more discriminating features to test. For |
+example, the following script extract is intended to work only on IE |
+5.0+ browsers:- |
+</p> |
+ |
+<pre id="bdOI_ex4"> |
+var isIE5p = !!window.ActiveXObject; |
+... |
+function copyToClip(myString){ |
+ if(!isIE5p) return; |
+ window.clipboardData.setData("text",myString); |
+} |
+</pre> |
+ |
+<p id="bdOI_11"> |
+The <code>ActiveXObject</code> constructor is intended to be |
+discriminating of an IE browser. However, this type if script still |
+does not work. It has placed the competing browser manufacturers in |
+exactly the same position as they were in when scripts tested the |
+<code>navigator.userAgent</code> string and excluded them from |
+accessing a site because they honestly reported that they where not |
+IE. As a result I already know of one browser that has implemented |
+a <code>window.ActiveXObject</code> function, it probably is a dummy |
+and exists in the browsers DOM specifically to defeat the exclusion |
+of that browser based on tests like the one above. |
+</p> |
+ |
+<p id="bdOI_12"> |
+The assumptions that the existence of one (or two) feature(s) in a |
+javascript environment infers the existence of any feature beyond |
+the ones tested is invalid. It is only used by those ignorant of the |
+potential for diversity, imitation and the patterns of evolution in |
+browser DOMs. |
+</p> |
+ |
+<p id="bdOI_13"> |
+No matter how specifically the objects from which the inferences are |
+derived are chosen, the technique itself sows the seeds of its own |
+invalidity, an object that may actually validly be used to infer that |
+a browser is of a particular type/version today probably will not still |
+be valid next year. Adding a maintenance burden to a task that already |
+presupposes an omniscient knowledge of <em>all</em> browser DOMs just |
+in order to be effectively implemented at present. |
+</p> |
+ |
+ |
+<h2 id="bdFD">A Strategy That Works: Object/Feature Detecting</h2> |
+ |
+<p id="bdFD_1"> |
+The main point of the previous discussion is to convey the idea that it |
+is impossible to detect exactly which type of browser (or version of |
+that browser) a script is being executed on. The use that such scripts |
+have been put to in the past (to exclude browsers from sites that |
+they probably could have successfully handled) has motivated the |
+manufactures of browsers to render browser detecting nonviable |
+as a strategy for dealing with the variations in browser DOMs. |
+</p> |
+ |
+<p id="bdFD_2"> |
+Fortunately, not being able to identify a web browser type or version |
+with more accuracy than could be achieved by generating a random number |
+and then biasing the result by your favourite (meaningless, because |
+they too are based on browser detecting and suffer exactly the same |
+unreliability) browser usage statistics, does not need to impact upon |
+your ability to script web browsers at all. A viable alternative |
+strategy has been identified and developed to the point where it is |
+possible to author javascript to be used on web pages without any |
+interest in the type or version of the browser at all. |
+</p> |
+ |
+<p id="bdFD_3"> |
+That alternative strategy is known as object or feature detecting. I |
+prefer to use the term "feature detecting", partly because the |
+resulting code often needs to test and probe a wider range of |
+features than just those that could be described as objects, but |
+mostly because "object detecting" is occasionally |
+erroneously applied to the object inference style of script described |
+above. |
+</p> |
+ |
+<p id="bdFD_4"> |
+Feature detecting seeks to match an attempt to execute as script (or a |
+part of a script) with the execution environment by seeking to test |
+features of that environment where the results of the test have a |
+direct one-to-one relationship with the features that need to be |
+supported in the environment for the code to successfully execute. It |
+is the direct one-to-one relationship in the implemented tests that |
+avoids the need to identify the specific browser because whatever |
+browser it is it either will support all of the required features or |
+it will not. That would mean testing the feature itself (to ensure |
+that it exists on the browser) and possibly aspects of the behaviour |
+of that feature. |
+</p> |
+ |
+<p id="bdFD_5"> |
+Taking the previous example that illustrated how the |
+<code>ActiveXObject</code> constructor might be used as the basis for |
+a script that inferred the existence of, and ability to use, the |
+<code>clipboardData</code> feature implemented on window IE. Rather |
+than inferring the browser's support for the <code>clipboardData</code> |
+feature from some other unrelated feature it should be fairly obvious |
+that the feature that should be tested for prior to attempting to write |
+to the clipboard <em>is</em> the <code>clipboardData</code> object, and |
+further, that calling the <code>setData</code> method of that object |
+should necessitate checking that it too is implemented:- |
+</p> |
+ |
+<pre id="bdFD_ex1"> |
+function copyToClip(myString){ |
+ if((typeof clipboardData != 'undefined')&& |
+ (clipboardData.setData)){ |
+ clipboardData.setData("text",myString); |
+ } |
+} |
+</pre> |
+ |
+<p id="bdFD_6"> |
+In this way the tests that determine whether the |
+<code>clipboardData.setData</code> method is called have a direct |
+one-to-one relationship with the browser's support for the feature. It |
+is not necessary to be interested in whether the browser is the |
+expected windows IE that is known to implement the feature, or whether |
+it is some other browser that has decided to copy IE's implementation |
+and provide the feature itself. If the feature is there (at least to |
+the required extent) it is used and if it is not there no attempt is |
+made to use it. |
+</p> |
+ |
+<p id="bdFD_7"> |
+The above feature detecting tests are done using two operations. The |
+first employs the <code>typeof</code> operator, which returns a string |
+depending on the type of its operand. That string is one of |
+<code>"undefined"</code>, <code>"object"</code>, |
+<code>"function"</code>, <code>"boolean"</code> |
+<code>"string"</code> and <code>"number"</code> |
+and the test compares the returned string with the string |
+<code>"undefined"</code>. The <code>clipboardData</code> |
+object is not used unless typeof does not return |
+<code>"undefined"</code>. |
+</p> |
+ |
+ |
+<p id="bdFD_8"> |
+The second test is a type-converting test. The logical AND |
+(<code>&&</code>) operator internally converts its operands to |
+boolean in order to make its decision about what value it will return. |
+If <code>clipboardData.setData</code> is undefined it will type-convert |
+to boolean <code>false</code>, while if it is an object or a function |
+the result of the conversion will be boolean <code>true</code>. |
+</p> |
+ |
+<p id="bdFD_9"> |
+However, that function is not a particularly clever application of |
+feature detecting because, while it avoids the function throwing errors |
+in an attempt to execute <code>clipboardData.setData</code> on a browser |
+thatdoes not support it, it will do nothing on a browser that does not |
+support it. That is a problem when the user has been presented with a |
+GUI component that gives them the impression that their interaction |
+will result in something being written to the clipboard but when they |
+use it nothing happens. And of course nothing was going to happen if |
+the browser in use did not support javascript or it had been disabled. |
+</p> |
+ |
+<p id="bdFD_10"> |
+Ensuring that a script will not attempt to use a feature that is not |
+supported is not sufficient to address the design challenge of crating |
+scripts for the Internet. Testing the browser for the features that it |
+does support makes it practical to handle a spectrum of browser DOMs |
+but the script design task also involves planning how to handle the |
+range of possibilities. A range that goes from guaranteed failure to |
+execute at all on browser that do not support javascript, to full |
+support for all of the required features. |
+</p> |
+ |
+<p id="bdFD_11"> |
+You can tell when the browser does not support the |
+<code>clipboardData</code> feature from the script prior to using it |
+but the user has no way of knowing why a button that promised them |
+some action has failed to do anything. So in addition to matching the |
+script to the browser's ability to execute it, it is also necessary to |
+match the GUI, and the user's resulting expectations, to what the |
+script is going to be able to deliver. |
+</p> |
+ |
+<p id="bdFD_12"> |
+Suppose the <code>copyToClip</code> function was called from an |
+<code>INPUT</code> element of <code>type="button"</code> |
+and was intended to copy a company e-mail address into the clipboard, |
+the <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> code for the button might look like:- |
+</p> |
+ |
+<pre id="bdFD_ex2"> |
+<input type="button" |
+ value="copy our contact e-mail address to your clipboard" |
+ onclick="copyToClip('info@example.com')"> |
+</pre> |
+ |
+<p id="bdFD_13"> |
+We know that that <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> will do nothing if javascript is |
+disabled/unavailable and we know that it will do nothing if the browser |
+does not support the required features, so one option would be to use a |
+script to write the button <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> into the document in the position in |
+which the button was wanted when the browser provided the facility:- |
+</p> |
+ |
+<pre id="bdFD_ex3"> |
+<script type="text/javascript"> |
+ if((typeof clipboardData != 'undefined')&& |
+ (clipboardData.setData)&& |
+ (document.write)){ |
+ document.write('<input type="button"', |
+ 'value="copy our contact e-mail address', |
+ ' to your clipboard" onclick="', |
+ 'copyToClip(\'info@example.com\')">'); |
+ } |
+</script> |
+</pre> |
+ |
+<p id="bdFD_14"> |
+Now the user will never see the button unless the browser supports the |
+required features <em>and</em> javascript is enabled. The user never |
+gets an expectation that the script will not be able to deliver (at |
+least that is the theory, it is still possible for the user's browser |
+configuration to prevent scripts from writing to the clipboard, but |
+the user might be expected to know how their browser is configured and |
+understand that the button is not in a position to override it). |
+</p> |
+ |
+<p id="bdFD_15"> |
+If the <code>copyToClip</code> function is only ever called from |
+buttons that are written only following the required feature detection |
+then it can be simplified by the removal of the test from its body as |
+it would be shielded from generating errors on nonsupporting browsers by |
+the fact that there would be no way for it to be executed. |
+</p> |
+ |
+<p id="bdFD_16"> |
+The <code>document.write</code> method is not the only way of adding |
+GUI components to a web page in a way that can be subject to the |
+verification of the features that render those components meaningful. |
+Alternatives include writing to a parent element's |
+<code>innerHTML</code> property (where supported, see |
+<a href="/faq/#updateContent">FAQ: How do I modify the content of the current page?</a>), or using the W3C DOM |
+<code>document.crateElement</code> (or <code>createElementNS</code>) |
+methods and appending the created element at a suitable location within |
+the DOM. Either of these two approaches are suited to adding the |
+components after the page has finished loading, but that can be useful |
+as some feature testing is not practical before that point. The |
+approach used can be chosen based on the requirements of the script. |
+If the script is going to be using the |
+<code>document.createElement</code> method itself then it is a good |
+candidate as a method for inserting any required GUI components, |
+similarly with <code>innerHTML</code>. The <code>document.write</code> |
+method is universally supported in <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> DOMs but is not necessarily |
+available at all in XHTML DOMs. |
+</p> |
+ |
+<p id="bdFD_17"> |
+Other ways of handling the possibility that the browser will not |
+support either javascript or the features required by the script used |
+is to design the <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span>/<span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> parts of the page so that the script, upon |
+verifying support for the features it requires, can modify, manipulate |
+and transform the resulting elements in the DOM. But in the absence of |
+sufficient script support the unmodified <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> presents all of the |
+required content, navigation, etc. |
+</p> |
+ |
+<p id="bdFD_18"> |
+This can be particularly significant with things like navigation menus. |
+One style of design would place the content of the navigation menus, |
+the URLs and text, in javascript structures such as Arrays. But either |
+of javascript being disabled/unavailable on the client or the absence |
+of the features required to support a functional javascript menu would |
+leave the page without any navigation at all. Generally that would not |
+be a viable web page, and not that good for search engine placement as |
+search engine robots do not tend to execute javascript either so they |
+would be left unable to navigate a site featuring such a menu and so |
+unable to rank its content for listing. |
+</p> |
+ |
+<p id="bdFD_19"> |
+A better approach to menu design would have the navigation menu content |
+defined in the <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span>, possibly as nested list elements of some sort, and |
+once the script has ascertained that the browser is capable of executing |
+it and providing the menu in an interactive form it can modify the <span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> |
+<code>position</code>, <code>display</code> and <code>visibility</code> |
+properties, move the elements to their desired location, attach event |
+handlers and generally get on with the task of providing a javascript |
+menu. And the worst that happens when the browser does not support |
+scripting or the required features is that the user (and any search |
+engine robots) finds the navigation in the page as a series of nested |
+lists containing links. Fully functional, if not quite as impressive as |
+it could have been had the script been supported. This is termed |
+"clean degradation" and goes hand in hand with feature |
+detecting during the process of planing and implementing a browser |
+script for the Internet. |
+</p> |
+ |
+<h3 id="bdGEID">Example 1: IDed Element Retrieval</h3> |
+ |
+<p id="bdGEID_1"> |
+An important aspect of feature detecting is that it allows a script to |
+take advantage of possible fall-back options. Having deduced that a |
+browser lacks the preferred feature it still may be possible to |
+achieve the desired goal by using an alternative feature that is know |
+to exist on some browsers. A common example of this is retrieving an |
+element reference from the DOM given a string representing the |
+<code>ID</code> attribute of that element. The preferred method would |
+be the W3C Core DOM standard <code>document.getElementById</code> |
+method which is supported on the widest number of browsers. If that |
+method was not available but the browser happened to support the |
+<code>document.all</code> collection then it could be used for the |
+element retrieval as a fall-back option. And for some types of |
+elements, such as <span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> positioned elements on Netscape 4 (where the |
+<code>document.layers</code> collection may be used to retrieve such |
+a reference), additional options may be available. |
+</p> |
+ |
+<pre id="bdGEID_ex1"> |
+function getElementWithId(id){ |
+ var obj; |
+ if(document.getElementById){ |
+ <span class="commentJS">/* Prefer the widely supported W3C DOM method, if |
+ available:- |
+ */</span> |
+ obj = document.getElementById(id); |
+ }else if(document.all){ |
+ <span class="commentJS">/* Branch to use document.all on document.all only |
+ browsers. Requires that IDs are unique to the page |
+ and do not coincide with NAME attributes on other |
+ elements:- |
+ */</span> |
+ obj = document.all[id]; |
+ }else if(document.layers){ |
+ <span class="commentJS">/* Branch to use document.layers, but that will only work for |
+ <span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> positioned elements and LAYERs that are not nested. A |
+ recursive method might be used instead to find positioned |
+ elements within positioned elements but most DOM nodes on |
+ document.layers browsers cannot be referenced at all. |
+ */</span> |
+ obj = document.layers[id]; |
+ } |
+ <span class="commentJS">/* If no appropriate/functional element retrieval mechanism |
+ exists on this browser this function returns null:- |
+ */</span> |
+ return obj||null; |
+} |
+</pre> |
+ |
+<p id="bdGEID_2"> |
+Although that function is not very long or complex (without its |
+comments) it does demonstrate a consequence of one style of |
+implementation of feature detecting, it repeats the test each time |
+it is necessary to retrieve an element using its ID. If not too many |
+elements need retrieving that may not be significant, but if many |
+elements needed retrieving in rapid succession and performance was |
+significant then the overhead of performing the feature detection on |
+each retrieval may add up and impact on the resulting |
+script. |
+</p> |
+ |
+<p id="bdGEID_3"> |
+An alternative is to assign one of many functions to a global |
+<code>getElementWithId</code> variable based on the results of the |
+feature detecting tests, as the script loads. |
+</p> |
+ |
+<pre id="bdGEID_ex2"> |
+var getElementWithId; |
+if(document.getElementById){ |
+ <span class="commentJS">/* Prefer the widely supported W3C DOM method, if |
+ available:- |
+ */</span> |
+ getElementWithId = function(id){ |
+ return document.getElementById(id); |
+ } |
+}else if(document.all){ |
+ <span class="commentJS">/* Branch to use document.all on document.all only |
+ browsers. Requires that IDs are unique to the page |
+ and do not coincide with NAME attributes on other |
+ elements:- |
+ */</span> |
+ getElementWithId = function(id){ |
+ return document.all[id]; |
+ } |
+}else if(document.layers){ |
+ <span class="commentJS">/* Branch to use document.layers, but that will only work for <span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> |
+ positioned elements. This function uses a recursive method |
+ to find positioned elements within positioned elements but most |
+ DOM nodes on document.layers browsers cannot be referenced at |
+ all. This function is expected to be called with only one |
+ argument, exactly like the over versions. |
+ */</span> |
+ getElementWithId = function(id, baseLayers){ |
+ <span class="commentJS">/* If the - baseLayers - parameter is not provided default |
+ its value to the document.layers collection of the main |
+ document: |
+ */</span> |
+ baseLayers = baseLayers||document.layers; |
+ <span class="commentJS">/* Assign the value of the property of the - baseLayers - |
+ object (possibly defaulted to the document.layers |
+ collection) with the property name corresponding to the |
+ - id - parameter to the local variable - obj: |
+ */</span> |
+ var obj = baseLayers[id]; |
+ <span class="commentJS">/* If - obj - remains undefined (no element existed with the |
+ given - id -) try searching the indexed members of |
+ - baseLayers - to see if any of their layers collections |
+ contain the element with the corresponding - id: |
+ */</span> |
+ if(!obj){ <span class="commentJS">//Element not found</span> |
+ <span class="commentJS">/* Loop through the indexed members of - baseLayers: */</span> |
+ for(var c = 0;c < baseLayers.length;c++){ |
+ if((baseLayers[c])&& <span class="commentJS">//Object at index - c.</span> |
+ (baseLayers[c].document)&& <span class="commentJS">//It has a - document.</span> |
+ <span class="commentJS">/* And a layers collection on that document: */</span> |
+ (baseLayers[c].document.layers)){ |
+ <span class="commentJS">/* Recursively call this function passing the - id - as |
+ the first parameter and the layers collection from |
+ within the document found on the layer at index - c |
+ - in - baseLayers - as the second parameter and |
+ assign the result to the local variable - obj: |
+ */</span> |
+ obj=getElementWithId(id,baseLayers[c].document.layers); |
+ <span class="commentJS">/* If - obj - is now not null then we have found the |
+ required element and can break out of the - for - |
+ loop: |
+ */</span> |
+ if(obj)break; |
+ } |
+ } |
+ } |
+ <span class="commentJS">/* If - obj - will type-convert to boolean true (it is not null |
+ or undefined) return it, else return null: |
+ */</span> |
+ return obj||null; |
+ } |
+}else{ |
+ <span class="commentJS">/* No appropriate element retrieval mechanism exists on |
+ this browser. So assign this function as a safe dummy. |
+ Values returned form calls to getElementWithId probably |
+ should be tested to ensure that they are non-null prior |
+ to use anyway so this branch always returns null:- |
+ */</span> |
+ getElementWithId = function(id){ |
+ return null; |
+ } |
+} |
+ |
+<span class="commentJS">/* |
+Usage:- |
+ |
+var elRef = getElementWithId("ID_string"); |
+if(elRef){ |
+ ... //element was found |
+}else{ |
+ ... //element could not be found. |
+} |
+*/</span> |
+</pre> |
+ |
+<p id="bdGEID_4"> |
+The feature detecting tests are performed once as the page loads and |
+one of many function expressions assigned to the |
+<code>getElementWithId</code> global variable as a result. The |
+assigned function is the one most capable of retrieving the required |
+element on the browser in use but it is still necessary to check that |
+the returned value in not <code>null</code> and plan for the |
+possibility of failure in the element retrieval process. It is |
+guaranteed to fail on any browser that does not implement at least one |
+of the element retrieval mechanisms used as the default function just |
+returns <code>null</code>. |
+</p> |
+ |
+<p id="bdGEID_5"> |
+It may not be sufficient to provide a function that does the best job |
+of element retrieval that can be done on the browser in use. Other |
+scripts, or parts of the script, may need to know how successful their |
+attempts to retrieve elements are likely to be. The |
+<code>getElementWithId</code> version that uses |
+<code>document.layers</code> cannot retrieve elements that are not <span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> |
+positioned and that may not be good enough for some scripts, while it |
+may not matter to others. The <code>document.getElementById</code> and |
+<code>document.all</code> versions should be able to retrieve any |
+element given its <code>ID</code>. An opting would be to set a couple |
+of boolean flags to indicate how successful element retrieval can be |
+expected to be. Defaulting the flags to <code>false</code> and setting |
+them to <code>true</code> in the branches that assign the various |
+function expressions. Then if other code is interested in what can be |
+expected from the <code>getElementWithId</code> function, say in order |
+to decide how to configure, or default itself, it can check the |
+pertinent flag. |
+</p> |
+ |
+<pre id="bdGEID_ex3"> |
+var getElementWithId; |
+var canGetAnyElement = false; <span class="commentJS">//default any element flag</span> |
+var canGetCSSPositionedElements = false; <span class="commentJS">//default <span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> positioned flag</span> |
+if(document.getElementById){ |
+ canGetAnyElement = true; <span class="commentJS">//set any element flag to true</span> |
+ canGetCSSPositionedElements = true; <span class="commentJS">//set <span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> positioned flag true</span> |
+ getElementWithId = ... |
+}else if(document.all){ |
+ canGetAnyElement = true; <span class="commentJS">//set any element flag to true</span> |
+ canGetCSSPositionedElements = true; <span class="commentJS">//set <span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> positioned flag true</span> |
+ getElementWithId = ... |
+}else if(document.layers){ |
+ canGetCSSPositionedElements = true; <span class="commentJS">//set <span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> positioned flag true</span> |
+ <span class="commentJS">/* The - canGetAnyElement - flag is not set in this branch because |
+ the document.layers collection does not make *all* elements |
+ available. |
+ */</span> |
+ getElementWithId = ... |
+}else{ |
+ <span class="commentJS">/* Neither flag is set when the dummy function is assigned because |
+ it is guaranteed not to be able to retrieve any elements: |
+ */</span> |
+ getElementWithId = function(id){ |
+ return null; |
+ } |
+} |
+... |
+if(canGetCSSPositionedElements){ |
+ <span class="commentJS">/* Expect to be able to retrieve <span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> positioned elements. |
+ */</span> |
+ ... |
+ if(canGetAnyElement){ |
+ <span class="commentJS">/* Expect to also be able to retrieve any other elements that |
+ have an ID attribute. |
+ */</span> |
+ ... |
+ } |
+} |
+</pre> |
+ |
+<p id="bdGEID_6"> |
+The flags do not directly reflect which feature is going to be used |
+for element retrieval, instead they reflect what can be expected from |
+the <code>getElementWithId</code> function on the current browser. |
+Allowing a script that requires a particular level of performance |
+(say the retrieval of any element) to determine whether it will have |
+that facility but without denying the facility from a script with a |
+less demanding requirement. |
+</p> |
+ |
+<h3 id="bdScroll">Example 2: Scroll Values</h3> |
+ |
+<p id="bdScroll_1"> |
+Another common task that needs to be approached differently on |
+different browsers is the retrieval of the extent to which the user |
+has scrolled a web page. The majority of browsers provide properties |
+of the global object called <code>pageXOffset</code> and |
+<code>pageYOffset</code>, which hold the relevant values. Some make the |
+equivalent browsers available as scrollLeft and scrollTop properties on |
+the "root" element (either in addition to the |
+<code>pageX/YOffset</code> properties or instead of them). The task is |
+complicated further by the fact that which element is the |
+"root" element depends on various factors, it was always the |
+<code>document.body</code> element in the past but newer (<span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span>) |
+standards compliant browsers (and browsers that can operate in various |
+modes, including standards compliant mode) make the |
+<code>document.documentElement</code> the root element. Then there may |
+be browsers that do not make the scrolling values available at all. |
+</p> |
+ |
+<p id="bdScroll_2"> |
+Because the <code>pageXOffset</code> and <code>pageYOffset</code> |
+properties are implemented on the largest number of browsers, and their |
+use avoids the need to worry about the "root" element, they |
+are the preferred values to use. In there absence the problem moves on |
+to identifying the "root" element, which is made easier by |
+the browsers that understand standards compliant mode and provide a |
+<code>document.compatMode</code> string property to announce which mode |
+they are in. If the string property is missing or the value of that |
+string is other than <code>"CSS1Compat"</code> then it is the |
+<code>document.body</code> object that needs to be read for the |
+scrolling values, else the <code>document.documentElement</code> should |
+be read. Testing for the presence of any of the scroll values |
+themselves needs to done with a <code>typeof</code> test because they |
+are numeric values and if implemented but set to zero a type-converting |
+test would return <code>false</code> but that would not be an indicator |
+of their absence. |
+</p> |
+ |
+<p id="bdScroll_3"> |
+The following is an example that employs feature detection to decide |
+which scroll values to read:- |
+</p> |
+ |
+<pre id="bdScroll_ex1"> |
+<span class="commentJS">/* The - getPageScroll - global variable is assigned a reference to a |
+ function and when that function is called initially it configures |
+ the script to read the correct values, if available, and then |
+ returns a reference to the object - interface - which provides |
+ methods that retrieve the scroll values. Subsequent invocations of |
+ the getPageScroll function do not repeat the configuration, they |
+ just return a reference to the same interface object. Because the |
+ configuration stage may need to check whether the document.body |
+ element exists the function cannot be called until the browser has |
+ parsed the opening body tag as prior to that point there is no |
+ document.body element. |
+ |
+ Usage:- |
+ var scrollInterface = getPageScroll(); |
+ var scrollX = scrollInterface.getScrollX(); |
+ var scrollY = scrollInterface.getScrollY(); |
+ |
+ The interface methods return NaN if the browser provides no method |
+ of reading the scroll values. A returned NaN value can be tested for |
+ with the - isNaN - global function, but it should not be necessary |
+ to perform the isNaN test on more than the first retrieval because |
+ if the returned value is NaN it will always be NaN and if it is not |
+ it should never be. |
+ |
+ if(isNaN(scrollX)){ |
+ //No scroll value retrieval mechanism was available on this browser |
+ } |
+ |
+ (* The script performs an inline execution of a function expression |
+ which returns the function object that is assigned to the - |
+ getPageScroll - global variable. This produces a closure that |
+ preserves the local variables of the executed function expression, |
+ allowing the execution context of the function expression to provide |
+ a repository for the configuration results, keeping them out of the |
+ global namespace. The format is:- |
+ |
+ v--- Anonymous function expression --v |
+ var variable = (function(){ ...; return returnValue; })(); |
+ Inline execution of the function expression ----^^ |
+ |
+ The value returned by the inline execution of the anonymous function |
+ expression is assigned to the variable. If that returned value is a |
+ reference to an inner function object then the assignment will form |
+ a closure.) |
+*/</span> |
+var getPageScroll = (function(){ |
+ <span class="commentJS">/* The local variable "global" is assigned the value - this - |
+ because the function expression is executing in the global |
+ context and - this - refers to the global object in that |
+ context. The global object is usually the - window - object on |
+ web browsers but this local variable is going to be used in the |
+ configuration tests for convenience: |
+ */</span> |
+ var global = this; |
+ <span class="commentJS">/* notSetUp - Is a flag to indicate when the script has done the |
+ setup configuration: |
+ */</span> |
+ var notSetUp = true; |
+ <span class="commentJS">/* readScroll - Is initially a dummy object that is used to return |
+ the NaN values whenever no functional scroll value retrieval |
+ mechanism is available on the browser. It is assigned a |
+ reference to the object from which the scroll values can be read |
+ if the feature detection determines that to be possible: |
+ */</span> |
+ var readScroll = {scrollLeft:NaN,scrollTop:NaN}; |
+ <span class="commentJS">/* Using the local variables - readScrollX - and readScrollY - to |
+ hold the property names allows the same functions to read both |
+ the pageX/YOffset properties of the global object and the |
+ scrollTop/Left properties of the "root" element by assigning |
+ different values to the variables. These are the defaults: |
+ */</span> |
+ var readScrollX = 'scrollLeft'; |
+ var readScrollY = 'scrollTop'; |
+ <span class="commentJS">/* The - itrface - local variable is assigned a reference to an |
+ object and it is this object that is returned whenever |
+ getPageScroll() is called. The object has two properties, |
+ getScrollX and getScrollY, which are assigned the values of two |
+ anonymous function expressions. These functions are inner |
+ functions and as a result have access to the local variables of |
+ the function that contains them (the anonymous function |
+ expression that is executed inline in order to assign value to |
+ the getPageScroll global variable). The use a square bracket |
+ property accessor to read a value of whatever object has been |
+ assigned to the variable - readScroll - with a property name |
+ that corresponds to the value assigned to whichever of the |
+ variables - readScrollX - or - readScrollY - are employed, |
+ allows the functions to use the simplest code poible to provide |
+ values for all of the possible permutations resting from the |
+ feature detection derived configuration: |
+ */</span> |
+ var itrface = { |
+ getScrollX:function(){ |
+ return readScroll[readScrollX]; |
+ }, |
+ getScrollY:function(){ |
+ return readScroll[readScrollY]; |
+ } |
+ }; |
+ <span class="commentJS">/* The - setUp - inner function is called to perform the feature |
+ detection and configure the variables that will be employed in |
+ reading the correct scroll values. It sets the - notSetUp - flag |
+ to false once it has been executed so that configuration only |
+ happens the first time that a request for the interface object |
+ is made: |
+ */</span> |
+ function setUp(){ |
+ <span class="commentJS">/* As the paeX/YOffset properties are the preferred values to |
+ use they are tested for first. They are not both tested |
+ because if one exists the other can be assumed to exist for |
+ symmetry. The testing method is a - typeof - test to see if |
+ the value is a number. A type-converting test cannot be used |
+ because the number zero would result in boolean false and a |
+ pageXOffset value will be zero if the page has not been |
+ scrolled: |
+ */</span> |
+ if(typeof global.pageXOffset == 'number'){ |
+ <span class="commentJS">/* If pageXOffset is a number then the value of the - |
+ global - variable (assigned a reference to the global |
+ object earlier) is assigned to the - readScroll - |
+ variable and the strings "pageXOffset" and "pageYOffset" |
+ are assigned to the - readScrollX - and - readScrollY - |
+ variables so they will be the property names used to |
+ access the - readScroll- (now the global) object. |
+ */</span> |
+ readScroll = global; |
+ readScrollY = 'pageYOffset'; |
+ readScrollX = 'pageXOffset'; |
+ }else{ |
+ <span class="commentJS">/* If pageXOffset is undefined it is time to find out which |
+ object is the "root" element. First, does the browser |
+ have a - document.compatMode - string, if it does then |
+ is its value "BackCompat", "QuirksMode" or "CSS1Compat". |
+ Instead of comparing the string directly it is searched |
+ for the substring "<span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span>" which might make the script more |
+ robust in the face of possible future "CSSnCompat" |
+ modes, which are unlikely to demand that the "root" |
+ element is moved again. |
+ |
+ The tests also verifies that there is a - |
+ document.documentElement - to read and that its |
+ - scrollLeft - property is a number: |
+ */</span> |
+ if((typeof document.compatMode == 'string')&& |
+ (document.compatMode.indexOf('CSS') >= 0)&& |
+ (document.documentElement)&& |
+ (typeof document.documentElement.scrollLeft=='number')){ |
+ <span class="commentJS">/* The - readScrollX - and - readScrollY - variables |
+ are already defaulted to the required strings so it |
+ is only necessary to assign a reference to the - |
+ document.documentElement - to the - readScroll - |
+ variable: |
+ */</span> |
+ readScroll = document.documentElement; |
+ <span class="commentJS">/* If the browser is not in the appropriate mode the scroll |
+ values should be read from the document.body - element, |
+ assuming it exists on this browser and that the |
+ - scrollLeft - property is a number: |
+ */</span> |
+ }else if((document.body)&& |
+ (typeof document.body.scrollLeft == 'number')){ |
+ <span class="commentJS">/* The - readScrollX - and - readScrollY - variables |
+ are already defaulted to the required strings so it |
+ is only necessary to assign a reference to the - |
+ document.body - to the - readScroll - variable: |
+ */</span> |
+ readScroll = document.body; |
+ } |
+ <span class="commentJS">/* No other scroll value reading options exist so if - |
+ readScroll - has not been assigned a new value by this |
+ point it will remain a reference to the object with the |
+ NaN value properties. |
+ */</span> |
+ } |
+ notSetUp = false; <span class="commentJS">//No need to repeat configuration.</span> |
+ } |
+ <span class="commentJS">/* The inline execution of the anonymous function expression |
+ returns with the following statement. It returns an inner |
+ function expression and it is that function that will be called |
+ when getPageScroll() is executed. Doing this forms a closure, |
+ preserving all of the local variables and functions defined |
+ within the executed anonymous function expression. Calling that |
+ returned function as - getPageScroll() - executes the setUp |
+ function, but only if it has not already been called, and |
+ returns a reference to the - itrface - object. |
+ */</span> |
+ return (function(){ |
+ if(notSetUp){ <span class="commentJS">//If the - notSetUp - variable is still true.</span> |
+ setUp(); <span class="commentJS">//Execute the - setUp - function.</span> |
+ } |
+ return itrface; <span class="commentJS">//returns a reference to - itrface</span> |
+ }); |
+})(); <span class="commentJS">//inline-execution of anonymous function expression, one-off!</span> |
+</pre> |
+ |
+<p id="bdScroll_4"> |
+The effect of this code is to match the browser's ability to provide |
+the scrolling information with a script's desire to read it through a |
+simple and efficient interface that acts based on the results of |
+featured detecting tests that it applies only once, and if the browser |
+does not support any methods of reading the scroll values the return |
+values form the interface method indicate that fact by being NaN. It |
+does not matter that Netscape 4 will be reading the global |
+<code>pageX/YOffset</code> properties, that IE 4 will read the |
+<code>scrollTop/Left</code> properties from <code>document.body</code> |
+or that IE 6 will read those values from one of two possible objects |
+based on the <code>document.compatMode</code> value. What is important |
+is that if unknown browser XYZ provides any of those mechanisms for |
+reporting the scroll values the script is going to be able to use them, |
+without ever knowing (or caring) that it is browser XYZ that it is |
+executing on. |
+</p> |
+ |
+<h3 id="bdReplace">Example 3: String.prototype.replace</h3> |
+ |
+<p id="bdReplace_1"> |
+Feature detecting is not restricted to features of the DOM, it can be |
+extended to include features of the javascript language implementation. |
+For example the <code>String.prototype.replace</code> function in later |
+language versions will accept a function reference as its second |
+argument, while earlier versions only accept a string in that context. |
+Code that wants to use the function argument facility of the |
+<code>replace</code> method will fail badly if it is not supported on |
+the browser. |
+</p> |
+ |
+<p id="bdReplace_2"> |
+As usual, a feature-detecting test for the implementation's ability to |
+support function arguments with the <code>replace</code> method has to |
+be a direct test on that feature. The following example test takes |
+advantage of the fact that a browser that only supports the string |
+argument version of <code>replace</code> will type-convert a function |
+reference used in that context into a string. The <code>replace</code> |
+method uses a Regular Expression (its first argument) to |
+identify parts of a string and then replaces them with a string that is |
+provided as its second argument. If the second argument is a function, |
+and the browser supports the function argument, that function is called |
+and its return value replaces the identified parts of the string. |
+</p> |
+ |
+<p id="bdReplace_3"> |
+By providing a function expression that returns an empty string as its |
+second argument and a Regular Expression that identifies the entire |
+original string as the first argument, the operation of the |
+<code>replace</code> method will result in an empty string if the |
+function argument is supported. But if only string arguments are |
+supported then the function will be type-converted into a string and |
+that string will not be an empty string so the result of the |
+evaluation of the <code>replace</code> method will not be an empty |
+string. Applying a NOT (<code>!</code>) operation to the resulting |
+string type-converts the empty string into a boolean value, inverts |
+it and returns <code>true</code>, the non-empty string would result |
+in <code>false</code>. |
+</p> |
+ |
+<pre id="bdReplace_ex1"> |
+<span class="commentJS">/* The original string is the one letter string literal "a". The |
+ Regular Expression /a/ identifies that entire string, so it is the |
+ entire original string that will be replaced. The second argument is |
+ the function expression - function(){return '';} -, so the entire |
+ original string will be replaced with an empty string if the |
+ function expression is executed. If it is instead type-converted |
+ into a string that string will not be an empty string. The NOT (!) |
+ operation type-converts its operand to boolean and then inverts it |
+ so the results of the test is boolean true if function references |
+ are supported in the second argument for the - replace - method, and |
+ false when not supported: |
+*/</span> |
+if(!('a'.replace(/a/, (function(){return '';})))){ |
+ ... <span class="commentJS">//function references OK.</span> |
+}else{ |
+ ... <span class="commentJS">//no function references with replace.</span> |
+} |
+</pre> |
+ |
+<p id="bdReplace_4"> |
+The common thread with feature detecting is that it is the code that is |
+going to use the features, and the nature of those features, that |
+defines how support for the required feature needs to be tested. Once |
+you get used to the idea it starts to become obvious which tests need |
+to be applied to verify a browser's support for a feature, and then it |
+is time to work on the efficient application of feature detection. |
+</p> |
+ |
+<h2 id="bdDesPb">The Javascript Design Problem</h2> |
+ |
+<p id="bdDesPb_1"> |
+Javascript as a language is not that complex, it may have its quirks |
+but it can be defined entirely in the 173 pages of the ECMA |
+specification (3rd edition). The challenge of authoring javascript |
+comes form the diversity of execution environments. When authoring for |
+the Internet nothing is known about the receiving software in advance, |
+and even when that software is a web browser that will execute |
+javascript, there is still a spectrum of possible DOM implementations |
+to contend with. |
+</p> |
+ |
+<p id="bdDesPb_2"> |
+The combination of the facts that it is impossible to determine which |
+browser is executing the script, and that it is impossible to be |
+familiar with all browser DOMs can be rendered insignificant by using |
+feature detection to match code execution with any browser's ability to |
+support it. But there is still going to be a diversity of |
+outcomes, ranging from total failure to execute any scripts (on |
+browsers that do not support javascript, or have it disabled) to full |
+successful execution on the most capable javascript enabled browsers. |
+</p> |
+ |
+<p id="bdDesPb_3"> |
+The challenge when designing scripts is to cope with all of the |
+possibilities in a way that makes sense for everyone. As those |
+possibilities will always include browsers incapable of executing |
+javascript at all, the starting point must be pages based on (valid) |
+<span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> that contain all of the required content, allow the necessary |
+navigation and are as functional as they purport to be (possibly with |
+the backing of server-side scripting, which does not have any of the |
+problems of client side scripting). On top of that reliable foundation |
+it is possible to layer the scripts. Feature detecting and adding |
+scripted enhancements when the browser is capable of supporting them, |
+cleanly degrading to the underlying and reliable <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> when it is not. |
+</p> |
+ |
+<p id="bdDesPb_4"> |
+A well designed script, implementing a suitable strategy, can enhance |
+the underlying <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> page, exploiting the browser's capabilities to the |
+maximum extent possible and still exhibit planed behaviour in the |
+absence of any (or all) desired features and degrade cleanly where |
+necessary. Nobody should either consider themselves a skilled Internet |
+javascript author, or deprecate javascript as a language and/or browser |
+scripting as a task, until they have demonstrated an ability to write |
+a non-trivial script that achieves that goal. |
+</p> |
+</body> |
+</html> |
/cljs/faq_notes/not_browser_detect.html |
---|
Property changes: |
Added: svn:mime-type |
## -0,0 +1 ## |
+text/plain |
\ No newline at end of property |