Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Various DOM-related wrappers (Code Worth Recommending Project)

3 views
Skip to first unread message

David Mark

unread,
Dec 8, 2007, 8:37:31 AM12/8/07
to
After a some reworking to support the "*" parameter properly in IE5
(my previous version failed to filter non-element nodes), here are my
proposals for various low-level DOM-related functions and the feature
tests that enable them to be created without issue. I welcome any
comments.

I just noticed that I left some of the function declarations as
variable declarations. There wasn't any specific reason for this,
they were just created differently in my framework.

var allElements, commonElementsByTagName, createElement,
documentElementsByTagName, element, elementElementsByTagName, doc,
headElement, html, htmlElement, i, xmlParseMode;
var filter, filterLegacy;
var canAdjustStyle, isStyleCapable, styles;
var reFeaturedMethod = new RegExp('^function|object$', 'i');
var global = this;

// Test for properties of host objects known not to be callable (e.g.
document nodes, elements)

var isRealObjectProperty = function(o, p) {
return !!(typeof(o[p]) == 'object' && o[p]);
};

// Test for host object properties that are typically callable (e.g.
all, getElementById),
// which may be of type function, object (IE and possibly others) or
unknown (IE ActiveX methods)

var isFeaturedMethod = function(o, m) {
var t = typeof(o[m]);
return !!((reFeaturedMethod.test(t) && o[m]) || t == 'unknown');
};

// Filter wrapper, which is included at this stage as it is needed for
the gEBTN workaround
// (all returns non-element nodes.)
// Standard filter wrapper is not useful for IE5.0 as it requires
Function.prototype.call.

if (Array.prototype.filter) {
filter = function(a, fn, context) { return a.filter(fn, context); };
}
else {
// Note that Array.prototype.reverse is not tested as it is from JS
1.1
if (Function.prototype.call) {
filter = function(a, fn, context) {
var l = a.length, r = [], c = 0;
context = context || global;
// Didn't want to use in operator and for in loop does not preserve
order
while (l--) {
if (typeof(l) != 'undefined') {
if (fn.call(context, a[l], a)) { r[c++] = a[l]; }
}
}
return r.reverse();
};
}
else {
// No simulated filter possible, so add a fallback with a slightly
different interface
// Parameter fn can be a string (method name) or function
// Context ignored unless fn is a string
filterLegacy = function(a, fn, context) {
var l = a.length, r = [], c = 0;
context = context || global;
fn = (typeof(fn) == 'string')?context[fn]:fn;
while (l--) {
if (typeof(l) != 'undefined') {
if (fn(a[l], a)) { r[c++] = a[l]; }
}
}
return r.reverse();
};
}
}


function elementFilter(o) {
// IE5 thinks comments and docTypes are elements.
// Second conjunction is for agents that don't support nodeType.
return (o.nodeType == 1 && o.tagName != '!') || (!o.nodeType &&
o.tagName);
}

// Used to convert array-like host objects to arrays
// IIRC, Array.prototype.slice didn't work with node lists
function toArray(o) {
var a = [];
var l = o.length;
while (l--) { a[l] = o[l]; }
return a;
}

if (isRealObjectProperty(this, 'document')) {
doc = this.document;

// gEBI wrapper
element = (function() {
function idCheck(el, id) {
return (el && el.id == id)?el:null;
}
if (isFeaturedMethod(doc, 'getElementById')) {
return function(id, docNode) { return idCheck((docNode ||
doc).getElementById(id), id); };
}
if (isFeaturedMethod(doc, 'all')) {
return function(id, docNode) { return idCheck((docNode ||
doc).all[id], id); };
}
})();


if (isFeaturedMethod(doc, 'all')) {
// Internal in my framework, called by: commonElementsByTagName and
htmlElement
allElements = (function() {
var fnFilter = filter || filterLegacy;
return function(el, bFilter) {
return (bFilter)?fnFilter(toArray(el.all), elementFilter):el.all;
};
})();
}

// Called by both gEBTN wrappers
commonElementsByTagName = (function() {
if (isFeaturedMethod(doc, 'all') && allElements) {
return function (el, t) {
return(t == '*' && el.all)?allElements(el,
true):el.getElementsByTagName(t);
};
}
return function (el, t) { return el.getElementsByTagName(t); };
})();

// Defined only if document nodes feature gEBTN or all.
// Returns an array or array-like host object.
documentElementsByTagName = (function() {
if (isFeaturedMethod(doc, 'getElementsByTagName')) {
return function(t, docNode) {
return commonElementsByTagName(docNode || doc, t);
};
}
if (isFeaturedMethod(doc, 'all') && isFeaturedMethod(doc.all,
'tags')) {
return function(t, docNode) {
return (docNode || doc).all.tags(t);
};
}
})();

// Returns the HTML element by default or optionally the first
element it finds.
htmlElement = function(docNode, bAnyElement) {
var html, all;
docNode = docNode || doc;
html = isRealObjectProperty(docNode, 'documentElement')?
docNode.documentElement:((documentElementsByTagName)?
documentElementsByTagName('html', docNode)[0]:null);
if (!html && allElements) {
all = allElements(docNode); // Don't bother to filter for this
html = all[(all[0].tagName == '!')?1:0];
if (html && !bAnyElement && html.tagName.toLowerCase() != 'html')
{ html = null; }
}
return html;
};

// Retrieve any element (what follows doesn't care which one)
html = htmlElement(doc, true);

// Note that the bodyElement function is not included yet as events
module is needed first
// That function (among others) is defined after the document is
ready.
if (documentElementsByTagName) {
headElement = function(docNode) {
return documentElementsByTagName('head', docNode || doc)[0] ||
null;
};
}

if (html) {
// Defined only if element nodes feature gEBTN or all.
// Returns an array or array-like host object.
elementElementsByTagName = (function() {
if (isFeaturedMethod(html, 'getElementsByTagName')) {
return commonElementsByTagName;
}
if (isFeaturedMethod(html, 'all') && isFeaturedMethod(html.all,
'tags')) {
return function(el, t) { return el.all.tags(t); };
}
})();

// MS has been known to implement elements as ActiveX objects
// (e.g. anchors that link to news resources)
isStyleCapable = isRealObjectProperty(html, 'style');

// These flags dictate which style-related functions should be
initialized
if (isStyleCapable) {
canAdjustStyle = {};
styles = ['display', 'visibility', 'position'];
i = 3;
while (i--) {
canAdjustStyle[styles[i]] = typeof(html.style[styles[i]]) ==
'string';
}
}
}

// This is called in two places,
// createElement (below) and elements (an XPath wrapper.)
// Each adds one additional line to work with XHTML (regardless of
MIME type.)
xmlParseMode = function(docNode) {
docNode = docNode || doc;
if (typeof(docNode.contentType) == 'string') {
return docNode.contentType.indexOf('xml') != -1;
}
else {
return typeof(docNode.body) == 'undefined';
}
};

// Included at this stage as it will be needed for feature testing
shortly
createElement = (function() {
if (doc.createElement) {
return (function() {
if (xmlParseMode() && doc.createElementNS) {
return function(tag, docNode) {
return (docNode || doc).createElementNS('http://www.w3.org/1999/
xhtml', 'html:' + tag);
};
}
return function(tag, docNode) {
return (docNode || doc).createElement(tag);
};
})();
}
})();
}

David Mark

unread,
Dec 8, 2007, 1:46:02 PM12/8/07
to
On Dec 8, 8:37 am, David Mark <dmark.cins...@gmail.com> wrote:

Correction:

filterLegacy = function(a, fn, context) {
var l = a.length, r = [], c = 0;
context = context || global;

while (l--) {
if (typeof(l) != 'undefined') {

if (((typeof(fn) == 'string')?context[fn]:fn)(a[l], a)) { r[c++] =
a[l]; }
}
}
return r.reverse();
};

I'm thinking this fallback doesn't really need to support context. It
is only used for one thing at the moment (the gEBTN "*" fix.)

Peter Michaux

unread,
Dec 8, 2007, 8:32:10 PM12/8/07
to
On Dec 8, 5:37 am, David Mark <dmark.cins...@gmail.com> wrote:
> After a some reworking to support the "*" parameter properly in IE5
> (my previous version failed to filter non-element nodes), here are my
> proposals for various low-level DOM-related functions and the feature
> tests that enable them to be created without issue. I welcome any
> comments.

[nitpick mode on]

Please use a 70 character line length to avoid wrapping. Usenet wraps
at 72 so a 70 character line allows one reply without wrapping.

A 2 space "tab stop" conserves line width.

Neither of these are my personal preferences but are appropriate to
code that will be posted to Usenet.

<URL: http://cljs.michaux.ca/trac/wiki/DesignGuidelines>

[nitpick mode off]

[code quoted below reformatted and reduced to just that related to the
gEBI wrapper]

> var element,
> doc,
> i;


> var reFeaturedMethod = new RegExp('^function|object$', 'i');
> var global = this;
>

> // Test for host object properties that are typically callable

> // (e.g. all, getElementById),


> // which may be of type function, object (IE and possibly others)

> // or unknown (IE ActiveX methods)


>
> var isFeaturedMethod = function(o, m) {
> var t = typeof(o[m]);
> return !!((reFeaturedMethod.test(t) && o[m]) || t == 'unknown');
> };
>
>

> if (isRealObjectProperty(this, 'document')) {
> doc = this.document;
>
> // gEBI wrapper
> element = (function() {
>
> function idCheck(el, id) {

> return (el && el.id == id) ? el : null;


> }
>
> if (isFeaturedMethod(doc, 'getElementById')) {
> return function(id, docNode) {
> return idCheck((docNode || doc).getElementById(id), id);
> };
> }
>
> if (isFeaturedMethod(doc, 'all')) {
> return function(id, docNode) {
> return idCheck((docNode || doc).all[id], id);
> };
> }
>
> })();
>
> }

Based on the repository design guidelines, the above could be
substantially reduced to just

if (document.getElementById) {
var getEBI = function(id, d) {
var el = (d||document).getElementById(id);
if (el.id == id) {
return el;
}
};
}

Notes:

There is no need to check for the existence of "document" as no
browser, NN4+ and IE4+, missing "document".

There is no need to check that document.getElementById is a callable
since there has never been a known implementation where
document.getElementById that is not callable.

Since this is a getter the name must start with "get". I'm tempted to
call it "getElementById" since that won't clash with
document.getElementById. "getEBI" is shorter.

It is clear that supporting IE4 requires quite a bit more code which
must be downloaded by everyone. I don't mean this to start a big
discussion about supporting IE4. It is just quite obvious in this
example.

--
Peter
Code Worth Recommending Project
http://cljs.michaux.ca

AKS

unread,
Dec 9, 2007, 12:00:42 AM12/9/07
to
On 8 дек, 23:46, David Mark <dmark.cins...@gmail.com> wrote:
> On Dec 8, 8:37 am, David Mark <dmark.cins...@gmail.com> wrote:

> var l = a.length
> ...


> if (typeof(l) != 'undefined')

What this checking is necessary for? The index of an array can be type
"undefined"?

Peter Michaux

unread,
Dec 9, 2007, 12:02:37 AM12/9/07
to
On Dec 8, 5:37 am, David Mark <dmark.cins...@gmail.com> wrote:

[code quoted below reformatted and reduced to just that related to the

gEBTN wrapper]

> var allElements, commonElementsByTagName,
> documentElementsByTagName, elementElementsByTagName,
> doc, html, htmlElement, i;
> var filter, filterLegacy;


> var reFeaturedMethod = new RegExp('^function|object$', 'i');
> var global = this;
>
> // Test for properties of host objects known not to be callable

> // (e.g.document nodes, elements)


>
> var isRealObjectProperty = function(o, p) {
> return !!(typeof(o[p]) == 'object' && o[p]);
> };
>
>
> // Test for host object properties that are typically callable

> // (e.g. all, getElementById),


> // which may be of type function, object (IE and possibly others)

> // or unknown (IE ActiveX methods)


>
> var isFeaturedMethod = function(o, m) {
> var t = typeof(o[m]);
> return !!((reFeaturedMethod.test(t) && o[m]) || t == 'unknown');
> };
>
>
> // Filter wrapper, which is included at this stage as it is needed

> // for the gEBTN workaround (all returns non-element nodes.)


> // Standard filter wrapper is not useful for IE5.0 as it requires

> // Function.prototype.call.


>
> if (Array.prototype.filter) {
> filter = function(a, fn, context) {
> return a.filter(fn, context);
> };
> }
> else {
> // Note that Array.prototype.reverse is not tested as it is

> // from JS 1.1


> if (Function.prototype.call) {
> filter = function(a, fn, context) {
> var l = a.length,
> r = [],
> c = 0;
> context = context || global;
> // Didn't want to use in operator and for in loop does

> // not preserve order


> while (l--) {
> if (typeof(l) != 'undefined') {
> if (fn.call(context, a[l], a)) {
> r[c++] = a[l];
> }
> }
> }
> return r.reverse();
> };
> }
> else {
> // No simulated filter possible, so add a fallback

> // with a slightly different interface


> // Parameter fn can be a string (method name) or function
> // Context ignored unless fn is a string
> filterLegacy = function(a, fn, context) {
> var l = a.length, r = [], c = 0;
> context = context || global;
> fn = (typeof(fn) == 'string')?context[fn]:fn;
> while (l--) {
> if (typeof(l) != 'undefined') {
> if (fn(a[l], a)) { r[c++] = a[l]; }
> }
> }
> return r.reverse();
> };
> }
> }
>
> function elementFilter(o) {
> // IE5 thinks comments and docTypes are elements.
> // Second conjunction is for agents that don't support nodeType.
> return (o.nodeType == 1 && o.tagName != '!') ||
> (!o.nodeType && o.tagName);
> }
>
> // Used to convert array-like host objects to arrays
> // IIRC, Array.prototype.slice didn't work with node lists
> function toArray(o) {
> var a = [];
> var l = o.length;
> while (l--) { a[l] = o[l]; }
> return a;
> }
>
> if (isRealObjectProperty(this, 'document')) {
> doc = this.document;
>

> if (isFeaturedMethod(doc, 'all')) {
> // Internal in my framework, called by:

> // commonElementsByTagName and htmlElement


> allElements = (function() {
> var fnFilter = filter || filterLegacy;
> return function(el, bFilter) {
> return (bFilter) ?
> fnFilter(toArray(el.all), elementFilter) :
> el.all;
> };
> })();
> }
>
> // Called by both gEBTN wrappers
> commonElementsByTagName = (function() {
> if (isFeaturedMethod(doc, 'all') && allElements) {
> return function(el, t) {
> return (t == '*' && el.all) ?
> allElements(el, true) :
> el.getElementsByTagName(t);
> };
> }

> return function(el, t) {


> return el.getElementsByTagName(t);
> };
> })();
>
> // Defined only if document nodes feature gEBTN or all.
> // Returns an array or array-like host object.
> documentElementsByTagName = (function() {
> if (isFeaturedMethod(doc, 'getElementsByTagName')) {
> return function(t, docNode) {
> return commonElementsByTagName(docNode || doc, t);
> };
> }
> if (isFeaturedMethod(doc, 'all') &&
> isFeaturedMethod(doc.all, 'tags')) {
> return function(t, docNode) {
> return (docNode || doc).all.tags(t);
> };
> }
> })();
>
> // Returns the HTML element by default or optionally

> // the first element it finds.


> htmlElement = function(docNode, bAnyElement) {
> var html, all;
> docNode = docNode || doc;
> html = isRealObjectProperty(docNode, 'documentElement') ?
> docNode.documentElement:((documentElementsByTagName)?
> documentElementsByTagName('html', docNode)[0] : null);
> if (!html && allElements) {

> all = allElements(docNode);//Don't bother to filter for this


> html = all[(all[0].tagName == '!')?1:0];
> if (html &&
> !bAnyElement &&
> html.tagName.toLowerCase() != 'html') {
> html = null;
> }
> }
> return html;
> };
>
> // Retrieve any element (what follows doesn't care which one)
> html = htmlElement(doc, true);
>

> if (html) {
> // Defined only if element nodes feature gEBTN or all.
> // Returns an array or array-like host object.
> elementElementsByTagName = (function() {
> if (isFeaturedMethod(html, 'getElementsByTagName')) {
> return commonElementsByTagName;
> }
> if (isFeaturedMethod(html, 'all') &&
> isFeaturedMethod(html.all, 'tags')) {
> return function(el, t) { return el.all.tags(t); };
> }
> })();
>
> }
>
> }

Whoa, that is a lot of code!

I didn't make a time test but I think the simulation of
Array.prototype.filter will be very slow. I made a CSS selector
function one time (something like jQuery has) and I found that any
code that will operate on any medium sized DOM needs to be very fast.
I also made a test between just simply looping over an array verses
using a function that takes another function and iterates over all of
an array. A simple loop wins by a long shot.

For the repository, the above code could be substantially reduced to
something like the following which will run much faster.

// -----------------------------------------

// from another file in the respository
if (document.documentElement) {
var getAnElement = function(d) {
return (d || document).documentElement;
};
}

// -----------------------------------------

// One implementation for developers not concerned with IE5
// problem of the browser thinking doctype and comments
// are elements.

if (document.getElementsByTagName &&
typeof getAnElement != 'undefined' &&
getAnElement().getElementsByTagName) {

var getEBTN = function(tag, root) {
var els = root.getElementsByTagName(tag);
if (tag == '*' && !els.length && root.all) {
els = root.all;
}
return els;
};

}

// -----------------------

// Another implementation for developers concerned with
// the IE5 problem about doctype and comments

if (document.getElementsByTagName &&
typeof getAnElement != 'undefined' &&
getAnElement().getElementsByTagName) {

var getEBTN = function(tag, root) {
var els = root.getElementsByTagName(tag);

if (tag == '*' && !els.length && root.all) {
var all = root.all;
els = [];
for (var i=0, ilen=all.length; i<ilen; i++) {
var el = all[i];
// The following conditional could be factored out
// if necessary.
if ((el.nodeType == 1 && el.tagName != '!') ||
(!el.nodeType && el.tagName)) {
els[els.length] = el;
}
}
}

return els;
};
}

I haven't actually verified the IE5 bug yet and tested the above in a
wide set of browsers. I'm just looking at the packaging of the
concepts.

I think there is no need for two getEBTN wrappers because, I think,
all of the known browsers that have document.getElementByTagName also
have element.getElementByTagName. It seems overly paranoid to think
that sometime in a future such a browser will be created. The reason
for two feature tests is because document.getElementsByTagName is
defined in the DOM2 standard as part of Document and the other
getElementsByTagName is defined for Element so a test only for one
could be construed as object inference.

Even though the two getEBTN wrappers use different fallbacks for IE4
it looks like they could be combined into one just like I've done here
with no real performance penalty. If some browsers had document.all
and not element.all then that would be a problem but has that ever
been the case?

The API should be considered here. A getEBTN wrapper function like the
above could be called "getByCssSelector" and the calls to getEBTN
would become a subset of the possible calls to "getByCssSelector".
There is a standard in the works for something like
"document.getByCssSelector". So eventually there would be a very fast
host object that could be wrapped.

I think that having getEBTN as it's own function is good and both
getEBID and getEBTN could be wrapped in a function getByCssSelector.
This is something I'd like to add to the repository eventually. I did
a couple weeks of work on these types of functions a while ago and it
is very handy for enlivening pages for unobtrusive JavaScript. (Cue
"PointedEars" for a "lemmings" comment.)

Peter Michaux

unread,
Dec 9, 2007, 12:58:22 AM12/9/07
to
On Dec 8, 5:32 pm, Peter Michaux <petermich...@gmail.com> wrote:
> On Dec 8, 5:37 am, David Mark <dmark.cins...@gmail.com> wrote:

[snip about gEBI wrappers for the repository]

> if (document.getElementById) {
> var getEBI = function(id, d) {
> var el = (d||document).getElementById(id);
> if (el.id == id) {
> return el;
> }
> };
>
> }

// the simplest possible implementation

if (document.getElementById) {
var getEBI = function(id, d) {

return (d||document).getElementById(id);
};
}

// -------------------------------------------

// an implementation that *fixes* the IE name/id bug
// I don't know what happens in the "hunt" if there
// are frames, iframes, or object elements in the DOM

var getEBI = (function() {

var el;

if (document.getElementById &&


typeof getAnElement != 'undefined' &&

((el=getAnElement()).firstChild || el.firstChild === null) &&
(el.nextSibling || el.nextSibling === null)) {

return function(id, d) {


var el = (d || document).getElementById(id);

if (el && el.id == id) {
return el;
}

function hunt(node, id) {
if (node.id == id) {
return node;
}
node = node.firstChild;

while (node) {
var el = hunt(node, id);
if (el) {
return el;
}
node = node.nextSibling;
}
}

return hunt(d, id);
}

}
})();

Evertjan.

unread,
Dec 9, 2007, 4:14:27 AM12/9/07
to
Peter Michaux wrote on 09 dec 2007 in comp.lang.javascript:

> // the simplest possible implementation
>
> if (document.getElementById) {
> var getEBI = function(id, d) {
> return (d||document).getElementById(id);
> };
>}
>

What is the use of testing for getElementById,
as the js code will error anyway
when getEBI() is subsequently called while not defined?

// the simplest possible implementation

var getEBI = function(id, d) {
return (d||document).getElementById(id);
};


--
Evertjan.
The Netherlands.
(Please change the x'es to dots in my emailaddress)

Thomas 'PointedEars' Lahn

unread,
Dec 9, 2007, 4:39:47 AM12/9/07
to
Peter Michaux wrote:
> Based on the repository design guidelines, the above could be
> substantially reduced to just
> [...]

> Notes:
>
> There is no need to check for the existence of "document" as no
> browser, NN4+ and IE4+, missing "document".
>
> There is no need to check that document.getElementById is a callable
> since there has never been a known implementation where
> document.getElementById that is not callable.

If these are the guidelines for your project, to produce code that is not
interoperable and inherently unreliable, I don't want to contribute.


PointedEars

VK

unread,
Dec 9, 2007, 4:51:30 AM12/9/07
to
On Dec 9, 4:32 am, Peter Michaux <petermich...@gmail.com> wrote:
> Based on the repository design guidelines, the above could be
> substantially reduced to just
>
> if (document.getElementById) {
> var getEBI = function(id, d) {
> var el = (d||document).getElementById(id);
> if (el.id == id) {
> return el;
> }
> };
>
> }

That is an absolutely terrible way to implement a _runtime_ method.
DOM interface calls are the most used in any script and they have the
greatest impact to the performance. JS <=> DOM calls already much
slower then JS <=> JS calls and cutting their performance even further
for any bit is a crime to be punished :-) :-|

Features/environment sniffing has to be done only _once_ on the
instantiation stage and _never_ in a loop on each invocation. So if
anyone still really cares of document.getElementById support - nobody
does in the world except some people in this NG but let's play
academics - then it must be function reference conditionally set once
for the identifier:
if (condition_1) {
$ = function(id){/* do this */};
}
else if (condition_2) {
$ = function(id){/* do that */};
}
else {
$ = function(id){/* do something */};
}
so on the further run $(id) will call the appropriate direct method.

David Mark

unread,
Dec 9, 2007, 5:01:24 AM12/9/07
to
On Dec 9, 12:00 am, AKS <aksus...@yandex.ru> wrote:

Typo. Should have been checking a[l].

David Mark

unread,
Dec 9, 2007, 5:04:00 AM12/9/07
to

A lot of the extra code is for the IE5 problem with the "*" parameter
(gEBTN.)

Thomas 'PointedEars' Lahn

unread,
Dec 9, 2007, 5:16:18 AM12/9/07
to
VK wrote:
> On Dec 9, 4:32 am, Peter Michaux <petermich...@gmail.com> wrote:
>> Based on the repository design guidelines, the above could be
>> substantially reduced to just
>>
>> if (document.getElementById) {
>> var getEBI = function(id, d) {
>> var el = (d||document).getElementById(id);
>> if (el.id == id) {
>> return el;
>> }
>> };
>>
>> }
>
> That is an absolutely terrible way to implement a _runtime_ method.
> DOM interface calls are the most used in any script and they have the
> greatest impact to the performance. JS <=> DOM calls already much
> slower then JS <=> JS calls and cutting their performance even further
> for any bit is a crime to be punished :-) :-|

I have to agree with VK here. Lacking necessary feature testing (instead
of mere property true-value testing) aside, it is not necessary and it is
inefficient to do the test every time the feature is used.

That is why I refactored my dhtml.js recently to use factory functions to
assign suitable function references to the wrapper object's properties.


PointedEars
--
"Use any version of Microsoft Frontpage to create your site. (This won't
prevent people from viewing your source, but no one will want to steal it.)"
-- from <http://www.vortex-webdesign.com/help/hidesource.htm>

David Mark

unread,
Dec 9, 2007, 5:24:41 AM12/9/07
to
On Dec 9, 12:02 am, Peter Michaux <petermich...@gmail.com> wrote:
[snip]

>
> Whoa, that is a lot of code!

I thought it best to start with every possibility and whittle it down
from there.

>
> I didn't make a time test but I think the simulation of
> Array.prototype.filter will be very slow. I made a CSS selector

Slow is relative. I don't know of any way to simulate filter any
faster. That's why I never use the "*" selector for gEBTN. I
included it only to illustrate what is required to work around that
problem.

> function one time (something like jQuery has) and I found that any

I have found that a combination of gEBI and gEBTN is all I ever need
to home in on whatever elements I need to manipulate.

> code that will operate on any medium sized DOM needs to be very fast.

As long as you don't use "*" with gEBTN, then these functions perform
well.

> I also made a test between just simply looping over an array verses
> using a function that takes another function and iterates over all of
> an array. A simple loop wins by a long shot.

Without question. It will create longer functions that duplicate
code, but that may be the way to go in this case.

>
> For the repository, the above code could be substantially reduced to
> something like the following which will run much faster.
>
> // -----------------------------------------
>
> // from another file in the respository
> if (document.documentElement) {
> var getAnElement = function(d) {
> return (d || document).documentElement;
> };
>
> }
>
> // -----------------------------------------
>
> // One implementation for developers not concerned with IE5
> // problem of the browser thinking doctype and comments
> // are elements.
>
> if (document.getElementsByTagName &&
> typeof getAnElement != 'undefined' &&
> getAnElement().getElementsByTagName) {
>
> var getEBTN = function(tag, root) {
> var els = root.getElementsByTagName(tag);
> if (tag == '*' && !els.length && root.all) {
> els = root.all;
> }
> return els;
> };
>
> }

That works for that case.

>
> // -----------------------
>
> // Another implementation for developers concerned with
> // the IE5 problem about doctype and comments
>
> if (document.getElementsByTagName &&
> typeof getAnElement != 'undefined' &&
> getAnElement().getElementsByTagName) {
>
> var getEBTN = function(tag, root) {
> var els = root.getElementsByTagName(tag);
>
> if (tag == '*' && !els.length && root.all) {
> var all = root.all;
> els = [];
> for (var i=0, ilen=all.length; i<ilen; i++) {
> var el = all[i];

I prefer not to declare variables inside of loops. And for long
loops, a while that counts down to 0 will be faster. Granted, you
have to reverse the results at the end.

> // The following conditional could be factored out
> // if necessary.
> if ((el.nodeType == 1 && el.tagName != '!') ||
> (!el.nodeType && el.tagName)) {
> els[els.length] = el;
> }
> }
> }
>
> return els;
> };
>
> }
>
> I haven't actually verified the IE5 bug yet and tested the above in a
> wide set of browsers. I'm just looking at the packaging of the
> concepts.

It isn't actually a bug, but a limitation of IE5's implementation of
gEBTN. It just doens't handle "*".

>
> I think there is no need for two getEBTN wrappers because, I think,
> all of the known browsers that have document.getElementByTagName also
> have element.getElementByTagName. It seems overly paranoid to think
> that sometime in a future such a browser will be created. The reason

I was actually more concerned with past browsers that may have
incomplete implementations of gEBTN.

> for two feature tests is because document.getElementsByTagName is
> defined in the DOM2 standard as part of Document and the other
> getElementsByTagName is defined for Element so a test only for one
> could be construed as object inference.

Right. That's why I avoided it.

>
> Even though the two getEBTN wrappers use different fallbacks for IE4
> it looks like they could be combined into one just like I've done here
> with no real performance penalty. If some browsers had document.all
> and not element.all then that would be a problem but has that ever
> been the case?

That one isn't likely. It was the gEBTN inference that I wanted to
avoid. The previous discussion about this stemmed from the fact that
most libraries make similar inferences about addEventListener (which
requires three implementations: window, document and element.) It was
noted that agents do exist that support that method for elements, but
not for window.

>
> The API should be considered here. A getEBTN wrapper function like the
> above could be called "getByCssSelector" and the calls to getEBTN
> would become a subset of the possible calls to "getByCssSelector".

If you feel we must have a getByCssSelector, then that makes sense. I
can't imagine a scenario where I would use such a thing.

> There is a standard in the works for something like
> "document.getByCssSelector". So eventually there would be a very fast
> host object that could be wrapped.

Right.

>
> I think that having getEBTN as it's own function is good and both
> getEBID and getEBTN could be wrapped in a function getByCssSelector.

That is similar to what I do, except they are both wrapped in one
branch of an XPath wrapper (for browsers that don't support XPath.)

> This is something I'd like to add to the repository eventually. I did
> a couple weeks of work on these types of functions a while ago and it
> is very handy for enlivening pages for unobtrusive JavaScript. (Cue
> "PointedEars" for a "lemmings" comment.)

I haven't found a need for such queries and I agree with "PointedEars"
in that event delegation is often the best way to go anyway.

David Mark

unread,
Dec 9, 2007, 5:27:57 AM12/9/07
to
On Dec 9, 12:58 am, Peter Michaux <petermich...@gmail.com> wrote:
> On Dec 8, 5:32 pm, Peter Michaux <petermich...@gmail.com> wrote:
>
> > On Dec 8, 5:37 am, David Mark <dmark.cins...@gmail.com> wrote:
>
> [snip about gEBI wrappers for the repository]
>
> > if (document.getElementById) {
> > var getEBI = function(id, d) {
> > var el = (d||document).getElementById(id);
> > if (el.id == id) {
> > return el;
> > }
> > };
>
> > }
>
> // the simplest possible implementation
>
> if (document.getElementById) {
> var getEBI = function(id, d) {
> return (d||document).getElementById(id);
> };
>
> }
>
> // -------------------------------------------
>
> // an implementation that *fixes* the IE name/id bug
> // I don't know what happens in the "hunt" if there
> // are frames, iframes, or object elements in the DOM

Frames and IFrames won't enter into it.

>
> var getEBI = (function() {
>
> var el;
>
> if (document.getElementById &&
> typeof getAnElement != 'undefined' &&
> ((el=getAnElement()).firstChild || el.firstChild === null) &&
> (el.nextSibling || el.nextSibling === null)) {
>
> return function(id, d) {
> var el = (d || document).getElementById(id);
> if (el && el.id == id) {
> return el;
> }
>
> function hunt(node, id) {
> if (node.id == id) {
> return node;
> }
> node = node.firstChild;
>
> while (node) {
> var el = hunt(node, id);
> if (el) {
> return el;
> }
> node = node.nextSibling;
> }
> }
>
> return hunt(d, id);
> }
>
> }
>
> })();

I thought about adding this, but decided it was too much extra code to
enable authors to write poor markup. It might be better to have the
function return null in such a case as it will clue the author that
something is wrong.

David Mark

unread,
Dec 9, 2007, 5:32:43 AM12/9/07
to
On Dec 9, 4:14 am, "Evertjan." <exjxw.hannivo...@interxnl.net> wrote:
> Peter Michaux wrote on 09 dec 2007 in comp.lang.javascript:
>
> > // the simplest possible implementation
>
> > if (document.getElementById) {
> > var getEBI = function(id, d) {
> > return (d||document).getElementById(id);
> > };
> >}
>
> What is the use of testing for getElementById,
> as the js code will error anyway
> when getEBI() is subsequently called while not defined?

The idea is that applications can more simply feature test API
abstractions than the individual features abstracted. Granted, this
particular example is too simple to illustrate this concept.

>
> // the simplest possible implementation
>
> var getEBI = function(id, d) {
> return (d||document).getElementById(id);
>
> };

In this particular case, that would be just as good (it isn't any more
complex to test getElementById.) But as soon as additional features
are abstracted by getEBI (e.g. document.all), it becomes simpler to
test the wrapper.

David Mark

unread,
Dec 9, 2007, 5:34:40 AM12/9/07
to
On Dec 9, 4:39 am, Thomas 'PointedEars' Lahn <PointedE...@web.de>
wrote:

I somewhat agree with that sentiment, which is why I presented both
tests, though not contributing at all would seem an extreme position.

David Mark

unread,
Dec 9, 2007, 5:37:07 AM12/9/07
to
On Dec 9, 4:51 am, VK <schools_r...@yahoo.com> wrote:
> On Dec 9, 4:32 am, Peter Michaux <petermich...@gmail.com> wrote:
>
> > Based on the repository design guidelines, the above could be
> > substantially reduced to just
>
> > if (document.getElementById) {
> > var getEBI = function(id, d) {
> > var el = (d||document).getElementById(id);
> > if (el.id == id) {
> > return el;
> > }
> > };
>
> > }
>
> That is an absolutely terrible way to implement a _runtime_ method.
> DOM interface calls are the most used in any script and they have the
> greatest impact to the performance. JS <=> DOM calls already much
> slower then JS <=> JS calls and cutting their performance even further
> for any bit is a crime to be punished :-) :-|
>
> Features/environment sniffing has to be done only _once_ on the
> instantiation stage and _never_ in a loop on each invocation.

Read it again. It is done only _once_.

David Mark

unread,
Dec 9, 2007, 7:32:40 AM12/9/07
to
On Dec 9, 12:02 am, Peter Michaux <petermich...@gmail.com> wrote:

[snip]

>
> I didn't make a time test but I think the simulation of
> Array.prototype.filter will be very slow. I made a CSS selector

Considering that the use of the filter wrapper only helps IE5 and
under, it does make sense to duplicate its functionality in the gEBTN
wrapper. But that function and other JS1.6 array method wrappers are
useful for other purposes where speed is less of an issue. I think
that we should deal with those shortly.

Looking at the rest of my "base" module, I think these topics should
be considered next (in no particular order.)

These extend what is currently being discussed:
- Get child elements
- Get first and last child element (with optionally specified tagName)
- Get HTML and head elements

These are needed to complete basic feature testing support:
- Style support detection
- createElement and XML parse mode detection
- Element parent and owner document
- Element "canHaveChildren" test

Array and object testing, traversal and mutation:
- isArray
- push/pop
- 1.6 Array method wrappers (filter, map, some, every, forEach, etc.)
- For in filter.

Events (needed to support more advanced feature testing):
- addEventListener wrapper
- DOMContentLoaded simulation

Miscellaneous:
- Get and set element text
- Cookies (with optional support for ASP's keyed values.)
- Plug-in detection

I realize that the task of finalizing, adding and documenting each is
a bottleneck, but perhaps we should start discussing some of these in
parallel. Everybody has their own areas of interest, so this may
increase overall participation in the project.

Peter Michaux

unread,
Dec 9, 2007, 10:38:38 AM12/9/07
to
On Dec 9, 1:14 am, "Evertjan." <exjxw.hannivo...@interxnl.net> wrote:
> Peter Michaux wrote on 09 dec 2007 in comp.lang.javascript:
>
> > // the simplest possible implementation
>
> > if (document.getElementById) {
> > var getEBI = function(id, d) {
> > return (d||document).getElementById(id);
> > };
> >}
>
> What is the use of testing for getElementById,
> as the js code will error anyway
> when getEBI() is subsequently called while not defined?

The idea is to avoid errors.

A function that calls getEBI should only test for the existance of
gEBI and not document.getElementById. This is because the
implementation of getEBI may be changed and may not use
document.getElementById.

Peter Michaux

unread,
Dec 9, 2007, 10:46:37 AM12/9/07
to
On Dec 9, 1:39 am, Thomas 'PointedEars' Lahn <PointedE...@web.de>
wrote:

Do you feature test everything?

Do you feature test that alert is callable?

Do you feature test that string concatenation works?

Do you feature test that float division works (e.g. 1/2 is 0.5)?

Do you feature test for the bugs in the implementation of feature "x"
in browser "y" that you have never seen? You could almost do this by
an insanely laborious process of checking things like float division
in 100 different combinations before doing float division.

Hopefully you answer "no" to at least one of those question. If so
then you see that the line has to be drawn somewhere.

Peter Michaux

unread,
Dec 9, 2007, 10:49:08 AM12/9/07
to
On Dec 9, 2:34 am, David Mark <dmark.cins...@gmail.com> wrote:
> On Dec 9, 4:39 am, Thomas 'PointedEars' Lahn <PointedE...@web.de>
> wrote:
>
> > Peter Michaux wrote:
> > > Based on the repository design guidelines, the above could be
> > > substantially reduced to just
> > > [...]
> > > Notes:
>
> > > There is no need to check for the existence of "document" as no
> > > browser, NN4+ and IE4+, missing "document".
>
> > > There is no need to check that document.getElementById is a callable
> > > since there has never been a known implementation where
> > > document.getElementById that is not callable.
>
> > If these are the guidelines for your project, to produce code that is not
> > interoperable and inherently unreliable, I don't want to contribute.
>
> I somewhat agree with that sentiment,

I would say your codes says otherwise and that you agree that a line
for feature testing should be drawn somewhere.

[quoted from the original post in this thread]

> // Note that Array.prototype.reverse is not

> // tested as it is from JS 1.1

Peter Michaux

unread,
Dec 9, 2007, 10:51:53 AM12/9/07
to
On Dec 9, 1:51 am, VK <schools_r...@yahoo.com> wrote:
> On Dec 9, 4:32 am, Peter Michaux <petermich...@gmail.com> wrote:
>
> > Based on the repository design guidelines, the above could be
> > substantially reduced to just
>
> > if (document.getElementById) {
> > var getEBI = function(id, d) {
> > var el = (d||document).getElementById(id);
> > if (el.id == id) {
> > return el;
> > }
> > };
>
> > }
>
> That is an absolutely terrible way to implement a _runtime_ method.
> DOM interface calls are the most used in any script and they have the
> greatest impact to the performance. JS <=> DOM calls already much
> slower then JS <=> JS calls and cutting their performance even further
> for any bit is a crime to be punished :-) :-|
>
> Features/environment sniffing has to be done only _once_ on the
> instantiation stage

In the code I posted the feature testing is only done once.

David Mark

unread,
Dec 9, 2007, 10:56:30 AM12/9/07
to
On Dec 9, 10:49 am, Peter Michaux <petermich...@gmail.com> wrote:
> On Dec 9, 2:34 am, David Mark <dmark.cins...@gmail.com> wrote:
>
>
>
>
>
> > On Dec 9, 4:39 am, Thomas 'PointedEars' Lahn <PointedE...@web.de>
> > wrote:
>
> > > Peter Michaux wrote:
> > > > Based on the repository design guidelines, the above could be
> > > > substantially reduced to just
> > > > [...]
> > > > Notes:
>
> > > > There is no need to check for the existence of "document" as no
> > > > browser, NN4+ and IE4+, missing "document".
>
> > > > There is no need to check that document.getElementById is a callable
> > > > since there has never been a known implementation where
> > > > document.getElementById that is not callable.
>
> > > If these are the guidelines for your project, to produce code that is not
> > > interoperable and inherently unreliable, I don't want to contribute.
>
> > I somewhat agree with that sentiment,
>
> I would say your codes says otherwise and that you agree that a line
> for feature testing should be drawn somewhere.

I was talking about the issue he was referring to (gEBTN for document
vs. element.) At least I think that was what he was referring to. In
any event, I don't think that everything should be feature tested or
that the position of boycotting the project otherwise is a reasonable
one.

>
> [quoted from the original post in this thread]
>
> > // Note that Array.prototype.reverse is not
> > // tested as it is from JS 1.1

I definitely don't bother testing anything from 1.1. That is a line I
have drawn in my own framework.

Peter Michaux

unread,
Dec 9, 2007, 11:09:57 AM12/9/07
to
On Dec 9, 2:24 am, David Mark <dmark.cins...@gmail.com> wrote:
> On Dec 9, 12:02 am, Peter Michaux <petermich...@gmail.com> wrote:

[snip]

> > I made a CSS selector function one time (something like


> > jQuery has) and I found that any
>
> I have found that a combination of gEBI and gEBTN is all I ever need
> to home in on whatever elements I need to manipulate.

I use something like a a getElementsByClassName sometimes but that
otherwise that is my limit also.

[snip]


> > // Another implementation for developers concerned with
> > // the IE5 problem about doctype and comments
>
> > if (document.getElementsByTagName &&
> > typeof getAnElement != 'undefined' &&
> > getAnElement().getElementsByTagName) {
>
> > var getEBTN = function(tag, root) {
> > var els = root.getElementsByTagName(tag);
>
> > if (tag == '*' && !els.length && root.all) {
> > var all = root.all;
> > els = [];
> > for (var i=0, ilen=all.length; i<ilen; i++) {
> > var el = all[i];
>
> I prefer not to declare variables inside of loops. And for long
> loops, a while that counts down to 0 will be faster. Granted, you
> have to reverse the results at the end.

I prefer countdown loops also but don't use them when order matters.


> > // The following conditional could be factored out
> > // if necessary.
> > if ((el.nodeType == 1 && el.tagName != '!') ||
> > (!el.nodeType && el.tagName)) {
> > els[els.length] = el;
> > }
> > }
> > }
>
> > return els;
> > };
>
> > }
>
> > I haven't actually verified the IE5 bug yet and tested the above in a
> > wide set of browsers. I'm just looking at the packaging of the
> > concepts.
>
> It isn't actually a bug, but a limitation of IE5's implementation of
> gEBTN. It just doens't handle "*".

Fair enough.

[snip]

> > Even though the two getEBTN wrappers use different fallbacks for IE4
> > it looks like they could be combined into one just like I've done here
> > with no real performance penalty. If some browsers had document.all
> > and not element.all then that would be a problem but has that ever
> > been the case?
>
> That one isn't likely. It was the gEBTN inference that I wanted to
> avoid. The previous discussion about this stemmed from the fact that
> most libraries make similar inferences about addEventListener (which
> requires three implementations: window, document and element.) It was
> noted that agents do exist that support that method for elements, but
> not for window.

Do you have a link to that previous discussion? If so please post it
when we get to events. I didn't know such a case existed.


> > The API should be considered here. A getEBTN wrapper function like the
> > above could be called "getByCssSelector" and the calls to getEBTN
> > would become a subset of the possible calls to "getByCssSelector".
>
> If you feel we must have a getByCssSelector,

I don't think it is a must.

> then that makes sense. I
> can't imagine a scenario where I would use such a thing.

I can only imagine myself using one that takes simple selectors like
"div", "#fooId", ".fooClass" and combinations of those three. A more
complex selector is only needed if the content creator is totally
uncooperative and won't add the hooks needed by the JavaScript
programmer.

The programming of a getByCssSelector sure is fun. Probably the most
enjoyable thing I've programmed in JavaScript that might be useful as
it involves a CSS selector string parser and optimization is a major
priority.

[snip]

> > This is something I'd like to add to the repository eventually. I did
> > a couple weeks of work on these types of functions a while ago and it
> > is very handy for enlivening pages for unobtrusive JavaScript. (Cue
> > "PointedEars" for a "lemmings" comment.)
>
> I haven't found a need for such queries and I agree with "PointedEars"

That is the fourth and a half time that has happened recently.

> in that event delegation is often the best way to go anyway.

I don't see them as mutually exclusive as there are only so many
elements on which to delegate and listeners concerned with different
handlers should not be combined. I really don't get what he is talking
about, he has told me I don't understand, and he is write.

Peter Michaux

unread,
Dec 9, 2007, 11:20:26 AM12/9/07
to

Yes. I am working on an automated testing system now and that will
help greatly. A documentation format has not be worked out. These are
both just as important to the project as the actual code.


> but perhaps we should start discussing some of these in
> parallel.

No more than two at a time and maybe with a bit of a breather in
between each topic. Folks that want to keep up but have to do actual
work will be overwhelmed. I felt this project was a bit too demanding
on my time this past week. There is no timeline and slow with more
time to digest and think about things is better than fast and rushed.

> Everybody has their own areas of interest,

indeed

> so this may
> increase overall participation in the project.

My preference is to focus on developing bigger modules and then the
smaller things you've listed above will be added in as necessary. This
helps us answer frequently asked questions like "Which Ajax library
should I use?" Ajax and event modules seem to be the first logical
modules to me. Already the functions prerequisite to Ajax have been
discussed so I would like to do that after gEBI and gEBTN. After the
gEBTN, gEBI and Ajax parts are finished the code repository will have
enough bulk to consider it in general and develop testing and
documentation systems before continuing with more features.

Peter Michaux

unread,
Dec 9, 2007, 11:28:55 AM12/9/07
to
On Dec 9, 7:56 am, David Mark <dmark.cins...@gmail.com> wrote:
> On Dec 9, 10:49 am, Peter Michaux <petermich...@gmail.com> wrote:
>
> > On Dec 9, 2:34 am, David Mark <dmark.cins...@gmail.com> wrote:
>
> > > On Dec 9, 4:39 am, Thomas 'PointedEars' Lahn <PointedE...@web.de>
> > > wrote:
>
> > > > Peter Michaux wrote:
> > > > > Based on the repository design guidelines, the above could be
> > > > > substantially reduced to just
> > > > > [...]
> > > > > Notes:
>
> > > > > There is no need to check for the existence of "document" as no
> > > > > browser, NN4+ and IE4+, missing "document".
>
> > > > > There is no need to check that document.getElementById is a callable
> > > > > since there has never been a known implementation where
> > > > > document.getElementById that is not callable.
>
> > > > If these are the guidelines for your project, to produce code that is not
> > > > interoperable and inherently unreliable, I don't want to contribute.
>
> > > I somewhat agree with that sentiment,
>
> > I would say your codes says otherwise and that you agree that a line
> > for feature testing should be drawn somewhere.
>
> I was talking about the issue he was referring to (gEBTN for document
> vs. element.)

I didn't even think of that difference. I never use
element.getElementById because an id is unique there is no real
difference (except perhaps performance by narrowing the search scope).
I agree there should be a feature test for both situations if the
particular implementation is documented to work in both situations.
For example

if (document.getElementsByTagName &&
typeof getAnElement != 'undefined' &&
getAnElement().getElementsByTagName) {

var getEBI = function(id, d) {
return (d||document).getElementById(id);
};

}

[snip]

Evertjan.

unread,
Dec 9, 2007, 11:31:18 AM12/9/07
to
Peter Michaux wrote on 09 dec 2007 in comp.lang.javascript:

> On Dec 9, 1:14 am, "Evertjan." <exjxw.hannivo...@interxnl.net> wrote:
>> Peter Michaux wrote on 09 dec 2007 in comp.lang.javascript:
>>
>> > // the simplest possible implementation
>>
>> > if (document.getElementById) {
>> > var getEBI = function(id, d) {
>> > return (d||document).getElementById(id);
>> > };
>> >}
>>
>> What is the use of testing for getElementById,
>> as the js code will error anyway
>> when getEBI() is subsequently called while not defined?
>
> The idea is to avoid errors.
>
> A function that calls getEBI should only test for the existance of
> gEBI and not document.getElementById. This is because the
> implementation of getEBI may be changed and may not use
> document.getElementById.

Seems a bit stupid to me, Peter.

If you plan to avoid any possible implementation change,
better not programme at all.

why not perhaps:

if (document.getElementById) {
var getEBI = function(id, d) {
return (d||document).getElementById(id);
};

} else {


var getEBI = function(id, d) {

return d.getElementById(id);

Peter Michaux

unread,
Dec 9, 2007, 11:43:28 AM12/9/07
to
On Dec 9, 8:31 am, "Evertjan." <exjxw.hannivo...@interxnl.net> wrote:
> Peter Michaux wrote on 09 dec 2007 in comp.lang.javascript:
>
> > On Dec 9, 1:14 am, "Evertjan." <exjxw.hannivo...@interxnl.net> wrote:
> >> Peter Michaux wrote on 09 dec 2007 in comp.lang.javascript:
>
> >> > // the simplest possible implementation
>
> >> > if (document.getElementById) {
> >> > var getEBI = function(id, d) {
> >> > return (d||document).getElementById(id);
> >> > };
> >> >}
>
> >> What is the use of testing for getElementById,
> >> as the js code will error anyway
> >> when getEBI() is subsequently called while not defined?
>
> > The idea is to avoid errors.
>
> > A function that calls getEBI should only test for the existance of
> > gEBI and not document.getElementById. This is because the
> > implementation of getEBI may be changed and may not use
> > document.getElementById.
>
> Seems a bit stupid to me, Peter.

I don't think there is any need for inflammatory words like "stupid".

> If you plan to avoid any possible implementation change,
> better not programme at all.

I'm not suggesting a gEBI wrapper should be written that doesn't use
getEBI. I'm speaking abou the principle that a function should only
test the features it calls directly to help to build a library in
layers with good modularity.

> if (document.getElementById) {
> var getEBI = function(id, d) {
> return (d||document).getElementById(id);
> };} else {
>
> var getEBI = function(id, d) {
> return d.getElementById(id);
> };
>
> };

This implementation confuses me a bit. What is the goal of the feature
testing in that implementation? If the else is the appropriate branch
and someone sends the second function "document" for the second
parameter it will error. Avoiding errors is the goal.

David Mark

unread,
Dec 9, 2007, 11:49:07 AM12/9/07
to
On Dec 9, 11:09 am, Peter Michaux <petermich...@gmail.com> wrote:
> On Dec 9, 2:24 am, David Mark <dmark.cins...@gmail.com> wrote:
>
> > On Dec 9, 12:02 am, Peter Michaux <petermich...@gmail.com> wrote:
>
> [snip]
>
> > > I made a CSS selector function one time (something like
> > > jQuery has) and I found that any
>
> > I have found that a combination of gEBI and gEBTN is all I ever need
> > to home in on whatever elements I need to manipulate.
>
> I use something like a a getElementsByClassName sometimes but that
> otherwise that is my limit also.

Yes, I occasionally find that useful. The biggest problem with it
(like gEBTN) is IE and other browsers that don't support XPath. It is
just too slow, so I try to avoid designs that rely on it.

>
> [snip]
>
>
>
>
>
> > > // Another implementation for developers concerned with
> > > // the IE5 problem about doctype and comments
>
> > > if (document.getElementsByTagName &&
> > > typeof getAnElement != 'undefined' &&
> > > getAnElement().getElementsByTagName) {
>
> > > var getEBTN = function(tag, root) {
> > > var els = root.getElementsByTagName(tag);
>
> > > if (tag == '*' && !els.length && root.all) {
> > > var all = root.all;
> > > els = [];
> > > for (var i=0, ilen=all.length; i<ilen; i++) {
> > > var el = all[i];
>
> > I prefer not to declare variables inside of loops. And for long
> > loops, a while that counts down to 0 will be faster. Granted, you
> > have to reverse the results at the end.
>
> I prefer countdown loops also but don't use them when order matters.

Is there that much of a performance hit with calling reverse on the
end result? It seemed like a good idea to me as the loop is n
operations, but the reverse is always one.

[snip]

>
> Do you have a link to that previous discussion? If so please post it

No.

> when we get to events. I didn't know such a case existed.

The source of that piece of information was Richard Cornford. The
discussion concerning splitting up addEventListener and gEBTN wrappers
was in a single thread. A search for "addEventListener" and
"getElementsByTagName" in the last two months or so should find the
posts.

>
> > > The API should be considered here. A getEBTN wrapper function like the
> > > above could be called "getByCssSelector" and the calls to getEBTN
> > > would become a subset of the possible calls to "getByCssSelector".
>
> > If you feel we must have a getByCssSelector,
>
> I don't think it is a must.

It certainly wouldn't bother me if we add it, but I know it is an
involved function and might not warrant the time it will take to
perfect it. I am more concerned with what gets left out than what
gets put in.

>
> > then that makes sense. I
> > can't imagine a scenario where I would use such a thing.
>
> I can only imagine myself using one that takes simple selectors like
> "div", "#fooId", ".fooClass" and combinations of those three. A more
> complex selector is only needed if the content creator is totally
> uncooperative and won't add the hooks needed by the JavaScript
> programmer.

Right. So it could be argued that those three are covered by gEBI,
gEBTN and gEBCN. Any further wrapping is just syntactic sugar.

>
> The programming of a getByCssSelector sure is fun. Probably the most

Fun?!

> enjoyable thing I've programmed in JavaScript that might be useful as
> it involves a CSS selector string parser and optimization is a major
> priority.

Certainly could be useful for some applications, but I don't see DOM
queries as one of them.

>
> [snip]
>
> > > This is something I'd like to add to the repository eventually. I did
> > > a couple weeks of work on these types of functions a while ago and it
> > > is very handy for enlivening pages for unobtrusive JavaScript. (Cue
> > > "PointedEars" for a "lemmings" comment.)
>
> > I haven't found a need for such queries and I agree with "PointedEars"
>
> That is the fourth and a half time that has happened recently.

And I half agreed with something VK posted recently. God save us.

>
> > in that event delegation is often the best way to go anyway.
>
> I don't see them as mutually exclusive as there are only so many

Certainly they aren't and I don't always use delegation.

> elements on which to delegate and listeners concerned with different
> handlers should not be combined. I really don't get what he is talking

Right.

> about, he has told me I don't understand, and he is write.

Oops. Now you are agreeing with him.

Thomas 'PointedEars' Lahn

unread,
Dec 9, 2007, 11:52:28 AM12/9/07
to
Peter Michaux wrote:
> On Dec 9, 1:39 am, Thomas 'PointedEars' Lahn <PointedE...@web.de> wrote:
>> Peter Michaux wrote:
>>> Based on the repository design guidelines, the above could be
>>> substantially reduced to just [...] Notes: There is no need to check
>>> for the existence of "document" as no browser, NN4+ and IE4+, missing
>>> "document". There is no need to check that document.getElementById is
>>> a callable since there has never been a known implementation where
>>> document.getElementById that is not callable.
>> If these are the guidelines for your project, to produce code that is
>> not interoperable and inherently unreliable,
>
> Do you feature test everything?

I do feature-testing on occasions where I know there can be a problem if
feature-testing is not performed prior to execution. This particular one
qualifies for such an occasion:

`document' is a host-defined property of the Global Object. It depends on
the host environment whether or not it is available. Not testing for this
feature would cause the code to break (instead of simply not work) in host
environments that don't provide it or simply don't provide it under this
name. However, I would be willing to concede that code that does not test
for this property is seldom error-prone as that property was introduced
with the first versions of the languages, and implementors tend to be
backwards-compatible.

`document.getElementById' is a host-defined property of a host object. That
host object did not have that property in all previous versions of the
corresponding host environments, so it is possible that this property does
not exist in the host environment this code is exposed to. That one is
covered by the type-converting true-value test. However, the property may
have any value and therefore it is possible that it cannot be called. Not
to make the best effort possible to ensure it can be called will result in a
runtime error in environments where the property is not supported as such
or external code has overwritten/defined the value with a non-callable one.

> Do you feature test that alert is callable?

Actually, I was about to implement that test because it has been reported
here that there is at least one user agent where calling it fails.

> Do you feature test that string concatenation works?

No. String concatenation is a *language* feature, but one that was
introduced with the first version of the language and not with later
implementations.

> Do you feature test that float division works (e.g. 1/2 is 0.5)?

Same here.

> Do you feature test for the bugs in the implementation of feature "x" in
> browser "y" that you have never seen?

Fallacy of many questions.

> You could almost do this by an insanely laborious process of checking
> things like float division in 100 different combinations before doing
> float division.

Appeal to ridicule.

> Hopefully you answer "no" to at least one of those question.
> If so then you see that

Straw man.

> the line has to be drawn somewhere.

Yes, but I don't agree with where you want to draw it.

Peter Michaux

unread,
Dec 9, 2007, 11:59:49 AM12/9/07
to
On Dec 9, 8:52 am, Thomas 'PointedEars' Lahn <PointedE...@web.de>

Above you stated you feature test "where you know there can be a
problem". Have you ever witnessed a problem with
document.getElementById not being callable?


> Not
> to make the best effort possible to ensure it can be called will result in a
> runtime error in environments where the property is not supported as such
> or external code has overwritten/defined the value with a non-callable one.
>
> > Do you feature test that alert is callable?
>
> Actually, I was about to implement that test because it has been reported
> here that there is at least one user agent where calling it fails.

Which browser has "alert" and it is not callable?

[snip]

> > Hopefully you answer "no" to at least one of those question.
> > If so then you see that
>
> Straw man.
>
> > the line has to be drawn somewhere.
>
> Yes, but I don't agree with where you want to draw it.

Fair enough and inevitable. I doubt we will ever agree on this.

David Mark

unread,
Dec 9, 2007, 12:13:15 PM12/9/07
to

That's interesting. How will that work?

> help greatly. A documentation format has not be worked out. These are
> both just as important to the project as the actual code.

It would be good a good idea to make the inline documentation as parse-
friendly as possible. Any build process for this library will require
a relational database for the meta data. It will be very time
consuming and error-prone to populate such a database manually.

>
> > but perhaps we should start discussing some of these in
> > parallel.
>
> No more than two at a time and maybe with a bit of a breather in
> between each topic. Folks that want to keep up but have to do actual

The newsgroup never sleeps. I don't know if it hurts anything to
discuss code ahead of its introduction to the repository (even if it
turns out to be way ahead.)

> work will be overwhelmed. I felt this project was a bit too demanding
> on my time this past week. There is no timeline and slow with more
> time to digest and think about things is better than fast and rushed.

Right.

>
> > Everybody has their own areas of interest,
>
> indeed
>
> > so this may
> > increase overall participation in the project.
>
> My preference is to focus on developing bigger modules and then the
> smaller things you've listed above will be added in as necessary. This

I somewhat disagree with that. Skipping cookies and plug-in detection
is certainly not an issue (as those aren't requirements for most
modules.) However, I think the items related to feature detection
should be in place before the modules that will rely on them are
considered.

> helps us answer frequently asked questions like "Which Ajax library

A basic Ajax module is virtually stand-alone. I don't think mine
relies on more than a couple of common functions.

> should I use?" Ajax and event modules seem to be the first logical

Events are pretty much stand-alone as well. Once we get that module
in place, which should include a DOMContentLoaded simulation, it opens
up additional possibilities for feature testing. As we will see, some
functionality cannot be created reliably before the document is ready
(e.g. anything that has to add a dummy element to the body to test for
defective CSS implementations.)

> modules to me. Already the functions prerequisite to Ajax have been
> discussed so I would like to do that after gEBI and gEBTN. After the
> gEBTN, gEBI and Ajax parts are finished the code repository will have
> enough bulk to consider it in general and develop testing and
> documentation systems before continuing with more features.

Post your take on XHR when you get a chance. I briefly looked at the
code in the repository and it appears you test for a couple of things
that I didn't consider in my implementation.

If we are going to talk about two things at once, I propose that the
rest of the feature detection code should be discussed at the same
time. It isn't particularly extensive and most of it was already
posted with my gEBI/gEBTN examples (e.g. isStyleCapable,
canAdjustStyle, xmlParseMode, etc.)

David Mark

unread,
Dec 9, 2007, 12:16:00 PM12/9/07
to

You lost me there. I was talking about the split gEBTN
implementation. I don't understand what that snippet is doing.

Thomas 'PointedEars' Lahn

unread,
Dec 9, 2007, 12:16:26 PM12/9/07
to
Peter Michaux wrote:
> On Dec 9, 8:52 am, Thomas 'PointedEars' Lahn <PointedE...@web.de> wrote:
>> Peter Michaux wrote:
>>> On Dec 9, 1:39 am, Thomas 'PointedEars' Lahn <PointedE...@web.de>
>>> wrote:
>>>> Peter Michaux wrote:
>>>>> Based on the repository design guidelines, the above could be
>>>>> substantially reduced to just [...] Notes: There is no need to
>>>>> check for the existence of "document" as no browser, NN4+ and
>>>>> IE4+, missing "document". There is no need to check that
>>>>> document.getElementById is a callable since there has never been
>>>>> a known implementation where document.getElementById that is not
>>>>> callable.
>>>> If these are the guidelines for your project, to produce code that
>>>> is not interoperable and inherently unreliable,
>>> Do you feature test everything?
>> I do feature-testing on occasions where I know there can be a problem
>> if feature-testing is not performed prior to execution. This
>> particular one qualifies for such an occasion:
>>
>> [...]

>> `document.getElementById' is a host-defined property of a host object.
>> That host object did not have that property in all previous versions of
>> the corresponding host environments, so it is possible that this
>> property does not exist in the host environment this code is exposed
>> to. That one is covered by the type-converting true-value test.
>> However, the property may have any value and therefore it is possible
>> that it cannot be called.
>
> Above you stated you feature test "where you know there can be a
> problem". Have you ever witnessed a problem with document.getElementById
> not being callable?

Understand this as "where I know there is the possibility of a problem".
I don't have to have observed that event to do my best to avoid it if I
already know it is technically possible, meaning inherent to the system
I am working with. That is what I mean with your approach producing (and
promoting) non-interoperable and therefore error-prone code.

>>> Do you feature test that alert is callable?
>> Actually, I was about to implement that test because it has been
>> reported here that there is at least one user agent where calling it
>> fails.
>
> Which browser has "alert" and it is not callable?

IIRC, Randy Webb is using or has used such one. Besides, Web browsers are
only a subset of scriptable HTML user agents which are only a subset of
scriptable user agents. Meaning also that testing something positive in a
few browsers means nothing. Secundum quid.

David Mark

unread,
Dec 9, 2007, 12:21:11 PM12/9/07
to
On Dec 9, 11:31 am, "Evertjan." <exjxw.hannivo...@interxnl.net> wrote:
> Peter Michaux wrote on 09 dec 2007 in comp.lang.javascript:
>
>
>
>
>
> > On Dec 9, 1:14 am, "Evertjan." <exjxw.hannivo...@interxnl.net> wrote:
> >> Peter Michaux wrote on 09 dec 2007 in comp.lang.javascript:
>
> >> > // the simplest possible implementation
>
> >> > if (document.getElementById) {
> >> > var getEBI = function(id, d) {
> >> > return (d||document).getElementById(id);
> >> > };
> >> >}
>
> >> What is the use of testing for getElementById,
> >> as the js code will error anyway
> >> when getEBI() is subsequently called while not defined?
>
> > The idea is to avoid errors.
>
> > A function that calls getEBI should only test for the existance of
> > gEBI and not document.getElementById. This is because the
> > implementation of getEBI may be changed and may not use
> > document.getElementById.
>
> Seems a bit stupid to me, Peter.

Not at all. Feature testing code in higher-level modules (and
applications) is far more concise and easier to maintain when it only
has to consider the the lower-level API interfaces. This will become
more apparent when more complex abstractions are added.

>
> If you plan to avoid any possible implementation change,
> better not programme at all.

I don't know what that means.

>
> why not perhaps:
>
> if (document.getElementById) {
> var getEBI = function(id, d) {
> return (d||document).getElementById(id);
> };} else {
>
> var getEBI = function(id, d) {
> return d.getElementById(id);
> };
>
> };
>

I definitely don't follow that.

David Mark

unread,
Dec 9, 2007, 12:36:59 PM12/9/07
to
On Dec 9, 11:59 am, Peter Michaux <petermich...@gmail.com> wrote:

[snip]

>
> > > Do you feature test that alert is callable?
>
> > Actually, I was about to implement that test because it has been reported
> > here that there is at least one user agent where calling it fails.
>
> Which browser has "alert" and it is not callable?

No idea which agent Thomas is referring to, but I don't see anything
wrong with:

if (isFeaturedMethod(this, 'alert')) {
...
}

Thomas would seem to prefer an equivalent to:

if (isFeaturedMethod(this, 'window') && isFeaturedMethod(this.window,
'alert')) {
...
}

I find that excessive.

What I don't like is:

if (this.alert) {
...
}

Same for:

if (document.getElementById) {
...
}

Those two are clearly ambiguous tests and I don't see how they save
more than a ms or two (and a few characters.) They also don't account
for Microsoft's ridiculous ActiveX host objects, whose methods blow up
when [[Get]] is invoked. Encapsulating these tests in a common
function seems like the most robust and concise solution.

And BTW, for some odd reason, Google keeps changing the thread subject
every time I reply. I personally don't care what it is called.

Peter Michaux

unread,
Dec 9, 2007, 12:45:36 PM12/9/07
to
On Dec 9, 9:13 am, David Mark <dmark.cins...@gmail.com> wrote:
> On Dec 9, 11:20 am, Peter Michaux <petermich...@gmail.com> wrote:
>
> > On Dec 9, 4:32 am, David Mark <dmark.cins...@gmail.com> wrote:
>
> > > On Dec 9, 12:02 am, Peter Michaux <petermich...@gmail.com> wrote:

[snip]

> > > I realize that the task of finalizing, adding and documenting each is


> > > a bottleneck,
>
> > Yes. I am working on an automated testing system now and that will
>
> That's interesting. How will that work?

The interesting part is that the testing framework needs to limit how
much client side scripting is used as it is testing a client side
scripting library. This is so even very old browsers can be tested.

I'm using a series of communications between the client and server.
The server orchestrates everything with a series redirects from test
page to test page using redirects until all test pages have been run.

The client logs test assertions (pass and fail) to the server by
setting (new Image()).src since this is the oldest(?) way to
communicate with the server. There will be extremely old browsers that
can't do this and those browsers will require manual testing if they
are to be tested at all.

The only other requirement is that the browser can set
window.location.

I think this is the ultimate in a low tech automated system for
testing.


> > help greatly. A documentation format has not be worked out. These are
> > both just as important to the project as the actual code.
>
> It would be good a good idea to make the inline documentation as parse-
> friendly as possible.

Yes.

Testing has higher priority than documentation.

[snip]

> > My preference is to focus on developing bigger modules and then the
> > smaller things you've listed above will be added in as necessary. This
>
> I somewhat disagree with that. Skipping cookies and plug-in detection
> is certainly not an issue (as those aren't requirements for most
> modules.) However, I think the items related to feature detection
> should be in place before the modules that will rely on them are
> considered.

What I'm trying to do is focus which lower level functionality goes in
first. Lower level functionality that is required by the higher level
modules should have priority. The lower level functionality
prerequisite to a particular module will necessarily be included
before or at the time the higher level module is included.


> > helps us answer frequently asked questions like "Which Ajax library
>
> A basic Ajax module is virtually stand-alone. I don't think mine
> relies on more than a couple of common functions.
>
> > should I use?" Ajax and event modules seem to be the first logical
>
> Events are pretty much stand-alone as well. Once we get that module
> in place, which should include a DOMContentLoaded simulation,

I no longer think that is even possible and window.onload is the only
option.

<URL: http://peter.michaux.ca/article/553>


> it opens
> up additional possibilities for feature testing. As we will see, some
> functionality cannot be created reliably before the document is ready
> (e.g. anything that has to add a dummy element to the body to test for
> defective CSS implementations.)
>
> > modules to me. Already the functions prerequisite to Ajax have been
> > discussed so I would like to do that after gEBI and gEBTN. After the
> > gEBTN, gEBI and Ajax parts are finished the code repository will have
> > enough bulk to consider it in general and develop testing and
> > documentation systems before continuing with more features.
>
> Post your take on XHR when you get a chance.

In the next lull in the threads for the project I will post.


> I briefly looked at the
> code in the repository and it appears you test for a couple of things
> that I didn't consider in my implementation.
>
> If we are going to talk about two things at once, I propose that the
> rest of the feature detection code should be discussed at the same
> time. It isn't particularly extensive and most of it was already
> posted with my gEBI/gEBTN examples (e.g. isStyleCapable,
> canAdjustStyle, xmlParseMode, etc.)

I can't keep up on more than is going on right now. I didn't think
think this project would be so popular!

Peter Michaux

unread,
Dec 9, 2007, 12:50:41 PM12/9/07
to

Copy and paste got me. I think I meant the following.

if (document.getElementById &&


typeof getAnElement != 'undefined' &&

getAnElement().getElementById) {

var getEBI = function(id, d) {
return (d||document).getElementById(id);
};

}

Since the getElementById method is specified separately for Document
and Element in the DOM2 spec, a feature test for one does not assure
the other is present. A test for both is necessary if the getEBI
function is to be documented as suitable for both.

David Mark

unread,
Dec 9, 2007, 12:57:22 PM12/9/07
to

I didn't split gEBI as I agree with the idea that it is useless for
anything but document nodes. gEBTN is another story, so the above
strategy would make sense for that one.

Thomas 'PointedEars' Lahn

unread,
Dec 9, 2007, 1:32:38 PM12/9/07
to
Peter Michaux wrote:
> [...] David Mark [...] wrote:
>> [...] Peter Michaux [...] wrote:
>>> [...] David Mark [...] wrote:
>>>> [...] Peter Michaux [...] wrote:
>>>>> [...] David Mark [...] wrote:
>>>>>> [...] Thomas 'PointedEars' Lahn [...] wrote:
>>>>>>> Peter Michaux wrote:
>>>>>>>> [...]

>>>>>>>> There is no need to check for the existence of "document" as no
>>>>>>>> browser, NN4+ and IE4+, missing "document".
>>>>>>>> There is no need to check that document.getElementById is a callable
>>>>>>>> since there has never been a known implementation where
>>>>>>>> document.getElementById that is not callable.
>>>>>>> If these are the guidelines for your project, to produce code that is not
>>>>>>> interoperable and inherently unreliable, I don't want to contribute.
>>>>>> I somewhat agree with that sentiment,
>>>>> I would say your codes says otherwise and that you agree that a line
>>>>> for feature testing should be drawn somewhere.
>>>> I was talking about the issue he was referring to (gEBTN for document
>>>> vs. element.)

JFTR: I was not referring to that at all.

>>> [...] I think I meant the following.


>
> if (document.getElementById &&
> typeof getAnElement != 'undefined' &&
> getAnElement().getElementById) {
>
> var getEBI = function(id, d) {
> return (d||document).getElementById(id);
> };
>
> }

The nonsense gets worse.

> Since the getElementById method is specified separately for Document
> and Element in the DOM2 spec,

No, it is not. I don't know which DOM 2 Spec you have been reading,
but the one I have been reading specifies only:

,-<http://www.w3.org/TR/DOM-Level-2-Core/core.html#i-Document>
|
| [...]
| interface Document : Node {
| [...]
| // Introduced in DOM Level 2:
| Element getElementById(in DOMString elementId);
| };

,-<http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-745549614>
|
| [...]
| interface Element : Node {
| readonly attribute DOMString tagName;
| DOMString getAttribute(in DOMString name);
| void setAttribute(in DOMString name,
| in DOMString value)
| raises(DOMException);
| void removeAttribute(in DOMString name)
| raises(DOMException);
| Attr getAttributeNode(in DOMString name);
| Attr setAttributeNode(in Attr newAttr)
| raises(DOMException);
| Attr removeAttributeNode(in Attr oldAttr)
| raises(DOMException);
| NodeList getElementsByTagName(in DOMString name);
| // Introduced in DOM Level 2:
| DOMString getAttributeNS(in DOMString namespaceURI,
| in DOMString localName);
| // Introduced in DOM Level 2:
| void setAttributeNS(in DOMString namespaceURI,
| in DOMString qualifiedName,
| in DOMString value)
| raises(DOMException);
| // Introduced in DOM Level 2:
| void removeAttributeNS(in DOMString namespaceURI,
| in DOMString localName)
| raises(DOMException);
| // Introduced in DOM Level 2:
| Attr getAttributeNodeNS(in DOMString namespaceURI,
| in DOMString localName);
| // Introduced in DOM Level 2:
| Attr setAttributeNodeNS(in Attr newAttr)
| raises(DOMException);
| // Introduced in DOM Level 2:
| NodeList getElementsByTagNameNS(in DOMString namespaceURI,
| in DOMString localName);
| // Introduced in DOM Level 2:
| boolean hasAttribute(in DOMString name);
| // Introduced in DOM Level 2:
| boolean hasAttributeNS(in DOMString namespaceURI,
| in DOMString localName);
| };

The item on the left is the *return type* of the method, not its
(additional) owner.

> a feature test for one does not assure the other is present.
> A test for both is necessary if the getEBI
> function is to be documented as suitable for both.

If what you stated above were the case, a test on an arbitrary element
object would not be sufficient. But it is not the case, so that point
is rather moot.


PointedEars
--
Prototype.js was written by people who don't know javascript for people
who don't know javascript. People who don't know javascript are not
the best source of advice on designing systems that use javascript.
-- Richard Cornford, cljs, <f806at$ail$1$8300...@news.demon.co.uk>

Peter Michaux

unread,
Dec 9, 2007, 1:54:56 PM12/9/07
to
On Dec 9, 10:32 am, Thomas 'PointedEars' Lahn <PointedE...@web.de>

I don't see what is wrong with the code above.


> > Since the getElementById method is specified separately for Document
> > and Element in the DOM2 spec,
>
> No, it is not.

I think it is. The portion of the spec that you quote below shows it
is specified spearately in the Document and the Element interfaces.

I still think it is the case.

> a test on an arbitrary element
> object would not be sufficient. But it is not the case, so that point
> is rather moot.

I don't understand what you are saying.

If the spec has getElementById in two places then it should be feature
tested in both places. An implementation may have one, the other, none

David Mark

unread,
Dec 9, 2007, 2:27:30 PM12/9/07
to
On Dec 9, 1:54 pm, Peter Michaux <petermich...@gmail.com> wrote:
[snip]

>
> I don't see what is wrong with the code above.
>

As I had never before tried to call gEBI on anything but document
nodes, I was unsure if it was even supported on element nodes. A
quick test confirmed it is not. As is typical, "PointedEars" couldn't
be expected to just say that as such, but instead posted the DOM
phonebook. I propose that in the interest of time management, his
often indecipherable scrutiny should be ignored. He has said he won't
contribute and so far he hasn't. Worse still, he is impeding progress
by constantly confusing the issues at hand.

Thomas 'PointedEars' Lahn

unread,
Dec 9, 2007, 2:34:37 PM12/9/07
to
[Sorry for misquoting you before, it is corrected below.]

Peter Michaux wrote:
> [...] Thomas 'PointedEars' Lahn [...] wrote:
>> Peter Michaux wrote:
>>> [...] I think I meant the following.
>>> if (document.getElementById
>>> && typeof getAnElement != 'undefined'
>>> && getAnElement().getElementById)
>>> {
>>> var getEBI = function(id, d)
>>> {
>>> return (d||document).getElementById(id);
>>> };
>>> }
>> The nonsense gets worse.
>
> I don't see what is wrong with the code above.

I explained that below.

>>> Since the getElementById method is specified separately for Document
>>> and Element in the DOM2 spec,
>> No, it is not.
>
> I think it is. The portion of the spec that you quote below shows it is
> specified spearately in the Document and the Element interfaces.

It shows the exact opposite. Watch closely:

>> ,-<http://www.w3.org/TR/DOM-Level-2-Core/core.html#i-Document>
>> |
>> | [...]
>> | interface Document : Node {
>> | [...]
>> | // Introduced in DOM Level 2:
>> | Element getElementById(in DOMString elementId);
>> | };
>>

>> [...]


>> The item on the left is the *return type* of the method, not its
>> (additional) owner.

>> a test on an arbitrary element object would not be sufficient. But it


>> is not the case, so that point is rather moot.
>
> I don't understand what you are saying.
>
> If the spec has getElementById in two places then it should be feature
> tested in both places. An implementation may have one, the other, none or
> both.

I don't know what anElement() returns, but ISTM you are drawing the false
conclusion that because one object implements the interface the passed one
also has to.


PointedEars

Peter Michaux

unread,
Dec 9, 2007, 3:04:17 PM12/9/07
to
On Dec 9, 11:34 am, Thomas 'PointedEars' Lahn <PointedE...@web.de>
wrote:

Some element. In any modern browser it will return
document.documentElement

> but ISTM you are drawing the false
> conclusion that because one object implements the interface the passed one
> also has to.

Sorry I've been reading this thread too fast. (I've actually been busy
having more fun making a testing framework for the repository.)

There is no Element getElementById in the spec. My function needs to
revert to

if (document.getElementById) {


var getEBI = function(id, d) {
return (d||document).getElementById(id);
};
}

// id is a string
// d is some optional node that implements the Document interface.

Now, what is wrong with that?

Peter Michaux

unread,
Dec 9, 2007, 3:07:03 PM12/9/07
to
On Dec 9, 11:27 am, David Mark <dmark.cins...@gmail.com> wrote:

[snip]

> As is typical, "PointedEars" couldn't
> be expected to just say that as such, but instead posted the DOM
> phonebook. I propose that in the interest of time management,

There is no time line.

> his
> often indecipherable scrutiny should be ignored.

I think the project must be inclusive to all participation.

> He has said he won't
> contribute and so far he hasn't.

I think he has. We have five and a half agreements with him so far.

> Worse still, he is impeding progress
> by constantly confusing the issues at hand.

I'm guilty of that too.

Thomas 'PointedEars' Lahn

unread,
Dec 9, 2007, 3:42:03 PM12/9/07
to
Peter Michaux wrote:
> [...] Thomas 'PointedEars' Lahn [...] wrote:
>> Peter Michaux wrote:
>>> [...] Thomas 'PointedEars' Lahn [...] wrote:
>>>> a test on an arbitrary element object would not be sufficient. But it
>>>> is not the case, so that point is rather moot.
>>> I don't understand what you are saying.
>>> If the spec has getElementById in two places then it should be feature
>>> tested in both places. An implementation may have one, the other, none or
>>> both.
>> I don't know what anElement() returns,
>
> Some element.

Some element *object*, if that.

> In any modern browser it will return document.documentElement

Which would confirm my suspicion below, though. BTW, what will it return in
any not so modern browser?

>> but ISTM you are drawing the false conclusion that because one object
>> implements the interface the passed one also has to.
>

> [...] My function needs to revert to


>
> if (document.getElementById) {
> var getEBI = function(id, d) {
> return (d||document).getElementById(id);
> };
> }
>
> // id is a string
> // d is some optional node that implements the Document interface.
>
> Now, what is wrong with that?

Besides the fact that your code is still lacking the necessary feature test,
you falsely assume that, because `document.getElementById' yields a
true-value, `d.getElementById' has to be callable.


Please, trim your quotes as explained in the FAQ and FAQ Notes. The entire
part about the DOM2 Spec was irrelevant to your followup, or at least could
have been shortened to very few lines.

Peter Michaux

unread,
Dec 9, 2007, 3:50:35 PM12/9/07
to
On Dec 9, 12:42 pm, Thomas 'PointedEars' Lahn <PointedE...@web.de>
wrote:
> Peter Michaux wrote:

[snip]

> > if (document.getElementById) {
> > var getEBI = function(id, d) {
> > return (d||document).getElementById(id);
> > };
> > }
>
> > // id is a string
> > // d is some optional node that implements the Document interface.
>
> > Now, what is wrong with that?
>
> Besides the fact that your code is still lacking the necessary feature test,
> you falsely assume that, because `document.getElementById' yields a
> true-value, `d.getElementById' has to be callable.

Have you ever seen a host where where document.getElementById is not
callable?

If you are concerned that someone might send a "d" that is not a
document then they will have violated what will be the documentation
saying "d" must be a document.

VK

unread,
Dec 9, 2007, 5:00:15 PM12/9/07
to
On Dec 9, 11:07 pm, Peter Michaux <petermich...@gmail.com> wrote:
> I think the project must be inclusive to all participation.

So far it mostly going "wild on the party" :-) as the major
participant s keep making monstrous paranoid all-in-one don't-trust-
anyone snippets of code where each snippet intended to accomplish a
single very primitive task. If the Code Worth project ever gets big
then it will be an army of soldiers where everyone carries on his
armor, food for the whole campaign, all necessary items, 200 pounds of
old family albums and beloved books. And before to ask for fire from
another solder, each one first makes a full scale fortification around
him so taking the lighter through the small hole in the wall after
triple check. Evidently the one who asked does exactly the same before
to get the lighter back: so in order of two to fire their cigars the
army has to make a stop for a day or two each time.

Such snippets may have some limited usability for bookmarklets and
thingies (inline anonymous functions for element event handlers).
God forbids to make a _library_ of all these things together. If one
at least pretending to be on OOP path then one shold start from the
basics: from the modularity. OOP is not about C out of B out of A out
of ground bottom - it is just a possible but not necessary consequence
of OOP approache. OOP is about everyone doing its own thing and this
thing only.
And what an anancastic sub is that so far? Are you trying to make the
resulting lib being able to run only on Cray clusters? First of all
the whole problem is totally artificial, because as of the end of 2007
the options are:
1) use document.getElementById
2) drop the sick sh** someone is using to run your script on.

Being a paranoiac it could be 3 options
1) use document.getElementById
2) use document.all if no 1)
3) drop the sick sh** someone is using to run your script on if
neither 1) nor 2)

So the entire - already paranoid for practical usage - code is:

if (document.getElementById) {
$ = function(id){
return document.getElementById(id);
};
}
else if (document.all) {
$ = function(id){return document.all(id);};
}
else {
$ = function(id){/* undefined */};
}

And then in Help file one writes something like:
com.something.utils.$(String id)
Takes single string argument and returns a reference to DOM element
with specified ID.
No behavior specified for arguments other than string type.
If DOM element with requested ID doesn't exists then returns null
value.
If the page contains several elements with the same ID then returns
the first element on the page going from the top with such ID.
If user agent doesn't support neither standard nor proprietary tools
for DOM element reference retrieval then returns undefined value.

Other words:
1) The main logic is moved to specification, not to runtime method.
Who wants a working program he will read it, who really wants to crash
the program he will do it no matter what.
2) Runtime performance is the King, fall-back protection is the sh** -
as long as no security violation is involved.

Peter Michaux

unread,
Dec 9, 2007, 8:00:16 PM12/9/07
to
On Dec 9, 2:00 pm, VK <schools_r...@yahoo.com> wrote:
> On Dec 9, 11:07 pm, Peter Michaux <petermich...@gmail.com> wrote:
>
> > I think the project must be inclusive to all participation.
>
> So far it mostly going "wild on the party" :-) as the major
> participant s keep making monstrous paranoid all-in-one don't-trust-
> anyone snippets of code where each snippet intended to accomplish a
> single very primitive task.

That is what foundational functions do.

I think you've missed the point. There are far bigger issues being
discussed about what things should be feature tested and what can be
assumed. Granularity of a code repository is also a major issue.


> If the Code Worth project ever gets big
> then it will be an army of soldiers where everyone carries on his
> armor, food for the whole campaign, all necessary items, 200 pounds of
> old family albums and beloved books. And before to ask for fire from
> another solder, each one first makes a full scale fortification around
> him so taking the lighter through the small hole in the wall after
> triple check. Evidently the one who asked does exactly the same before
> to get the lighter back: so in order of two to fire their cigars the
> army has to make a stop for a day or two each time.

I think it would be interesting to meet you, VK.


> Such snippets may have some limited usability for bookmarklets and
> thingies (inline anonymous functions for element event handlers).

A big piece of code would likely use little pieces. The alternative is
one huge function.


> God forbids to make a _library_ of all these things together.

The individual pieces need to be quality or the whole thing may as
well be tossed.


> If one
> at least pretending to be on OOP path then one shold start from the
> basics: from the modularity. OOP is not about C out of B out of A out
> of ground bottom - it is just a possible but not necessary consequence
> of OOP approache. OOP is about everyone doing its own thing and this
> thing only.

I don't think OOP comes into this.


> And what an anancastic sub is that so far? Are you trying to make the
> resulting lib being able to run only on Cray clusters? First of all
> the whole problem is totally artificial, because as of the end of 2007
> the options are:
> 1) use document.getElementById
> 2) drop the sick sh** someone is using to run your script on.

Very creative.


> Being a paranoiac it could be 3 options
> 1) use document.getElementById
> 2) use document.all if no 1)
> 3) drop the sick sh** someone is using to run your script on if
> neither 1) nor 2)
>
> So the entire - already paranoid for practical usage - code is:
>
> if (document.getElementById) {
> $ = function(id){
> return document.getElementById(id);
> };
> }
> else if (document.all) {
> $ = function(id){return document.all(id);};
> }
> else {
> $ = function(id){/* undefined */};
> }

This doesn't address the IE id/name attribute issue.


> And then in Help file one writes something like:
> com.something.utils.$(String id)

I think you would be the only one in favor of this API.


> Takes single string argument and returns a reference to DOM element
> with specified ID.
> No behavior specified for arguments other than string type.
> If DOM element with requested ID doesn't exists then returns null
> value.
> If the page contains several elements with the same ID then returns
> the first element on the page going from the top with such ID.
> If user agent doesn't support neither standard nor proprietary tools
> for DOM element reference retrieval then returns undefined value.

Even the documentation doesn't address the IE id/name problem.


> Other words:
> 1) The main logic is moved to specification, not to runtime method.

No one has suggested an implementation that makes the
document.getElementById and document.all tests at "runtime".


> Who wants a working program he will read it, who really wants to crash
> the program he will do it no matter what.
> 2) Runtime performance is the King, fall-back protection is the sh** -
> as long as no security violation is involved.

A tangential thinker, eh?

Peter Michaux

unread,
Dec 9, 2007, 10:50:39 PM12/9/07
to
On Dec 9, 9:45 am, Peter Michaux <petermich...@gmail.com> wrote:
> On Dec 9, 9:13 am, David Mark <dmark.cins...@gmail.com> wrote:
>
> > On Dec 9, 11:20 am, Peter Michaux <petermich...@gmail.com> wrote:
>
> > > On Dec 9, 4:32 am, David Mark <dmark.cins...@gmail.com> wrote:
>
> > > > On Dec 9, 12:02 am, Peter Michaux <petermich...@gmail.com> wrote:
>
> [snip]
>
> > > > I realize that the task of finalizing, adding and documenting each is
> > > > a bottleneck,
>
> > > Yes. I am working on an automated testing system now and that will
>
> > That's interesting. How will that work?
>
> The interesting part is that the testing framework needs to limit how
> much client side scripting is used as it is testing a client side
> scripting library. This is so even very old browsers can be tested.
>
> I'm using a series of communications between the client and server.
> The server orchestrates everything with a series redirects from test
> page to test page using redirects until all test pages have been run.
>
> The client logs test assertions (pass and fail) to the server by
> setting (new Image()).src since this is the oldest(?) way to
> communicate with the server. There will be extremely old browsers that
> can't do this and those browsers will require manual testing if they
> are to be tested at all.
>
> The only other requirement is that the browser can set
> window.location.

and setTimeout.

The testing framework is in the repository now. It seems to be working
with IE5+ and NN6+. I can't figure out why it doesn't work with IE4
since that browser can do all the things needed. I didn't try NN4.
Regardless even if it only works on IE5+ and NN6+ it will save a huge
amount of time and a very sore index finger.

There is a file trunk/test/README.txt that should contain enough
information to help someone get the automatic testing going. It uses
PHP and I use Apache.

It is good enough for the time being. Now back to the features...

Peter Michaux

unread,
Dec 10, 2007, 12:48:06 AM12/10/07
to
On Dec 9, 4:32 am, David Mark <dmark.cins...@gmail.com> wrote:
> On Dec 9, 12:02 am, Peter Michaux <petermich...@gmail.com> wrote:
>
> [snip]
>
> > I didn't make a time test but I think the simulation of
> > Array.prototype.filter will be very slow. I made a CSS selector
>
> Considering that the use of the filter wrapper only helps IE5 and
> under, it does make sense to duplicate its functionality in the gEBTN
> wrapper. But that function and other JS1.6 array method wrappers are
> useful for other purposes where speed is less of an issue. I think
> that we should deal with those shortly.

Prototype.js focuses heavily on these sort of iterators (e.g. filter,
each, find, etc). The other code in Prototype.js uses these iterators
and this makes the library difficult to use in pieces. The
foundational code is very large and must be included just for a small
piece of higher level functionality. I want to avoid this type of
interdependence. I think that code should be able to stand on it's own
or have very few dependencies.

[snip]

Peter Michaux

unread,
Dec 10, 2007, 1:13:24 AM12/10/07
to
> > > > Even though the two getEBTN wrappers use different fallbacks for IE4
> > > > it looks like they could be combined into one just like I've done here
> > > > with no real performance penalty. If some browsers had document.all
> > > > and not element.all then that would be a problem but has that ever
> > > > been the case?
> >
> > > That one isn't likely. It was the gEBTN inference that I wanted to
> > > avoid. The previous discussion about this stemmed from the fact that
> > > most libraries make similar inferences about addEventListener (which
> > > requires three implementations: window, document and element.) It was
> > > noted that agents do exist that support that method for elements, but
> > > not for window.

> >
> > Do you have a link to that previous discussion? If so please post it
>
> No.
>
> > when we get to events. I didn't know such a case existed.
>
> The source of that piece of information was Richard Cornford. The
> discussion concerning splitting up addEventListener and gEBTN wrappers
> was in a single thread. A search for "addEventListener" and
> "getElementsByTagName" in the last two months or so should find the
> posts.

I found it.

<URL: http://groups.google.com/group/comp.lang.javascript/browse_frm/thread/9857be01265be432>

I don't think that thread particularly suggests that a gEBTN wrapper
must be split into two functions although I suppose it could be.

David Mark

unread,
Dec 10, 2007, 4:57:44 AM12/10/07
to

Sounds good to me. I have always tested manually and it is time-
consuming.

[snip]

>
> What I'm trying to do is focus which lower level functionality goes in
> first. Lower level functionality that is required by the higher level
> modules should have priority. The lower level functionality
> prerequisite to a particular module will necessarily be included
> before or at the time the higher level module is included.

Right. I am basing my recommendations for the initial low-level
functionality based on what is used by my higher-level modules.

[snip]

>
> I no longer think that is even possible and window.onload is the only
> option.
>
> <URL:http://peter.michaux.ca/article/553>

It is impossible to exactly simulate DOMContentLoaded, but that has
never been a goal of mine. I simple stopped using onload by itself
(it is used as a fallback for situations when the DOMContentLoaded
event or simulation fails to work.) In a nutshell, a timeout set at
the bottom of the page calls the listener (and this is always optional
as onload used as a fallback.) As long as a page degrades gracefully
without script, then the split second that the content is visible
without enhancement is not an issue. In cases where the layout
changes dramatically on enhancement, I add style rules to hide certain
content during the page load.

[snip]

>
> I can't keep up on more than is going on right now. I didn't think

This automated testing suite should help that. But if only one person
is handling documentation and everybody else is adding and
scrutinizing code, then a bottleneck will always exist.

> think this project would be so popular!

It will certainly fill a glaring need once the basic foundation is in
place. After that, I'm sure the popularity will increase
exponentially.

David Mark

unread,
Dec 10, 2007, 5:11:48 AM12/10/07
to
On Dec 10, 12:48 am, Peter Michaux <petermich...@gmail.com> wrote:
> On Dec 9, 4:32 am, David Mark <dmark.cins...@gmail.com> wrote:
>
> > On Dec 9, 12:02 am, Peter Michaux <petermich...@gmail.com> wrote:
>
> > [snip]
>
> > > I didn't make a time test but I think the simulation of
> > > Array.prototype.filter will be very slow. I made a CSS selector
>
> > Considering that the use of the filter wrapper only helps IE5 and
> > under, it does make sense to duplicate its functionality in the gEBTN
> > wrapper. But that function and other JS1.6 array method wrappers are
> > useful for other purposes where speed is less of an issue. I think
> > that we should deal with those shortly.
>
> Prototype.js focuses heavily on these sort of iterators (e.g. filter,
> each, find, etc). The other code in Prototype.js uses these iterators
> and this makes the library difficult to use in pieces. The

I tend to avoid using them in low-level modules for performance
reasons, but the code itself is very compact, so I do include it in my
base module. I have considered moving it to its own module, which
would only be required by those higher-level modules that use it.

> foundational code is very large and must be included just for a small
> piece of higher level functionality. I want to avoid this type of
> interdependence. I think that code should be able to stand on it's own
> or have very few dependencies.
>

Depends on what it does. All but a couple of my modules require the
base module. Most have no other dependencies, but the highest level
modules and widgets typically depend on two or three lower level
modules. In general, as granularity is maximized, redundancy is
minimized and dependencies increase accordingly.

David Mark

unread,
Dec 10, 2007, 5:22:15 AM12/10/07
to
On Dec 10, 1:13 am, Peter Michaux <petermich...@gmail.com> wrote:
> > > > > Even though the two getEBTN wrappers use different fallbacks for IE4
> > > > > it looks like they could be combined into one just like I've done here
> > > > > with no real performance penalty. If some browsers had document.all
> > > > > and not element.all then that would be a problem but has that ever
> > > > > been the case?
>
> > > > That one isn't likely. It was the gEBTN inference that I wanted to
> > > > avoid. The previous discussion about this stemmed from the fact that
> > > > most libraries make similar inferences about addEventListener (which
> > > > requires three implementations: window, document and element.) It was
> > > > noted that agents do exist that support that method for elements, but
> > > > not for window.
>
> > > Do you have a link to that previous discussion? If so please post it
>
> > No.
>
> > > when we get to events. I didn't know such a case existed.
>
> > The source of that piece of information was Richard Cornford. The
> > discussion concerning splitting up addEventListener and gEBTN wrappers
> > was in a single thread. A search for "addEventListener" and
> > "getElementsByTagName" in the last two months or so should find the
> > posts.
>
> I found it.
>
> <URL:http://groups.google.com/group/comp.lang.javascript/browse_frm/thread...>

>
> I don't think that thread particularly suggests that a gEBTN wrapper
> must be split into two functions although I suppose it could be.
>

As I recall, my question was whether it would be excessively paranoid
to do so and the answer was no. As for addEventListener, it was
suggested that that would have to be done in at least two parts if one-
off testing was to be used. I ended up doing it in three parts
(element, document and window.) It didn't result in bloated code as I
used factories to generate the functions. In fact, the base module
that includes add/removeEventListener, gEBTN, gEBI, all of the feature
testing foundation previously discussed, plus iterator code and
several other low level abstractions is less then 10K minified. It
has become an arbitrary rule for me to keep all modules under 10K and
granularity requirements have tended to confirm that as a realistic
limit.

David Mark

unread,
Dec 10, 2007, 6:18:48 AM12/10/07
to
On Dec 9, 5:00 pm, VK <schools_r...@yahoo.com> wrote:
> On Dec 9, 11:07 pm, Peter Michaux <petermich...@gmail.com> wrote:
>
> > I think the project must be inclusive to all participation.
>
> So far it mostly going "wild on the party" :-) as the major

Colorful, but what does it mean?

> participant s keep making monstrous paranoid all-in-one don't-trust-
> anyone snippets of code where each snippet intended to accomplish a
> single very primitive task. If the Code Worth project ever gets big
> then it will be an army of soldiers where everyone carries on his
> armor, food for the whole campaign, all necessary items, 200 pounds of
> old family albums and beloved books. And before to ask for fire from
> another solder, each one first makes a full scale fortification around
> him so taking the lighter through the small hole in the wall after
> triple check. Evidently the one who asked does exactly the same before
> to get the lighter back: so in order of two to fire their cigars the
> army has to make a stop for a day or two each time.

Even more colorful and even less clear.

>
> Such snippets may have some limited usability for bookmarklets and
> thingies (inline anonymous functions for element event handlers).

What snippets?

> God forbids to make a _library_ of all these things together. If one

Really?

> at least pretending to be on OOP path then one shold start from the
> basics: from the modularity. OOP is not about C out of B out of A out
> of ground bottom - it is just a possible but not necessary consequence
> of OOP approache. OOP is about everyone doing its own thing and this
> thing only.

So far, nothing discussed has involved OOP, though it would be trivial
to add a thin OOP layer over the API functions. It would be a mistake
(often seen in currently popular JS libraries) to encapsulate
everything in objects (or a single object.)

> And what an anancastic sub is that so far? Are you trying to make the
> resulting lib being able to run only on Cray clusters? First of all

Unsurprisingly, I don't follow you on that either. Are you worried
about code size? If so, then you don't understand the basic concept
of the repository, which includes multiple versions of each function,
from the simplest (and least compatible) to the more complicated LCD
approach, which can ostensibly run on anything.

> the whole problem is totally artificial, because as of the end of 2007
> the options are:

Which "whole problem?"

> 1) use document.getElementById
> 2) drop the sick sh** someone is using to run your script on.

The gEBI wrapper is not the only issue at hand.

>
> Being a paranoiac it could be 3 options
> 1) use document.getElementById
> 2) use document.all if no 1)

Which adds a couple of extra lines to one version of one function.

> 3) drop the sick sh** someone is using to run your script on if
> neither 1) nor 2)

Right. As the only other approach would be to include support for
NS4. The way the API is designed, anyone who needs such support can
augment the existing LCD wrapper to suit.

>
> So the entire - already paranoid for practical usage - code is:
>
> if (document.getElementById) {
> $ = function(id){
> return document.getElementById(id);
> };
> }

That is your code. Nobody else is advocating the use of a $
identifier.

> else if (document.all) {
> $ = function(id){return document.all(id);};

Though the all object is typically callable, I prefer to reference its
properties.

> }
> else {

That is basically what I presented as the LCD approach. You left out
the ID check though.

> $ = function(id){/* undefined */};
> }

This branch is nonsense. You don't create a function that does
nothing, which would break the feature detection of higher level
modules and widgets.

>
> And then in Help file one writes something like:
> com.something.utils.$(String id)

No one doesn't. For one, we aren't defining a namespace at the moment
and if we were, I doubt it would resemble that. Personally, I use
relatively flat "namespace" and that is what I recommend for any
future build processes.

> Takes single string argument and returns a reference to DOM element
> with specified ID.

That is correct.

> No behavior specified for arguments other than string type.

No such behavior has been proposed.

> If DOM element with requested ID doesn't exists then returns null
> value.

Right.

> If the page contains several elements with the same ID then returns
> the first element on the page going from the top with such ID.
> If user agent doesn't support neither standard nor proprietary tools
> for DOM element reference retrieval then returns undefined value.

As noted, that would defeat the whole purpose of feature testing.

>
> Other words:
> 1) The main logic is moved to specification, not to runtime method.

All proposed feature testing so far has been of the one-off variety,
so I don't understand your point.

> Who wants a working program he will read it, who really wants to crash
> the program he will do it no matter what.

That is another issue. You don't see any argument validation and
exception raising in anything proposed so far and I would be against
such excesses.

> 2) Runtime performance is the King, fall-back protection is the sh** -
> as long as no security violation is involved.

Your "technical writing" is far too colorful. I don't understand half
of what you write. Judging by the other half, I don't think I am
missing out on much.

Peter Michaux

unread,
Dec 10, 2007, 9:36:05 AM12/10/07
to
On Dec 10, 2:11 am, David Mark <dmark.cins...@gmail.com> wrote:
> On Dec 10, 12:48 am, Peter Michaux <petermich...@gmail.com> wrote:
>
> > On Dec 9, 4:32 am, David Mark <dmark.cins...@gmail.com> wrote:
>
> > > On Dec 9, 12:02 am, Peter Michaux <petermich...@gmail.com> wrote:
>
> > > [snip]
>
> > > > I didn't make a time test but I think the simulation of
> > > > Array.prototype.filter will be very slow. I made a CSS selector
>
> > > Considering that the use of the filter wrapper only helps IE5 and
> > > under, it does make sense to duplicate its functionality in the gEBTN
> > > wrapper. But that function and other JS1.6 array method wrappers are
> > > useful for other purposes where speed is less of an issue. I think
> > > that we should deal with those shortly.
>
> > Prototype.js focuses heavily on these sort of iterators (e.g. filter,
> > each, find, etc). The other code in Prototype.js uses these iterators
> > and this makes the library difficult to use in pieces. The
>
> I tend to avoid using them in low-level modules for performance
> reasons, but the code itself is very compact, so I do include it in my
> base module.

That is a fair choice but very far removed from my choice. The large
base modules are one of my primary gripes with the popular JavaScript
libraries. I've watched a few project's base modules grow and it is a
slippery slope that will *definitely* slip when more than one person
wants to add functions to the base module.


> I have considered moving it to its own module, which
> would only be required by those higher-level modules that use it.
>
> > foundational code is very large and must be included just for a small
> > piece of higher level functionality. I want to avoid this type of
> > interdependence. I think that code should be able to stand on it's own
> > or have very few dependencies.
>
> Depends on what it does. All but a couple of my modules require the
> base module. Most have no other dependencies, but the highest level
> modules and widgets typically depend on two or three lower level
> modules. In general, as granularity is maximized, redundancy is
> minimized and dependencies increase accordingly.

As granularity is increased the amount of code served to the client
that is not necessary for a given page is also increased. This is the
bloat problem that all of the popular libraries seem to have.

David Mark

unread,
Dec 10, 2007, 9:42:00 AM12/10/07
to
On Dec 10, 9:36 am, Peter Michaux <petermich...@gmail.com> wrote:
[snip]
>
> As granularity is increased the amount of code served to the client
> that is not necessary for a given page is also increased. This is the
> bloat problem that all of the popular libraries seem to have.

Do you mean when granularity is decreased? By increasing granularity,
I mean breaking the code base up into smaller morsels, thereby
reducing the downloading of unused code.

Peter Michaux

unread,
Dec 10, 2007, 9:52:52 AM12/10/07
to
On Dec 10, 1:57 am, David Mark <dmark.cins...@gmail.com> wrote:
> On Dec 9, 12:45 pm, Peter Michaux <petermich...@gmail.com> wrote:
>
> I have always tested manually and it is time-consuming.

Very and especially when testing on a wide set of browsers. I think
the code in this repository should be tested on a wider set than the
following which took me days manually.

<URL: http://forkjavascript.org/welcome/browser_support>

[snip]

> > I can't keep up on more than is going on right now. I didn't think
>
> This automated testing suite should help that. But if only one person
> is handling documentation and everybody else is adding and
> scrutinizing code, then a bottleneck will always exist.

I think some pacing is needed anyway. In just couple weeks from now we
can have a full Ajax module and that is a substantial component to any
code base.

Given what we've already discussed in the various threads, I would
like to shoot for the following for the first milestone.

* DHTML
setOpacity
* DOM queries
getEBI
getEBTN
getEBCN (getElementsByClassName)
getEBCS (getElementsByCSSSelector) (a very simple one)
* Ajax
form serialization
XHR
* automated testing
* parsable documentation

We will need to have threads on Ajax and documentation over the next
while when the current threads have died down.

That is sufficient for the first round and enough to reflect on how
the process is going and if things need to be changed.

After that I will certainly make my own build process and start using
the code where I can in my own work.


> > think this project would be so popular!
>
> It will certainly fill a glaring need once the basic foundation is in
> place.

I hope so!

I have already learned enough to justify my time invested.


> After that, I'm sure the popularity will increase
> exponentially.

I'm not so concerned with that and that requires marketing which I
learned I am not interested in doing.

I'm interested to see if "Code Worth Recommending" is deemed to be
code worth recommending by many comp.lang.javascript contributors.

Peter Michaux

unread,
Dec 10, 2007, 9:55:40 AM12/10/07
to

So you recognized it was at least moderately paranoid ;-)

> to do so and the answer was no. As for addEventListener, it was
> suggested that that would have to be done in at least two parts if one-
> off testing was to be used. I ended up doing it in three parts
> (element, document and window.) It didn't result in bloated code as I
> used factories to generate the functions. In fact, the base module
> that includes add/removeEventListener, gEBTN, gEBI, all of the feature
> testing foundation previously discussed, plus iterator code and
> several other low level abstractions is less then 10K minified. It
> has become an arbitrary rule for me to keep all modules under 10K and
> granularity requirements have tended to confirm that as a realistic
> limit.

I try to keep all source files under 1K. Programmers have a very
difficult time agreeing on granularity.

Peter Michaux

unread,
Dec 10, 2007, 9:57:39 AM12/10/07
to
On Dec 10, 6:42 am, David Mark <dmark.cins...@gmail.com> wrote:
> On Dec 10, 9:36 am, Peter Michaux <petermich...@gmail.com> wrote:
> [snip]
>
>
>
> > As granularity is increased the amount of code served to the client
> > that is not necessary for a given page is also increased. This is the
> > bloat problem that all of the popular libraries seem to have.
>
> Do you mean when granularity is decreased?

I mean bigger grains of sand :-)

> By increasing granularity,
> I mean breaking the code base up into smaller morsels, thereby
> reducing the downloading of unused code.

yes.

David Mark

unread,
Dec 10, 2007, 10:47:09 AM12/10/07
to
On Dec 10, 9:52 am, Peter Michaux <petermich...@gmail.com> wrote:
> On Dec 10, 1:57 am, David Mark <dmark.cins...@gmail.com> wrote:
>
> > On Dec 9, 12:45 pm, Peter Michaux <petermich...@gmail.com> wrote:
>
> > I have always tested manually and it is time-consuming.
>
> Very and especially when testing on a wide set of browsers. I think
> the code in this repository should be tested on a wider set than the
> following which took me days manually.
>
> <URL:http://forkjavascript.org/welcome/browser_support>
>
> [snip]
>
> > > I can't keep up on more than is going on right now. I didn't think
>
> > This automated testing suite should help that. But if only one person
> > is handling documentation and everybody else is adding and
> > scrutinizing code, then a bottleneck will always exist.
>
> I think some pacing is needed anyway. In just couple weeks from now we
> can have a full Ajax module and that is a substantial component to any
> code base.
>
> Given what we've already discussed in the various threads, I would
> like to shoot for the following for the first milestone.
>
> * DHTML
> setOpacity
> * DOM queries
> getEBI
> getEBTN
> getEBCN (getElementsByClassName)

Let me know when you want to start discussing this one. I'll have to
disengage mine from a more generalized XPath wrapper (or perhaps it
would make sense to discuss an XPath wrapper first?)

> getEBCS (getElementsByCSSSelector) (a very simple one)

I take it you already have this.

> * Ajax
> form serialization
> XHR
> * automated testing
> * parsable documentation

This is definitely a must. I've got some ideas for a database schema
for the build process.

>
> We will need to have threads on Ajax and documentation over the next
> while when the current threads have died down.
>
> That is sufficient for the first round and enough to reflect on how
> the process is going and if things need to be changed.
>
> After that I will certainly make my own build process and start using
> the code where I can in my own work.

Be sure to share that. Do you plan to use a database?

>
> > > think this project would be so popular!
>
> > It will certainly fill a glaring need once the basic foundation is in
> > place.
>
> I hope so!
>
> I have already learned enough to justify my time invested.

Certainly it has helped me improve a few of my modules.

>
> > After that, I'm sure the popularity will increase
> > exponentially.
>
> I'm not so concerned with that and that requires marketing which I
> learned I am not interested in doing.

Me either. However, I think the viral spread of a "better mousetrap"
for browser scripting will be inevitable. I recently read a
completely ridiculous article titled "Top Ten JavaScript Functions of
All Time" and noted that despite its virtually useless content, it was
being echoed all over the place on blogs, shared bookmark sites, etc.

>
> I'm interested to see if "Code Worth Recommending" is deemed to be
> code worth recommending by many comp.lang.javascript contributors.

Same here, at least for those contributors whose opinions have not
been deemed to be worthless.

David Mark

unread,
Dec 10, 2007, 10:49:56 AM12/10/07
to

Absolutely. That's why I asked Richard what he thought about it. He
didn't seem to consider it excessive, so I kept it.

>
> > to do so and the answer was no. As for addEventListener, it was
> > suggested that that would have to be done in at least two parts if one-
> > off testing was to be used. I ended up doing it in three parts
> > (element, document and window.) It didn't result in bloated code as I
> > used factories to generate the functions. In fact, the base module
> > that includes add/removeEventListener, gEBTN, gEBI, all of the feature
> > testing foundation previously discussed, plus iterator code and
> > several other low level abstractions is less then 10K minified. It
> > has become an arbitrary rule for me to keep all modules under 10K and
> > granularity requirements have tended to confirm that as a realistic
> > limit.
>
> I try to keep all source files under 1K. Programmers have a very
> difficult time agreeing on granularity.

If I did that, I'd have nearly 200 files and far too many dependencies
to document.

Peter Michaux

unread,
Dec 10, 2007, 10:57:22 AM12/10/07
to

It is very simple (~30 lines). Since this thread has been about DOM
query functions, I'll post it in this thread shortly.


> > * Ajax
> > form serialization
> > XHR
> > * automated testing
> > * parsable documentation
>
> This is definitely a must. I've got some ideas for a database schema
> for the build process.

I believe build process is out of the scope of the project but the
project code should be flexible enough to allow for many build
processes. All along, I've not been sure why you need a database. I'm
probably just going to concatenate files.


> > We will need to have threads on Ajax and documentation over the next
> > while when the current threads have died down.
>
> > That is sufficient for the first round and enough to reflect on how
> > the process is going and if things need to be changed.
>
> > After that I will certainly make my own build process and start using
> > the code where I can in my own work.
>
> Be sure to share that. Do you plan to use a database?

I am definitely not using a database. It just doesn't seem that
complex to me to make a list of files that require concatenation. I
may use a simple templating system.

My build process will add an API that I like with one level of
"namespacing". Since this doesn't affect the correctness of algorithms
I really don't see the chance that group members will agree or that
the discussion will be productive.

[snip]

David Mark

unread,
Dec 10, 2007, 11:07:10 AM12/10/07
to

I was thinking in terms of a generalized, automated build process that
would choose the proper versions of each function, based on
compatibility requirements. Personally, I also just concatenate
files.

[snip]

>
> My build process will add an API that I like with one level of
> "namespacing". Since this doesn't affect the correctness of algorithms
> I really don't see the chance that group members will agree or that
> the discussion will be productive.

Probably not, but I agree that a "flat" namespace is the best way to
go.

Peter Michaux

unread,
Dec 10, 2007, 11:33:09 AM12/10/07
to

If everyone using the library just concatenates then I sure would
spend the time on an elaborate system. I say let the person who wants
the elaborate system build it exactly to his liking.

Peter Michaux

unread,
Dec 10, 2007, 2:30:31 PM12/10/07
to
Below is a very simple getElementsByCssSelector function. I think that
selector functions that can do more are more bulk to download than
necessary. I am not opposed to a fancier one being in the repository
but it will require a huge amount of testing and performance
profiling. If anyone wants to build such a thing I have some code that
can make a fast function. This type of function is a perfect candidate
for the multiple implementations approach.

There is a w3c spec for a css selector like document.getElementById in
the works so this may be a good approach to adopt.

// s is a subset of the CSS simple selector syntax.
// Examples
// div
// #foo
// .bar
// div#foo
// div.bar
// #foo.bar
// .bar#foo
// div#foo.bar
// div.bar#foo
// If a tagName is used it must be first.
// No whitespace tolerated
//
// d is the optional document or element object
//
// always returns an array
//
// If you are simply using getEBCS('#foo') then
// using this function is overkill and
// it is faster to use getEBI('foo')

if (typeof getEBI != 'undefined' &&
typeof getEBTN != 'undefined' &&
// match appears first in ECMAScript in v3 so test
String.prototype.match) {

var getEBCS = function(s, d) {
var m, // regexp matches and temp var in loop
tn, // tagName in s
id, // id in s
cn, // className in s
els, // candidate elements for return
ns, // elements to return
i, // loop index
ilen, // loop limit
el; // temp element variable

m = s.match(/^([^#\.]+)/);
tn = m ? m[1] : '*';

m = s.match(/#([^\.]+)/);
id = m ? m[1] : null;

m = s.match(/\.([^#]+)/);
cn = m ? m[1] : null;

// If d.documentElement then know d is a document object.
// Need this because getEBI only takes document object
// as optional second argument but getEBCS takes any
// element or a document as second argument.
els = (id && (!d || d.documentElement)) ?
((el=getEBI(id, d)) ? [el] : []) :
getEBTN(tn, d);

ns = [];
for (i=0, ilen=els.length; i<ilen; ++i) {
el = els[i];
// Could call an external hasClassName function but in line
// it here for efficiency. There are no cross browser issue
// with checking className.
if ((!cn ||
((m = el.className) &&
(' '+m+' ').indexOf(' '+cn+' ')>-1)) &&
// If tn != '*' then el necessarily has tagname property.
(tn == '*' || el.tagName.toLowerCase() == tn) &&
(!id || el.id == id)) {
ns[ns.length] = el;
}
}
return ns;
};

}

No checks for toLowerCase and indexOf because they are so old.

If the indexOf trick for checking classname looks weird it is used
because it is much faster than a regexp. Since this is in a loop speed
counts.

getElementsByClassName and getElementsByCssSelector functions are both
sugar on top of what the browser offers. Given the limited scope of
what a getElementsByClassName function can do I think it would be best
to just implement it as one of the getElementsByCssSelector functions.
The expense is just removing a preceeding dot in the call. For
example, getEBCS('.bar') instead of getEBCN('bar'). This also saves
one more function in the repository API and I am all for that!

Here is the getEBCS that can get elements only by class name

if (typeof getEBTN != 'undefined') {

var getEBCS = function(s, d) {
var m, // regexp matches and temp var in loop
i, // loop index
ilen, // loop limit
el, // temp element variable
ns = [], // elements to return
cn = s.substring(1), // className in s
els = getEBTN('*', d); // candidate elements for return

for (i=0, ilen=els.length; i<ilen; ++i) {
el = els[i];
// Could call an external hasClassName function but in line
// it here for efficiency. There are no cross browser issue
// with checking className.
if ((m = el.className) &&
(' ' + m + ' ').indexOf(' ' + cn + ' ') >- 1) {
ns[ns.length] = el;
}
}
return ns;
};

Thomas 'PointedEars' Lahn

unread,
Dec 10, 2007, 5:32:27 PM12/10/07
to
Peter Michaux wrote:
> [...] Thomas 'PointedEars' Lahn [...] wrote:
>> Peter Michaux wrote:
>>> if (document.getElementById) {
>>> var getEBI = function(id, d) {
>>> return (d||document).getElementById(id);
>>> };
>>> }
>>> // id is a string
>>> // d is some optional node that implements the Document interface.
>>> Now, what is wrong with that?
>> Besides the fact that your code is still lacking the necessary feature test,
>> you falsely assume that, because `document.getElementById' yields a
>> true-value, `d.getElementById' has to be callable.
>
> Have you ever seen a host where where document.getElementById is not
> callable?

Several existing (and used) UAs do not support the W3C DOM, but I thought I
had made myself clear already. I can also easily create you a fitting UA,
and that does not involve (re)compiling its source code. With client-side
scripting, and especially host objects, all bets are off. The sooner you
realize this, the earlier your client-side scripts will become more
interoperable and less error-prone.

> If you are concerned that someone might send a "d" that is not a
> document then they will have violated what will be the documentation
> saying "d" must be a document.

`d' may be a a reference to a "document" object and still does not need to
provide that method. You assume a forced commonality where none exists.


PointedEars
--
Anyone who slaps a 'this page is best viewed with Browser X' label on
a Web page appears to be yearning for the bad old days, before the Web,
when you had very little chance of reading a document written on another
computer, another word processor, or another network. -- Tim Berners-Lee

David Mark

unread,
Dec 10, 2007, 5:36:50 PM12/10/07
to
On Dec 10, 2:30 pm, Peter Michaux <petermich...@gmail.com> wrote:
> Below is a very simple getElementsByCssSelector function. I think that
> selector functions that can do more are more bulk to download than
> necessary. I am not opposed to a fancier one being in the repository

No question. Functions that enable complex CSS selector queries take
up space for no good reason. When you look at the samples provided
with such functions, it is apparent that they will break if the markup
or related styles are changed in the slightest. In other words,
changing a className, changing the order or nesting level of elements,
etc. requires scrutinizing all of the selector queries in associated
scripts. To me, this is completely contrary to the notion of
separation of markup, presentation and behavior. In fact, it tangles
all three of them together.

> but it will require a huge amount of testing and performance

No question there either.

> profiling. If anyone wants to build such a thing I have some code that
> can make a fast function. This type of function is a perfect candidate
> for the multiple implementations approach.

I've got to mention that if we are going to allow something like that
in, then an extra gEBI (and getAnElement) function with a couple of
extra lines doesn't seem like it should qualify as an issue.

>
> There is a w3c spec for a css selector like document.getElementById in
> the works so this may be a good approach to adopt.

Might as well create a wrapper for it now. But I wonder how complex
the w3c's version will be. If we can't duplicate everything it does,
then the wrapper might confuse people. It might be better to stick
with gEBCN.

I don't test the JS 1.2 String (or RegExp) methods. I think we can
safely draw the line there.

>
> var getEBCS = function(s, d) {
> var m, // regexp matches and temp var in loop
> tn, // tagName in s
> id, // id in s
> cn, // className in s
> els, // candidate elements for return
> ns, // elements to return
> i, // loop index
> ilen, // loop limit
> el; // temp element variable
>
> m = s.match(/^([^#\.]+)/);
> tn = m ? m[1] : '*';
>
> m = s.match(/#([^\.]+)/);
> id = m ? m[1] : null;
>
> m = s.match(/\.([^#]+)/);
> cn = m ? m[1] : null;
>
> // If d.documentElement then know d is a document object.
> // Need this because getEBI only takes document object
> // as optional second argument but getEBCS takes any
> // element or a document as second argument.
> els = (id && (!d || d.documentElement)) ?
> ((el=getEBI(id, d)) ? [el] : []) :
> getEBTN(tn, d);

That seems like an iffy test for a document node. It would be more
robust to test if it is not an element node (see the gEBTN wrapper.)

>
> ns = [];
> for (i=0, ilen=els.length; i<ilen; ++i) {

Does it not make sense to use while (--i) and reverse the results
after the loop? At least for large numbers of nodes, it would seem
more efficient.

> el = els[i];
> // Could call an external hasClassName function but in line
> // it here for efficiency. There are no cross browser issue
> // with checking className.
> if ((!cn ||
> ((m = el.className) &&
> (' '+m+' ').indexOf(' '+cn+' ')>-1)) &&

Would this be a more efficient test?

// Outside of the loop
re = new RegExp('(^|\\s)' + cn + '(\\s|$)')

re.test(el.className)

> // If tn != '*' then el necessarily has tagname property.

Right. It should be noted that if one uses the unfiltered gEBTN
version, then this function may return non-element nodes.

> (tn == '*' || el.tagName.toLowerCase() == tn) &&
> (!id || el.id == id)) {
> ns[ns.length] = el;
> }
> }
> return ns;
> };
>
> }
>
> No checks for toLowerCase and indexOf because they are so old.
>
> If the indexOf trick for checking classname looks weird it is used
> because it is much faster than a regexp. Since this is in a loop speed
> counts.

Is the extra string concatenation really faster than testing with a
RegExp? If so, I need to re-think my use of the test method in some
cases.

Speaking of speed. I have an XPath version of this. It is tangled up
in a general-purpose query function that has a few more options (e.g.
checking for the presence of attributes.) But it wouldn't take too
much effort to untangle it and add it as a branch to this.

>
> getElementsByClassName and getElementsByCssSelector functions are both
> sugar on top of what the browser offers. Given the limited scope of
> what a getElementsByClassName function can do I think it would be best
> to just implement it as one of the getElementsByCssSelector functions.

I think it makes sense to include it as a one-liner that calls gEBCS.

> The expense is just removing a preceeding dot in the call. For
> example, getEBCS('.bar') instead of getEBCN('bar'). This also saves
> one more function in the repository API and I am all for that!

Either way. I just think it is more readable to use
getElementsByClassName. And isn't that an upcoming w3c function as
well?

>
> Here is the getEBCS that can get elements only by class name

I would leave this one out. But I guess if one is only interested in
querying by className, this would provide a slightly smaller
alternative

>
> if (typeof getEBTN != 'undefined') {
>
> var getEBCS = function(s, d) {
> var m, // regexp matches and temp var in loop

It is only the latter in this function.

Peter Michaux

unread,
Dec 10, 2007, 6:37:32 PM12/10/07
to
On Dec 10, 2:36 pm, David Mark <dmark.cins...@gmail.com> wrote:
> On Dec 10, 2:30 pm, Peter Michaux <petermich...@gmail.com> wrote:
>
> > Below is a very simple getElementsByCssSelector function. I think that
> > selector functions that can do more are more bulk to download than
> > necessary. I am not opposed to a fancier one being in the repository
>
> No question. Functions that enable complex CSS selector queries take
> up space for no good reason. When you look at the samples provided
> with such functions, it is apparent that they will break if the markup
> or related styles are changed in the slightest. In other words,
> changing a className, changing the order or nesting level of elements,
> etc. requires scrutinizing all of the selector queries in associated
> scripts. To me, this is completely contrary to the notion of
> separation of markup, presentation and behavior. In fact, it tangles
> all three of them together.
>
> > but it will require a huge amount of testing and performance
>
> No question there either.
>
> > profiling. If anyone wants to build such a thing I have some code that
> > can make a fast function. This type of function is a perfect candidate
> > for the multiple implementations approach.
>
> I've got to mention that if we are going to allow something like that
> in, then an extra gEBI (and getAnElement) function with a couple of
> extra lines doesn't seem like it should qualify as an issue.

The issue isn't a few extra lines in gEBI. The issue for me is that
there needs to be a set of guidelines that are followed when deciding
how to code for the repository. A rule like "support IE4 when it is
easy" is an ambiguous rule. I want the guidelines to be as black and
white as possible. I've never seen any library with a set of design
guidelines that can be pointed to when someone says "why didn't you
test for that?" I want a set of such guidelines so there is no need to
deal with those questions by humming and hawing about how I was
feeling about IE4 on a particular day.

I know the IE4 support issue will be a fun reoccurring issue. I don't
mind. I imagine that IE4 support is only an issue for a few members of
comp.lang.javascript and almost no one else in the world. It is easy
enough to maintain your own gEBI and getEBTN. I know there will be
some things I will have to maintain for my particular situation and
they won't go into the repository. That's ok.


> > There is a w3c spec for a css selector like document.getElementById in
> > the works so this may be a good approach to adopt.
>
> Might as well create a wrapper for it now. But I wonder how complex
> the w3c's version will be.

I imagine at least full CSS2 support given libraries like jQuery have
a big set of CSS2 support.


> If we can't duplicate everything it does,
> then the wrapper might confuse people. It might be better to stick
> with gEBCN.

We already have wrappers that don't completely match what they are
supposed to wrap. For example, the gEBI wrapper you suggested that
checks the name/id problem but doesn't walk the DOM is somewhat
similar in my mind.

The tables are turned and now I want to check something you don't :-)

Given ECMAScript v3 is the most recent spec I thought we should
probably check anything new in it.

Please post a replacement for the above line.


> > ns = [];
> > for (i=0, ilen=els.length; i<ilen; ++i) {
>
> Does it not make sense to use while (--i) and reverse the results
> after the loop? At least for large numbers of nodes, it would seem
> more efficient.

I haven't tested this system. Is it really faster? By how much?


> > el = els[i];
> > // Could call an external hasClassName function but in line
> > // it here for efficiency. There are no cross browser issue
> > // with checking className.
> > if ((!cn ||
> > ((m = el.className) &&
> > (' '+m+' ').indexOf(' '+cn+' ')>-1)) &&
>
> Would this be a more efficient test?
>
> // Outside of the loop
> re = new RegExp('(^|\\s)' + cn + '(\\s|$)')
>
> re.test(el.className)

Using a regular expression is much slower. I tested this. It is a big
difference.


> > // If tn != '*' then el necessarily has tagname property.
>
> Right. It should be noted that if one uses the unfiltered gEBTN
> version, then this function may return non-element nodes.

But this is not a worry here as gEBTN will always return elements with
the correct tagName if it is sent a tagName (i.e. not sent '*'). I
believe the next line is fine even with the unfiltered gEBTN and
el.tagName.toLowerCase will never throw a "no properties" error.


> > (tn == '*' || el.tagName.toLowerCase() == tn) &&
> > (!id || el.id == id)) {
> > ns[ns.length] = el;
> > }
> > }
> > return ns;
> > };
>
> > }
>
> > No checks for toLowerCase and indexOf because they are so old.
>
> > If the indexOf trick for checking classname looks weird it is used
> > because it is much faster than a regexp. Since this is in a loop speed
> > counts.
>
> Is the extra string concatenation really faster than testing with a
> RegExp? If so, I need to re-think my use of the test method in some
> cases.

Much faster. Give it a try.

Where I learned this is from the EXT css selector which is the fastest
of the popular ones. I looked in it and was amazed to see the string
concatenation.


> Speaking of speed. I have an XPath version of this. It is tangled up
> in a general-purpose query function that has a few more options (e.g.
> checking for the presence of attributes.) But it wouldn't take too
> much effort to untangle it and add it as a branch to this.

Please do post that! I have never looked into this but want to learn
about.


> > getElementsByClassName and getElementsByCssSelector functions are both
> > sugar on top of what the browser offers. Given the limited scope of
> > what a getElementsByClassName function can do I think it would be best
> > to just implement it as one of the getElementsByCssSelector functions.
>
> I think it makes sense to include it as a one-liner that calls gEBCS.

I really don't mind either way (thanks to automated testing! :D). So
you are suggesting the following in two files?

getEBCN = function(cn, d) = {
/* lots of code*/
};

getEBCS = function(s, d) = {
return getEBCN(s.substring(1), d);
};

> > The expense is just removing a preceeding dot in the call. For
> > example, getEBCS('.bar') instead of getEBCN('bar'). This also saves
> > one more function in the repository API and I am all for that!
>
> Either way. I just think it is more readable to use
> getElementsByClassName. And isn't that an upcoming w3c function as
> well?

I believe it is.


> > Here is the getEBCS that can get elements only by class name
>
> I would leave this one out.

It will just be the "one-liner" above.

> But I guess if one is only interested in
> querying by className, this would provide a slightly smaller
> alternative
>
>
>
> > if (typeof getEBTN != 'undefined') {
>
> > var getEBCS = function(s, d) {
> > var m, // regexp matches and temp var in loop
>
> It is only the latter in this function.

Indeed.

> > i, // loop index
> > ilen, // loop limit
> > el, // temp element variable
> > ns = [], // elements to return
> > cn = s.substring(1), // className in s
> > els = getEBTN('*', d); // candidate elements for return
>
> > for (i=0, ilen=els.length; i<ilen; ++i) {
> > el = els[i];
> > // Could call an external hasClassName function but in line
> > // it here for efficiency. There are no cross browser issue
> > // with checking className.
> > if ((m = el.className) &&
> > (' ' + m + ' ').indexOf(' ' + cn + ' ') >- 1) {
> > ns[ns.length] = el;
> > }
> > }
> > return ns;
> > };
>
> > }

--

Peter Michaux

unread,
Dec 10, 2007, 7:02:53 PM12/10/07
to
On Dec 10, 2:32 pm, Thomas 'PointedEars' Lahn <PointedE...@web.de>
wrote:

> Peter Michaux wrote:
> > [...] Thomas 'PointedEars' Lahn [...] wrote:
> >> Peter Michaux wrote:
> >>> if (document.getElementById) {
> >>> var getEBI = function(id, d) {
> >>> return (d||document).getElementById(id);
> >>> };
> >>> }
> >>> // id is a string
> >>> // d is some optional node that implements the Document interface.
> >>> Now, what is wrong with that?
> >> Besides the fact that your code is still lacking the necessary feature test,
> >> you falsely assume that, because `document.getElementById' yields a
> >> true-value, `d.getElementById' has to be callable.
>
> > Have you ever seen a host where where document.getElementById is not
> > callable?
>
> Several existing (and used) UAs do not support the W3C DOM,

I know that. What is your answer to my question? I'll make it clearer

Have you ever seen a host that has document.getElementById where
document.getElementById is not callable?

> but I thought I had made myself clear already.

Unfortunately no. Communication takes two people. I'll take half the
responsibility.


> I can also easily create you a fitting UA,

What is a "fitting" UA?

> and that does not involve (re)compiling its source code.

If I work hard enough I can create a brand new browser that will break
any code that any JavaScript programmer produces. I don't think an
argument like this is helpful however.

> With client-side
> scripting, and especially host objects, all bets are off.

I see no difference between language features and host features. They
were probably programmed by the same guys in the same cubicles. There
are standards for both the language and the DOM. The two are very
similar situations.

If "all bets are off" then you must test everything but I don't think
that is true. It was a serious question when I asked do you test that
float division works properly? If you do not, which is what I expect
is true, then you recognize that it is ok to draw the line somewhere
where certain features, that have never been known to have a problem,
can be simply assumed to work.

> The sooner you
> realize this,

I realize this. Do you see that, if I am right that you draw the line
somewhere for feature testing, that different programmers will draw
the line in different places? Can you see that this is a grey area and
it is ok if different people draw the line in different places than
you do? (I'm not asking if you think I draw the line in the right
place.)

> the earlier your client-side scripts will become more
> interoperable and less error-prone.

Yes. The more testing, the less error-prone. The more testing, the
more buggy code to maintain. The more testing, the more code to
download. There are pros and cons.

I think by now you will agree that for feature testing the line has to
be draw somewhere and different developers will draw the line in
different places. Do you agree?


> > If you are concerned that someone might send a "d" that is not a
> > document then they will have violated what will be the documentation
> > saying "d" must be a document.
>
> `d' may be a a reference to a "document" object and still does not need to
> provide that method. You assume a forced commonality where none exists.

One more time...

Have you ever seen a host that has document.getElementById where
document.getElementById is not callable?

I don't mean any of the above questions sarcastically.

So far I'm the only one that has posted a set of design guidelines.
I'd be interested to read your design guidelines.

Peter Michaux

unread,
Dec 10, 2007, 8:27:41 PM12/10/07
to
On Dec 10, 7:47 am, David Mark <dmark.cins...@gmail.com> wrote:
> On Dec 10, 9:52 am, Peter Michaux <petermich...@gmail.com> wrote:

[snip]

> > After that I will certainly make my own build process and start using
> > the code where I can in my own work.
>
> Be sure to share that.

I wrote up how to build using the Template::Toolkit library for Perl.

<URL: http://cljs.michaux.ca/trac/wiki/BuildWithTemplateToolkit>

David Mark

unread,
Dec 10, 2007, 8:29:52 PM12/10/07
to

Slightly similar. It doesn't completely work around the IE bug (and I
am not convinced it needs to), but it does add some value to the
implementation. The gEBCS wrapper as it stands is probably only
implementing half of what the w3c method will do. I don't think that
is necessarily a bad thing, but it may cause some confusion in that
the two branches will diverge considerably once there is a featured
w3c method. And BTW, it wouldn't hurt to add the test for it and
associated branch now (assuming we are going to wrap it.)

[snip]

>
> The tables are turned and now I want to check something you don't :-)

IE4/NN4 support 1.2 and I ignore the consequences of inadequate
feature testing for anything that came before that. That's where I
draw the line. The use of RegExp literals in this project will ensure
that NN <= 4 and IE < 4 won't parse the scripts anyway.

[snip]


>
> > That seems like an iffy test for a document node. It would be more
> > robust to test if it is not an element node (see the gEBTN wrapper.)
>
> Please post a replacement for the above line.

See below.

>
> > > ns = [];
> > > for (i=0, ilen=els.length; i<ilen; ++i) {
>
> > Does it not make sense to use while (--i) and reverse the results
> > after the loop? At least for large numbers of nodes, it would seem
> > more efficient.
>
> I haven't tested this system. Is it really faster? By how much?

Depends on the environment and what is happening inside the loop. In
most cases it is relatively faster.

[smip]

>
> > Would this be a more efficient test?
>
> > // Outside of the loop
> > re = new RegExp('(^|\\s)' + cn + '(\\s|$)')
>
> > re.test(el.className)
>
> Using a regular expression is much slower. I tested this. It is a big
> difference.

That surprises me. You could eliminate the extra string concatenation
though.

>
> > > // If tn != '*' then el necessarily has tagname property.
>
> > Right. It should be noted that if one uses the unfiltered gEBTN
> > version, then this function may return non-element nodes.
>
> But this is not a worry here as gEBTN will always return elements with
> the correct tagName if it is sent a tagName (i.e. not sent '*').

That's what I meant by "right." I was just noting that the
explanation should perhaps be expanded.

I
> believe the next line is fine even with the unfiltered gEBTN and
> el.tagName.toLowerCase will never throw a "no properties" error.
>

Right.

>
>
> > > (tn == '*' || el.tagName.toLowerCase() == tn) &&
> > > (!id || el.id == id)) {
> > > ns[ns.length] = el;
> > > }
> > > }
> > > return ns;
> > > };
>
> > > }
>
> > > No checks for toLowerCase and indexOf because they are so old.
>
> > > If the indexOf trick for checking classname looks weird it is used
> > > because it is much faster than a regexp. Since this is in a loop speed
> > > counts.
>
> > Is the extra string concatenation really faster than testing with a
> > RegExp? If so, I need to re-think my use of the test method in some
> > cases.
>
> Much faster. Give it a try.
>
> Where I learned this is from the EXT css selector which is the fastest
> of the popular ones. I looked in it and was amazed to see the string
> concatenation.

I amazed to hear that. I suspect I will have to update some of my
functions to reflect this.

>
> > Speaking of speed. I have an XPath version of this. It is tangled up
> > in a general-purpose query function that has a few more options (e.g.
> > checking for the presence of attributes.) But it wouldn't take too
> > much effort to untangle it and add it as a branch to this.
>
> Please do post that! I have never looked into this but want to learn
> about.

I spliced together a new version of function.

>
> > > getElementsByClassName and getElementsByCssSelector functions are both
> > > sugar on top of what the browser offers. Given the limited scope of
> > > what a getElementsByClassName function can do I think it would be best
> > > to just implement it as one of the getElementsByCssSelector functions.
>
> > I think it makes sense to include it as a one-liner that calls gEBCS.
>
> I really don't mind either way (thanks to automated testing! :D). So
> you are suggesting the following in two files?
>
> getEBCN = function(cn, d) = {
> /* lots of code*/
>
> };
>
> getEBCS = function(s, d) = {
> return getEBCN(s.substring(1), d);
>
> };

No, exactly the opposite. Else how could you swap in the version of
gEBCS that does more than just className?

[snip]

I haven't tested this thoroughly.

var queryXPath, resolve;

if (isFeaturedMethod(doc, 'evaluate')) {
resolve = function() { return 'http://www.w3.org/1999/xhtml'; };
queryXPath = function(ancestor, tagName, className) {
var l, r, bSnapshot, docNode = (ancestor.nodeType == 9)?ancestor:
(ancestor.ownerDocument || doc);
var queryResults = [];
var queryString = ['.//', ((xmlParseMode(docNode))?'html:':''),
tagName, ((className)?"[contains(concat(' ', @class, ' '), ' " +
className + " ')]":'')].join('');
r = docNode.evaluate(queryString, ancestor,
(xmlParseMode(docNode))?resolve:null,
global.XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
l = r.snapshotLength;
while (l--) { queryResults[l] = r[l]; }
return queryResults;
};
}

var getEBCN;
var getEBCS = (function() {


var m, // regexp matches and temp var in loop
tn, // tagName in s
id, // id in s
cn, // className in s
els, // candidate elements for return
ns, // elements to return
i, // loop index

el; // temp element variable

var get = (function() {
if (typeof queryXPath != 'undefined' && typeof getEBI !=
'undefined') {
return function(d, id, tn, cn) {
if (id) {
ns = [];
el = getEBI(id);
if (el) {
if ((tn == '*' || el.tagName.toLowerCase() == tn) &&


(!cn || ((m = el.className) &&

(' '+m+' ').indexOf(' '+cn+' ') > -1))) {
ns = [el];
}
}
return ns;
}
else {
return queryXPath(d, tn, cn);
}
};


}
if (typeof getEBI != 'undefined' &&
typeof getEBTN != 'undefined' &&
// match appears first in ECMAScript in v3 so test
String.prototype.match) {

return function(d, id, tn, cn) {


// Need this because getEBI only takes document object
// as optional second argument but getEBCS takes any
// element or a document as second argument.

els = (id && (d.nodeType == 9 || (!d.nodeType && !
d.tagName))) ?


((el=getEBI(id, d)) ? [el] : [])
:
getEBTN(tn, d);

ns = [];
i = els.length;
while (i--) {


el = els[i];
// Could call an external hasClassName function but in line
// it here for efficiency. There are no cross browser issue
// with checking className.
if ((!cn ||
((m = el.className) &&
(' '+m+' ').indexOf(' '+cn+' ')>-1)) &&

// If tn != '*' then el necessarily has tagname
property.

(tn == '*' || el.tagName.toLowerCase() == tn) &&
(!id || el.id == id)) {
ns[ns.length] = el;
}
}

return ns.reverse();
};
}
})();

if (get) {
return function(s, d) {


m = s.match(/^([^#\.]+)/);
tn = m ? m[1] : '*';

m = s.match(/#([^\.]+)/);
id = m ? m[1] : null;

m = s.match(/\.([^#]+)/);
cn = m ? m[1] : null;

return get(d || doc, id, tn, cn);
};
}
})();

if (getEBCS) {
getEBCN = function(s, d) {
return getEBCS('.' + s, d);
};
}

I was surprised to see that Windows Safari has XPath. So other than
IE, what modern browser doesn't have it?

Peter Michaux

unread,
Dec 10, 2007, 11:53:33 PM12/10/07
to
On Dec 10, 5:29 pm, David Mark <dmark.cins...@gmail.com> wrote:
> On Dec 10, 6:37 pm, Peter Michaux <petermich...@gmail.com> wrote:
>
> > On Dec 10, 2:36 pm, David Mark <dmark.cins...@gmail.com> wrote:
>
> > > On Dec 10, 2:30 pm, Peter Michaux <petermich...@gmail.com> wrote:

[snip]

> Slightly similar. It doesn't completely work around the IE bug (and I
> am not convinced it needs to), but it does add some value to the
> implementation. The gEBCS wrapper as it stands is probably only
> implementing half of what the w3c method will do. I don't think that
> is necessarily a bad thing, but it may cause some confusion in that
> the two branches will diverge considerably once there is a featured
> w3c method. And BTW, it wouldn't hurt to add the test for it and
> associated branch now (assuming we are going to wrap it.)

Wrapping an unstable and/or unimplemented spec seems to be jumping the
gun a bit.

> > The tables are turned and now I want to check something you don't :-)
>
> IE4/NN4 support 1.2 and I ignore the consequences of inadequate
> feature testing for anything that came before that. That's where I
> draw the line. The use of RegExp literals in this project will ensure
> that NN <= 4 and IE < 4 won't parse the scripts anyway.

Ok, I'll yield. We will do less testing!

[snip]

> > > That seems like an iffy test for a document node. It would be more
> > > robust to test if it is not an element node (see the gEBTN wrapper.)
>
> > Please post a replacement for the above line.
>
> See below.
>
>
>
> > > > ns = [];
> > > > for (i=0, ilen=els.length; i<ilen; ++i) {
>
> > > Does it not make sense to use while (--i) and reverse the results
> > > after the loop? At least for large numbers of nodes, it would seem
> > > more efficient.
>
> > I haven't tested this system. Is it really faster? By how much?
>
> Depends on the environment and what is happening inside the loop. In
> most cases it is relatively faster.

I tested the following in Firefox

// ----------------------

var start = (new Date()).getTime();
var a = [];
var i=100000;
while (i--) {
a[i]=i;
}
a.reverse();
console.log((new Date()).getTime() - start); // ~800

// ----------------------

var start = (new Date()).getTime();
var a = [];
for (var i=0; i<100000; i++) {
a[i]=i;
}
console.log((new Date()).getTime() - start); // ~900

// ----------------------

The first version with reverse must be a little faster because the
memory alloted for the array does not need to be dynamically. Sound
reasonable?

For what it's worth, 13% more time for the forward version when the
array is huge (100000) doesn't really bother me because it would be
very unlikely to deal with an array that big in a browser scripting
task. I don't mind the reverse way but it is a little more confusing
to understand.


> > > Would this be a more efficient test?
>
> > > // Outside of the loop
> > > re = new RegExp('(^|\\s)' + cn + '(\\s|$)')
>
> > > re.test(el.className)
>
> > Using a regular expression is much slower. I tested this. It is a big
> > difference.
>
> That surprises me. You could eliminate the extra string concatenation
> though.

If the function is to be optimized a lot more needs to be done than
that. The CSS selector string should be compiled into a JavaScript
string which represents a function and then that function string is
evaluated. This function can be cached to be used later. This sort of
optimization is really important if selectors with several simple
selectors (e.g. div div div) because those tasks are so much more
laborious. For simple selectors it is not so important.

[snip]

> > > Speaking of speed. I have an XPath version of this. It is tangled up
> > > in a general-purpose query function that has a few more options (e.g.
> > > checking for the presence of attributes.) But it wouldn't take too
> > > much effort to untangle it and add it as a branch to this.
>
> > Please do post that! I have never looked into this but want to learn
> > about.
>
> I spliced together a new version of function.

[snip]

> > > > getElementsByClassName and getElementsByCssSelector functions are both
> > > > sugar on top of what the browser offers. Given the limited scope of
> > > > what a getElementsByClassName function can do I think it would be best
> > > > to just implement it as one of the getElementsByCssSelector functions.
>
> > > I think it makes sense to include it as a one-liner that calls gEBCS.
>
> > I really don't mind either way (thanks to automated testing! :D). So
> > you are suggesting the following in two files?
>
> > getEBCN = function(cn, d) = {
> > /* lots of code*/
>
> > };
>
> > getEBCS = function(s, d) = {
> > return getEBCN(s.substring(1), d);
>
> > };
>
> No, exactly the opposite.

Ok.

[snip]

> I haven't tested this thoroughly.
>
> var queryXPath, resolve;
>
> if (isFeaturedMethod(doc, 'evaluate')) {
> resolve = function() { return 'http://www.w3.org/1999/xhtml';};
> queryXPath = function(ancestor, tagName, className) {
> var l, r, bSnapshot, docNode = (ancestor.nodeType == 9)?ancestor:
> (ancestor.ownerDocument || doc);

If ancestor is from a different document and doc is used, then the doc
is not the one that contains ancestor. It seems to me this could be a
big problem if ancestor is in an XHTML document and doc is an HTML
document. Isn't it necessary to walk up the DOM from ancestor to find
it's ownerDocument?


> var queryResults = [];
> var queryString = ['.//', ((xmlParseMode(docNode))?'html:':''),
> tagName, ((className)?"[contains(concat(' ', @class, ' '), ' " +
> className + " ')]":'')].join('');

Why use join() instead of just adding the bits together with string
concatenation?

> r = docNode.evaluate(queryString, ancestor,
> (xmlParseMode(docNode))?resolve:null,
> global.XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);


Please use a 70 character line width. This is very difficult to read
and time consuming to reformat so it is readable.

I didn't think the code for using xpath was going to be so big. Given
how big this code is I think speed profiles would be good if this is
to be recommended over the non-xpath version. I think that there won't
be too much difference for simple selectors.

> I was surprised to see that Windows Safari has XPath. So other than
> IE, what modern browser doesn't have it?

I don't know.

What I would like to know is why in Prototype.js they turn off using
xpath for any browser with "AppleWebKit" in navigator.userAgent. Do
early versions of Safari have a bug in the xpath implementation. If
there is a bug, a feature test would be needed.

Peter Michaux

unread,
Dec 11, 2007, 2:05:04 AM12/11/07
to


For example

if (typeof getEBI != 'undefined' &&

typeof getEBTN != 'undefined') {

var getEBCS;

(function() {

// http://elfz.laacz.lv/beautify/
var compile = function(s) {


var m, // regexp matches

tn, // tagName in s
id, // id in s
cn, // className in s

f; // the function body

m = s.match(/^([^#\.]+)/);

tn = m ? m[1] : null;

m = s.match(/#([^\.]+)/);
id = m ? m[1] : null;

m = s.match(/\.([^#]+)/);
cn = m ? m[1] : null;

f = 'var i,els,el,m,ns=[];';
if (id) {
f += 'els=(!d || d.documentElement) ?' +
'((el=getEBI("'+id+'", d)) ? [el] : []) :' +
'getEBTN("'+(tn||'*')+'", d);'
}
else {
f += 'els = getEBTN("'+(tn||'*')+'", d);';
}

f += 'i = els.length;' +
'while (i--) {' +
'el = els[i];' +
'if (';
if (id) {
f += 'el.id=="'+id+'"';
}
if ((cn||tn) && id) {
f += '&&';
}
if (tn) {
f += 'el.tagName.toLowerCase() == "' + tn + '"';
}
if (cn && tn) {
f += '&&';
}
if (cn) {
f += '((m = el.className) &&' +
'(" "+m+" ").indexOf(" '+cn+' ")>-1)';
}
f += '){' +
'ns[ns.length] = el;' +
'}' +
'}';

f += 'return ns.reverse();';

return new Function('d', f);
}

var cache = {};

getEBCS = function(s, d) {
if (!cache[s]) {
cache[s] = compile(s);
}
return cache[s](d);
}

})();

}

// -------------------------------

I may have been wrong saying that

ns[ns.length] = el;

is faster than

ns.push(el)

// --------------------------------

If you find out that there is no problem with xpath and safari or that
there is a feature test, perhaps we can compare the two for speed.

David Mark

unread,
Dec 11, 2007, 3:49:17 AM12/11/07
to
On Dec 10, 11:53 pm, Peter Michaux <petermich...@gmail.com> wrote:
> On Dec 10, 5:29 pm, David Mark <dmark.cins...@gmail.com> wrote:
>
> > On Dec 10, 6:37 pm, Peter Michaux <petermich...@gmail.com> wrote:
>
> > > On Dec 10, 2:36 pm, David Mark <dmark.cins...@gmail.com> wrote:
>
> > > > On Dec 10, 2:30 pm, Peter Michaux <petermich...@gmail.com> wrote:
>
> [snip]
>
> > Slightly similar. It doesn't completely work around the IE bug (and I
> > am not convinced it needs to), but it does add some value to the
> > implementation. The gEBCS wrapper as it stands is probably only
> > implementing half of what the w3c method will do. I don't think that
> > is necessarily a bad thing, but it may cause some confusion in that
> > the two branches will diverge considerably once there is a featured
> > w3c method. And BTW, it wouldn't hurt to add the test for it and
> > associated branch now (assuming we are going to wrap it.)
>
> Wrapping an unstable and/or unimplemented spec seems to be jumping the
> gun a bit.

I think it is unimplemented at the moment. I guess it may be unstable
when it is implemented in Beta versions. So that makes sense.

It is the comparison that is faster (boolean vs. numeric comparison.)

>
> For what it's worth, 13% more time for the forward version when the
> array is huge (100000) doesn't really bother me because it would be
> very unlikely to deal with an array that big in a browser scripting
> task. I don't mind the reverse way but it is a little more confusing
> to understand.

Right.

You mean if the element node doesn't have an ownerDocument property?
I don't see that as possible in a browser that supports XPath, but I
could be wrong. Granted the || doc fallback is ambiguous.

>
> > var queryResults = [];
> > var queryString = ['.//', ((xmlParseMode(docNode))?'html:':''),
> > tagName, ((className)?"[contains(concat(' ', @class, ' '), ' " +
> > className + " ')]":'')].join('');
>
> Why use join() instead of just adding the bits together with string
> concatenation?

In the original query function, which accepted multiple query
parameters, this was in a loop and I thought the join would be faster.

Well, the actual XPath code is pretty small. It is the inline
duplication of code that has grown the gEBCS function, but that is
necessary for the branch that loops.

> how big this code is I think speed profiles would be good if this is
> to be recommended over the non-xpath version. I think that there won't
> be too much difference for simple selectors.

It is an order or magnitude faster for all cases, but won't have a big
impact unless the fallback branch loops through lots of results (e.g.
finding className matches for any tagName.) I definitely think this
should be an alternate version. Especially if you think we will want
to provide a wrapper for gEBCS that does more than just tagNames and
classNames.

>
> > I was surprised to see that Windows Safari has XPath. So other than
> > IE, what modern browser doesn't have it?
>
> I don't know.
>
> What I would like to know is why in Prototype.js they turn off using
> xpath for any browser with "AppleWebKit" in navigator.userAgent.

Who knows why they do anything? Looking through Rails tickets makes
you wonder if they know. Googling for combinations of "XPath bug",
"WebKit" and "Safari" yielded nothing. I suspect that there might be
a limitation in some older Safari version that defeats some of the
more outlandish of their supported queries, but that is just a guess.

Do
> early versions of Safari have a bug in the xpath implementation. If
> there is a bug, a feature test would be needed.

I agree if a bug can be confirmed.

David Mark

unread,
Dec 11, 2007, 3:59:53 AM12/11/07
to
> //http://elfz.laacz.lv/beautify/

That looks like it would speed up the IE branch considerably. It will
still be slower than browsers that can use XPath though.

>
> I may have been wrong saying that
>
> ns[ns.length] = el;
>
> is faster than
>
> ns.push(el)

It seemed to me that push would be faster. I haven't had time to test
it though. As I use a wrapper for push, I have typically used the
former method, which is obviously faster than calling a wrapper. But,
for example, the XPath code doesn't need to worry about
Array.prototype.push, so it would be a okay to use it without the
wrapper.

>
> // --------------------------------
>
> If you find out that there is no problem with xpath and safari or that

It will be impossible to prove that there isn't one. I can only say
that I can't find a reference to one.

> there is a feature test, perhaps we can compare the two for speed.

If a bug can be confirmed, I am sure I can write a feature test for
it. There isn't really a need to compare them for speed as XPath is
going to be faster than looping in virtually every case.

David Mark

unread,
Dec 11, 2007, 4:28:29 AM12/11/07
to
On Dec 11, 2:05 am, Peter Michaux <petermich...@gmail.com> wrote:
[snip]
>
> If you find out that there is no problem with xpath and safari or that
> there is a feature test, perhaps we can compare the two for speed.

I just spotted a mistake in the queryXPath function. Remove
"global." Also, for this version, the queryString variable is
unneeded (can just be an expression passed to evaluate.)

It also looks like there could be some additional consolidation
between the two branches if size is an issue and the XPath branch
could get away with calling generalized hasClass and tagName
comparison functions (that's the longest line in that branch.)

David Mark

unread,
Dec 11, 2007, 5:32:28 AM12/11/07
to
On Dec 11, 4:28 am, David Mark <dmark.cins...@gmail.com> wrote:
[snip]

I consolidated a few lines, shortened a few variable names and removed
a couple of unneeded remnants from my original version (as well as the
match test from your branch.)

Note that I did update your document node test in the previously
posted version. I think it is better as it won't be fooled by ancient
browsers that don't support documentElement (and those are the ones
that will take that branch.) Also, you need the previously posted
xmlParseMode function to run this.

As for speed, I added a hundred or so extraneous elements to my test
page and short-circuited the XPath branch, and the result went from
instantaneous to about half a second for a couple of simple test cases
(e.g. searching the entire document for an element by className.)

And revisiting the feature test argument from a few posts back, I
really thing that isFeaturedMethod should be used, instead of things
like:

if (document.getElementById)

I don't know of any browser that returns a truthy, but non-callable
value for this host method, but when you consider that every host
method should be tested in uniform fashion, you can imagine that at
least one of them might do so in some past agent. And if you exclude
that possibility, there is still the ActiveX issue to deal with.
Those host methods throw a script error when tested this way. All it
would take for the above example to break in IE8 would be for MS to
decide to implement some or all document nodes as ActiveX objects. If
that sounds far-fetched, realize that they already implement some
element nodes as ActiveX objects (e.g. anchors that link to news
resources.) I account for that possibility in my getAttribute
wrapper. In other words, this example may look perfectly benign, but
one line can currently throw a script error in IE and the other may in
the future.

var a = document.getElementById('myanchor');
if (a.href) { ... }

I would prefer:

var a, doc = this.document;
if (doc && isFeaturedMethod(doc, 'getElementById') {
a = doc.getElementById('myanchor');
if (getAttribute(a, 'href')) { ... }
}

Or shortened to assume the global document property:

var a;
if (isFeaturedMethod(document, 'getElementById') {
a = document.getElementById('myanchor');
if (getAttribute(a, 'href')) { ... }
}

Granted, you wouldn't have to use a getAttribute wrapper, you could
use any function that takes the 'unknown' type into account.
I don't see this as paranoia as the problem case exists today (in IE7)
and in fact caused a script error on a group member's site a few
months back.

Here is the updated function. Looks like a couple of lines still
wrapped. Sorry. I may start linking to snippets as I really dislike
dealing with these ng issues.

var queryXPath, resolve;

if (isFeaturedMethod(doc, 'evaluate')) {
resolve = function() { return 'http://www.w3.org/1999/xhtml'; };

queryXPath = function(a, tn, cn) {
var l, r, docNode = (a.nodeType == 9)?
a:(a.ownerDocument || doc);
var q = [];
r = docNode.evaluate(['.//',
((xmlParseMode(docNode))?'html:':''),
tn,
((cn)?"[contains(concat(' ', @class, ' '),
' " + cn + " ')]":'')].join(''),
a,
(xmlParseMode(docNode))?resolve:null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null);
l = r.snapshotLength;
while (l--) { q[l] = r[l]; }
return q;
};
}

var getEBCN;
var getEBCS = (function() {
var m, // regexp matches and temp var in loop

tn, // tagName in s
id, // id in s
cn, // className in s

els, // candidate elements for return
ns, // elements to return
i, // loop index
el; // temp element variable
var get = (function() {
if (typeof queryXPath != 'undefined' &&
typeof getEBI != 'undefined') {
return function(d, id, tn, cn) {
if (id) {
ns = [];

if (el = getEBI(id)) {


if ((tn == '*' || el.tagName.toLowerCase() == tn) &&
(!cn || ((m = el.className) &&
(' '+m+' ').indexOf(' '+cn+' ') > -1))) {
ns = [el];
}
}
return ns;
}

return queryXPath(d, tn, cn);
};


}
if (typeof getEBI != 'undefined' &&
typeof getEBTN != 'undefined') {

return function(d, id, tn, cn) {

m = s.match(/^([^#\.]+)/);

tn = m ? m[1] : '*';

m = s.match(/#([^\.]+)/);
id = m ? m[1] : null;

m = s.match(/\.([^#]+)/);
cn = m ? m[1] : null;

return get(d || doc, id, tn, cn);

David Mark

unread,
Dec 11, 2007, 6:21:31 AM12/11/07
to
On Dec 10, 11:53 pm, Peter Michaux <petermich...@gmail.com> wrote:
[snip]
>
> What I would like to know is why in Prototype.js they turn off using
> xpath for any browser with "AppleWebKit" in navigator.userAgent. Do
> early versions of Safari have a bug in the xpath implementation. If
> there is a bug, a feature test would be needed.

I found it. It is as I suspected. They found a specific selector
syntax that failed in Safari 3 and instead of feature testing for that
issue, they disabled XPath altogether (based on the userAgent
string.) The post did say that they plan to implement proper feature
testing for this in the next release. Why they don't just scrap the
whole project and start over is a mystery.

http://groups.google.com/group/prototype-core/browse_thread/thread/ae21402d7f7d253c

This won't be an issue for now. Quoting from that thread:

"...the failures are related to the ":first-of-type/:first-child"
pseudoclasses (and their brethren)"

If and when we decide to add such selectors, we will have to revisit
this issue.

Peter Michaux

unread,
Dec 11, 2007, 12:32:51 PM12/11/07
to
On Dec 11, 12:59 am, David Mark <dmark.cins...@gmail.com> wrote:
> On Dec 11, 2:05 am, Peter Michaux <petermich...@gmail.com> wrote:

[snip]

> > I may have been wrong saying that
>
> > ns[ns.length] = el;
>
> > is faster than
>
> > ns.push(el)
>
> It seemed to me that push would be faster. I haven't had time to test
> it though. As I use a wrapper for push, I have typically used the
> former method, which is obviously faster than calling a wrapper. But,
> for example, the XPath code doesn't need to worry about
> Array.prototype.push, so it would be a okay to use it without the
> wrapper.

The most popular browser, still by a long shot, is IE which doesn't
have XPath. That means the non-XPath version still needs to be fast.

> > // --------------------------------
>
> > If you find out that there is no problem with xpath and safari or that
>
> It will be impossible to prove that there isn't one. I can only say
> that I can't find a reference to one.
>
> > there is a feature test, perhaps we can compare the two for speed.
>
> If a bug can be confirmed, I am sure I can write a feature test for
> it. There isn't really a need to compare them for speed as XPath is
> going to be faster than looping in virtually every case.

In many cases, download time of more code will far outweigh any speed
advantage of the XPath branch. I always try to keep in mind that many
people are still using 56K modems.

Peter Michaux

unread,
Dec 11, 2007, 12:35:05 PM12/11/07
to
On Dec 11, 1:28 am, David Mark <dmark.cins...@gmail.com> wrote:
> On Dec 11, 2:05 am, Peter Michaux <petermich...@gmail.com> wrote:
> [snip]
>
>
>
> > If you find out that there is no problem with xpath and safari or that
> > there is a feature test, perhaps we can compare the two for speed.
>
> I just spotted a mistake in the queryXPath function. Remove
> "global." Also, for this version, the queryString variable is
> unneeded (can just be an expression passed to evaluate.)
>
> It also looks like there could be some additional consolidation
> between the two branches if size is an issue

It is always an issue!

[snip]

Peter Michaux

unread,
Dec 11, 2007, 12:42:41 PM12/11/07
to
On Dec 11, 12:49 am, David Mark <dmark.cins...@gmail.com> wrote:
> On Dec 10, 11:53 pm, Peter Michaux <petermich...@gmail.com> wrote:
>

[snip]

> > how big this code is I think speed profiles would be good if this is
> > to be recommended over the non-xpath version. I think that there won't
> > be too much difference for simple selectors.
>
> It is an order or magnitude faster for all cases, but won't have a big
> impact unless the fallback branch loops through lots of results (e.g.
> finding className matches for any tagName.) I definitely think this
> should be an alternate version. Especially if you think we will want
> to provide a wrapper for gEBCS that does more than just tagNames and
> classNames.

I do think the XPATH version should be included. I don't think we
should make gEBCS handle more complex selectors for now.

[snip]

Peter Michaux

unread,
Dec 11, 2007, 12:47:04 PM12/11/07
to
On Dec 11, 3:21 am, David Mark <dmark.cins...@gmail.com> wrote:
> On Dec 10, 11:53 pm, Peter Michaux <petermich...@gmail.com> wrote:
> [snip]
>
>
>
> > What I would like to know is why in Prototype.js they turn off using
> > xpath for any browser with "AppleWebKit" in navigator.userAgent. Do
> > early versions of Safari have a bug in the xpath implementation. If
> > there is a bug, a feature test would be needed.
>
> I found it. It is as I suspected. They found a specific selector
> syntax that failed in Safari 3 and instead of feature testing for that
> issue, they disabled XPath altogether (based on the userAgent
> string.) The post did say that they plan to implement proper feature
> testing for this in the next release.

It is an emergency situation that needs fixing. If they don't have a
feature test for it now then they have to disable the whole thing in
Safari otherwise the library is broken. That makes some sense to me.


> Why they don't just scrap the whole project and start over is a mystery.

They would need to abandon their fundamental concept which is
augmenting built in prototypes.

VK

unread,
Dec 11, 2007, 12:52:46 PM12/11/07
to
On Dec 10, 4:00 am, Peter Michaux <petermich...@gmail.com> wrote:
> Even the documentation doesn't address the IE id/name problem.

id/name problem is not the preoccupation of a library developer: it is
the preoccupation of developer making the final script running on a
particular page. If she makes a broken page w/o any testing then it is
he free choice but not your headache.

> > 2) Runtime performance is the King, fall-back protection is the sh** -
> > as long as no security violation is involved.
>
> A tangential thinker, eh?

A very soft definition, appreciated. :-) Normally if assume the
possibility do not care of say IE4 or IE5 it is diagnosed as
"clueless !@#$% programmer". I just looked at the direction of the
discussion and I saw - maybe mistakenly - that it goes once again
towards the "programming onanism". So for some very simple task all
possible _theoretical_ possibilities to fail being collected from all
imaginable sources: W3C proposals, ancient browsers, personal fears
etc. and then all these theoretical problems overcame with very
practical processor time.

function $(id) {
return document.getElementById(id);
}

Again: what exactly browser being in use this year will fail to
execute properly this function?

VK

unread,
Dec 11, 2007, 1:03:18 PM12/11/07
to
On Dec 11, 8:32 pm, Peter Michaux <petermich...@gmail.com> wrote:
> The most popular browser, still by a long shot, is IE which doesn't
> have XPath.

You may want to update your data: IE supports XPath for the last 8
years at least. The only possible issue may be with MSXML3 where the
default selection language XSLPatterns and not XPath. However low
chances are to get IE4/IE5 on Windows 2000/ME, you may set the
language explicitly in your script before XPath usage:
xmlDoc.setProperty('SelectionLanguage', 'XPath');

David Mark

unread,
Dec 11, 2007, 1:07:57 PM12/11/07
to

But that's one download (assuming room in their cache) vs. however
many times the site's code queries the DOM.

David Mark

unread,
Dec 11, 2007, 1:15:38 PM12/11/07
to

Oops. Too late (at least here.) I compared the XPath and CSS2 specs
and decided it wouldn't be too difficult to add logic to each branch
to implement the various other selectors. The suggestion of pre-
compiling code to speed up the slower branch intrigued me enough to
try it. I am currently testing the updated parse logic without the
pre-compile trick. So far so good, but I am sure it will be useless
for "deep" queries without that optimization.

Peter Michaux

unread,
Dec 11, 2007, 1:16:42 PM12/11/07
to
On Dec 11, 2:32 am, David Mark <dmark.cins...@gmail.com> wrote:
> On Dec 11, 4:28 am, David Mark <dmark.cins...@gmail.com> wrote:

[snip]

> And revisiting the feature test argument from a few posts back, I


> really thing that isFeaturedMethod should be used,

Why not just "isMethod"? It is always testing a function property of
an object which is called a method. This isFeaturedMethod gets
plastered all over the place and if "isMethod" is sufficient then I
always like shorter.

> instead of things
> like:
>
> if (document.getElementById)
>
> I don't know of any browser that returns a truthy, but non-callable
> value for this host method, but when you consider that every host
> method should be tested in uniform fashion, you can imagine that at
> least one of them might do so in some past agent. And if you exclude
> that possibility, there is still the ActiveX issue to deal with.
> Those host methods throw a script error when tested this way. All it
> would take for the above example to break in IE8 would be for MS to
> decide to implement some or all document nodes as ActiveX objects. If
> that sounds far-fetched, realize that they already implement some
> element nodes as ActiveX objects (e.g. anchors that link to news
> resources.)

What does an anchor to a news resource look like in HTML?

> I account for that possibility in my getAttribute
> wrapper. In other words, this example may look perfectly benign, but
> one line can currently throw a script error in IE and the other may in
> the future.
>
> var a = document.getElementById('myanchor');

How would the above line throw an error?

> if (a.href) { ... }

I assume this is the same error that would occur in the first "if" of
your post above if document.getElementById was an ActiveX object.


> I would prefer:
>
> var a, doc = this.document;
> if (doc && isFeaturedMethod(doc, 'getElementById') {
> a = doc.getElementById('myanchor');
> if (getAttribute(a, 'href')) { ... }
>
> }

I sure hope this kind of thing doesn't need to be used frequently in
the repository. It is very bulky.


> Or shortened to assume the global document property:
>
> var a;
> if (isFeaturedMethod(document, 'getElementById') {
> a = document.getElementById('myanchor');
> if (getAttribute(a, 'href')) { ... }
>
> }
>
> Granted, you wouldn't have to use a getAttribute wrapper, you could
> use any function that takes the 'unknown' type into account.
> I don't see this as paranoia as the problem case exists today (in IE7)
> and in fact caused a script error on a group member's site a few
> months back.
>
> Here is the updated function. Looks like a couple of lines still
> wrapped. Sorry. I may start linking to snippets as I really dislike
> dealing with these ng issues.

The code should be posted to the group so the discussions stored in
the archives are complete. If the code is not posted to the group then
it makes it difficult to quote it and insert comments, as I have done
below.


> var queryXPath, resolve;
>
> if (isFeaturedMethod(doc, 'evaluate')) {
> resolve = function() { return 'http://www.w3.org/1999/xhtml';};
> queryXPath = function(a, tn, cn) {
> var l, r, docNode = (a.nodeType == 9)?
> a:(a.ownerDocument || doc);

I think '||doc' should be removed and thought you agreed in another
post. This will require a feature test on ownerDocument where you test
for document.evaluate.

Ihe following returns false in Firefox

document.documentElement.ownerDocument == document.documentElement


> var q = [];
> r = docNode.evaluate(['.//',
> ((xmlParseMode(docNode))?'html:':''),
> tn,
> ((cn)?"[contains(concat(' ', @class, ' '),
> ' " + cn + " ')]":'')].join(''),

Is this join going to remain for some reason?


> a,
> (xmlParseMode(docNode))?resolve:null,
> XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
> null);
> l = r.snapshotLength;

I like single character variable names in JavaScript because that
means an aggressive minifier doesn't need to be used. The most
controversial are 'l' and 'O'. My font shows them fine but it is just
as easy to use another character to avoid discussion. Richard Cornford
already wrote about this in a thread when I used 'l'. I think 'i' is
the best choice here. I imagine that even men like Newton probably
used 'i' for sequence and series work. All the textbooks I ever used
in Math had 'i'.

How does the above fallback work and in which browser?


> ((el=getEBI(id, d)) ? [el] : [])
> :
> getEBTN(tn, d);
> ns = [];
> i = els.length;
> while (i--) {
> el = els[i];
> // Could call an external hasClassName function but in
> line
> // it here for efficiency. There are no cross browser
> issue
> // with checking className.
> if ((!cn ||
> ((m = el.className) &&
> (' '+m+' ').indexOf(' '+cn+' ')>-1)) &&
> // If tn != '*' then el necessarily has tagname
> property.
> (tn == '*' || el.tagName.toLowerCase() == tn) &&
> (!id || el.id == id)) {
> ns[ns.length] = el;
> }
> }
> return ns.reverse();
> };
> }

Do you think it would be better to use my CSS-->JavaScript compiler as
the fallback? IE is the most common browser so this fallback will be
taken for many years to come.

> })();
>
> if (get) {
> return function(s, d) {
> m = s.match(/^([^#\.]+)/);
> tn = m ? m[1] : '*';
>
> m = s.match(/#([^\.]+)/);
> id = m ? m[1] : null;
>
> m = s.match(/\.([^#]+)/);
> cn = m ? m[1] : null;
>
> return get(d || doc, id, tn, cn);
> };
> }
> })();
>
> if (getEBCS) {
> getEBCN = function(s, d) {
> return getEBCS('.' + s, d);
> };
> }

--

David Mark

unread,
Dec 11, 2007, 1:21:02 PM12/11/07
to
On Dec 11, 12:47 pm, Peter Michaux <petermich...@gmail.com> wrote:
> On Dec 11, 3:21 am, David Mark <dmark.cins...@gmail.com> wrote:
>
> > On Dec 10, 11:53 pm, Peter Michaux <petermich...@gmail.com> wrote:
> > [snip]
>
> > > What I would like to know is why in Prototype.js they turn off using
> > > xpath for any browser with "AppleWebKit" in navigator.userAgent. Do
> > > early versions of Safari have a bug in the xpath implementation. If
> > > there is a bug, a feature test would be needed.
>
> > I found it. It is as I suspected. They found a specific selector
> > syntax that failed in Safari 3 and instead of feature testing for that
> > issue, they disabled XPath altogether (based on the userAgent
> > string.) The post did say that they plan to implement proper feature
> > testing for this in the next release.
>
> It is an emergency situation that needs fixing. If they don't have a
> feature test for it now then they have to disable the whole thing in
> Safari otherwise the library is broken. That makes some sense to me.

It seems like the feature test would be dictated by whatever unit
test(s) failed.

>
> > Why they don't just scrap the whole project and start over is a mystery.
>
> They would need to abandon their fundamental concept which is
> augmenting built in prototypes.

Among other things.

Peter Michaux

unread,
Dec 11, 2007, 1:26:20 PM12/11/07
to

If you want to see something I did that is somewhat similar

<URL: http://peter.michaux.ca/examples/css-selector-match/match.js>

I threw that one together quickly from other code and that one is
buggy when multiple, comma separated selectors are used, I believe.

I think it is best to save these complex selectors for another time. I
know I won't be using them and a code review and serious testing
doesn't interest me right now.

David Mark

unread,
Dec 11, 2007, 1:29:14 PM12/11/07
to
On Dec 11, 12:52 pm, VK <schools_r...@yahoo.com> wrote:
> On Dec 10, 4:00 am, Peter Michaux <petermich...@gmail.com> wrote:
>
> > Even the documentation doesn't address the IE id/name problem.
>
> id/name problem is not the preoccupation of a library developer: it is
> the preoccupation of developer making the final script running on a
> particular page. If she makes a broken page w/o any testing then it is
> he free choice but not your headache.

You don't seem to understand that the developer may not realize they
screwed up if the gEBI wrapper is allowed to return elements without
the proper ID.

>
> > > 2) Runtime performance is the King, fall-back protection is the sh** -
> > > as long as no security violation is involved.
>
> > A tangential thinker, eh?
>
> A very soft definition, appreciated. :-) Normally if assume the
> possibility do not care of say IE4 or IE5 it is diagnosed as
> "clueless !@#$% programmer". I just looked at the direction of the
> discussion and I saw - maybe mistakenly - that it goes once again
> towards the "programming onanism". So for some very simple task all

I don't know if you are mistaken as I don't have time to look that up.

> possible _theoretical_ possibilities to fail being collected from all
> imaginable sources: W3C proposals, ancient browsers, personal fears
> etc. and then all these theoretical problems overcame with very
> practical processor time.
>
> function $(id) {
> return document.getElementById(id);
>
> }

That is nonsense and we have long since moved on from gEBI. Needless
to say, the final solution did not look like that.

>
> Again: what exactly browser being in use this year will fail to
> execute properly this function?

Who knows what browsers (or other agents) are in use this year? One
thing is for sure, if you blindly declare a function that wraps a host
method which may not be supported, calling applications will have
unpredictable behavior.

David Mark

unread,
Dec 11, 2007, 1:30:07 PM12/11/07
to
On Dec 11, 1:03 pm, VK <schools_r...@yahoo.com> wrote:
> On Dec 11, 8:32 pm, Peter Michaux <petermich...@gmail.com> wrote:
>
> > The most popular browser, still by a long shot, is IE which doesn't
> > have XPath.
>
> You may want to update your data: IE supports XPath for the last 8
> years at least. The only possible issue may be

Not for HTML documents.

Peter Michaux

unread,
Dec 11, 2007, 1:52:47 PM12/11/07
to
On Dec 10, 11:05 pm, Peter Michaux <petermich...@gmail.com> wrote:
> On Dec 10, 8:53 pm, Peter Michaux <petermich...@gmail.com> wrote:


[snip]

> > If the function is to be optimized a lot more needs to be done than
> > that. The CSS selector string should be compiled into a JavaScript
> > string which represents a function and then that function string is
> > evaluated. This function can be cached to be used later. This sort of
> > optimization is really important if selectors with several simple
> > selectors (e.g. div div div) because those tasks are so much more
> > laborious. For simple selectors it is not so important.
>
> For example
>
> if (typeof getEBI != 'undefined' &&
> typeof getEBTN != 'undefined') {
>
> var getEBCS;
>
> (function() {
>

> //http://elfz.laacz.lv/beautify/

If for some reason a user sends an s that is "toString" then this will
cause a problem. I thought it would be better to use hasOwnProperty.
There is at least one bug with hasOwnProperty in Opera when s is a
number.

<URL: http://groups.google.com/group/comp.lang.javascript/msg/7ad2adb486fc3f20>

This bug could be worked around by prefixing the selector with a fixed
string.

Are there any other bugs with hasOwnProperty?


> cache[s] = compile(s);
> }
> return cache[s](d);
> }
>
> })();
>
> }

--

VK

unread,
Dec 11, 2007, 3:04:25 PM12/11/07
to
On Dec 11, 9:29 pm, David Mark <dmark.cins...@gmail.com> wrote:
> You don't seem to understand that the developer may not realize they
> screwed up if the gEBI wrapper is allowed to return elements without
> the proper ID.

The developer is screwed if the docs say "$(String id) returns DOM
element reference with such ID or null if doesn't exist" - and the
developer is using it to get over it an element by NAME or get
something useful out of say $(undefined); Such developer is screwed by
default and sooner realized it so he will start to read the docs - so
better for him. By compensating rude programming errors up to the last
possibility you do not help anyone: the program will still eventually
fail, but on higher execution level where it will be times more
difficult to find the reason.

> Who knows what browsers (or other agents) are in use this year?

If you don't know that then why are you trying to program in
JavaScript anything at all above simple form control helpers?
Theoretically possible someone is still using Windows 3.0 with
Netscape 2.0b (the first one with JavaScript 1.0)

Again: let's stop that fallacy pretending to program "for the
humanity's past, present and future generations". For _a hell of a
good_ start let's try to cover at least A grade UAs from Yahoo! chart
http://developer.yahoo.com/yui/articles/gbs/

If your program ever gets any popularity, you'll get a bunch of bug
reports and you'll fix them then.

> One thing is for sure, if you blindly declare a function that wraps a host
> method which may not be supported, calling applications will have
> unpredictable behavior.

If UA doesn't have document.getElementById support then it's NN4 or
older or IE4 or older or something else from the last century. If you
really want to make it work on IE4 then I want you to make it work on
NN4 as well as of the same period browser. What the problem? Not
possible? It is possible but extremely ugly and labor intensive. Much
much more labor intensive then ...else if (document.all)... and "look
ma' now how do I care of every possible user!".

VK

unread,
Dec 11, 2007, 3:14:11 PM12/11/07
to
On Dec 11, 9:30 pm, David Mark <dmark.cins...@gmail.com> wrote:
> On Dec 11, 1:03 pm, VK <schools_r...@yahoo.com> wrote:
>
> > On Dec 11, 8:32 pm, Peter Michaux <petermich...@gmail.com> wrote:
>
> > > The most popular browser, still by a long shot, is IE which doesn't
> > > have XPath.
>
> > You may want to update your data: IE supports XPath for the last 8
> > years at least.
>
> Not for HTML documents.

Eh? What in the name XPath has to do with HTML? It is a tool traverse
XML documents. OK, I guess it is just too esoteric to ask.

David Mark

unread,
Dec 11, 2007, 3:20:06 PM12/11/07
to
On Dec 11, 1:16 pm, Peter Michaux <petermich...@gmail.com> wrote:
> On Dec 11, 2:32 am, David Mark <dmark.cins...@gmail.com> wrote:
>
> > On Dec 11, 4:28 am, David Mark <dmark.cins...@gmail.com> wrote:
>
> [snip]
>
> > And revisiting the feature test argument from a few posts back, I
> > really thing that isFeaturedMethod should be used,
>
> Why not just "isMethod"? It is always testing a function property of
> an object which is called a method. This isFeaturedMethod gets
> plastered all over the place and if "isMethod" is sufficient then I
> always like shorter.

Fine by me. It is only for appropriate for testing methods of host
objects (which are the only ones that IE considers of type 'object.')
I called it "isFeaturedMethod" to make it clear that it was for
feature testing the host environment.

>
>
>
> > instead of things
> > like:
>
> > if (document.getElementById)
>
> > I don't know of any browser that returns a truthy, but non-callable
> > value for this host method, but when you consider that every host
> > method should be tested in uniform fashion, you can imagine that at
> > least one of them might do so in some past agent. And if you exclude
> > that possibility, there is still the ActiveX issue to deal with.
> > Those host methods throw a script error when tested this way. All it
> > would take for the above example to break in IE8 would be for MS to
> > decide to implement some or all document nodes as ActiveX objects. If
> > that sounds far-fetched, realize that they already implement some
> > element nodes as ActiveX objects (e.g. anchors that link to news
> > resources.)
>
> What does an anchor to a news resource look like in HTML?

From Dr. John Stockton's page:

<a href="news:uk.tech.y2k">newsgroup</a>

You don't see these much anymore. Why IE7 implements them as ActiveX
objects is anybody's guess.

>
> > I account for that possibility in my getAttribute
> > wrapper. In other words, this example may look perfectly benign, but
> > one line can currently throw a script error in IE and the other may in
> > the future.
>
> > var a = document.getElementById('myanchor');
>
> How would the above line throw an error?

If gEBI was not a featured method of the document object or if
document were implemented as an ActiveX object.

>
> > if (a.href) { ... }
>
> I assume this is the same error that would occur in the first "if" of
> your post above if document.getElementById was an ActiveX object.

If document was an ActiveX object. The error occurs when [[Get]] is
called on methods of ActiveX objects, not on the objects themselves.

>
> > I would prefer:
>
> > var a, doc = this.document;
> > if (doc && isFeaturedMethod(doc, 'getElementById') {
> > a = doc.getElementById('myanchor');
> > if (getAttribute(a, 'href')) { ... }
>
> > }
>
> I sure hope this kind of thing doesn't need to be used frequently in
> the repository. It is very bulky.

Nothing like that would be in the repository. It is just an example.
The check for gEBI is done once when the wrapper is created. I check
document once at the top of my base module. If isn't present, then
none of the document-related functions are created. After that, any
code that requires it can just do this:

if (typeof getEBI != 'undefined') { ... }

>
>
>
> > Or shortened to assume the global document property:
>
> > var a;
> > if (isFeaturedMethod(document, 'getElementById') {
> > a = document.getElementById('myanchor');
> > if (getAttribute(a, 'href')) { ... }
>
> > }
>
> > Granted, you wouldn't have to use a getAttribute wrapper, you could
> > use any function that takes the 'unknown' type into account.
> > I don't see this as paranoia as the problem case exists today (in IE7)
> > and in fact caused a script error on a group member's site a few
> > months back.
>
> > Here is the updated function. Looks like a couple of lines still
> > wrapped. Sorry. I may start linking to snippets as I really dislike
> > dealing with these ng issues.
>
> The code should be posted to the group so the discussions stored in
> the archives are complete. If the code is not posted to the group then
> it makes it difficult to quote it and insert comments, as I have done
> below.

Makes sense.

>
> > var queryXPath, resolve;
>
> > if (isFeaturedMethod(doc, 'evaluate')) {
> > resolve = function() { return 'http://www.w3.org/1999/xhtml';};
> > queryXPath = function(a, tn, cn) {
> > var l, r, docNode = (a.nodeType == 9)?
> > a:(a.ownerDocument || doc);
>
> I think '||doc' should be removed and thought you agreed in another
> post. This will require a feature test on ownerDocument where you test
> for document.evaluate.

Right.

>
> Ihe following returns false in Firefox
>
> document.documentElement.ownerDocument == document.documentElement

As it should. However, this returns true:

document.documentElement.ownerDocument == document

>
> > var q = [];
> > r = docNode.evaluate(['.//',
> > ((xmlParseMode(docNode))?'html:':''),
> > tn,
> > ((cn)?"[contains(concat(' ', @class, ' '),
> > ' " + cn + " ')]":'')].join(''),
>
> Is this join going to remain for some reason?

Not necessarily.

>
> > a,
> > (xmlParseMode(docNode))?resolve:null,
> > XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
> > null);
> > l = r.snapshotLength;
>
> I like single character variable names in JavaScript because that
> means an aggressive minifier doesn't need to be used. The most
> controversial are 'l' and 'O'. My font shows them fine but it is just
> as easy to use another character to avoid discussion. Richard Cornford
> already wrote about this in a thread when I used 'l'. I think 'i' is
> the best choice here. I imagine that even men like Newton probably
> used 'i' for sequence and series work. All the textbooks I ever used
> in Math had 'i'.

I have been in the habit of using l for length, but in this case it
doesn't make sense as it is then used as an iterator.

Any browser that does not support nodeType should return true for
document nodes as they have no tagName property.

>
>
>
> > ((el=getEBI(id, d)) ? [el] : [])
> > :
> > getEBTN(tn, d);
> > ns = [];
> > i = els.length;
> > while (i--) {
> > el = els[i];
> > // Could call an external hasClassName function but in
> > line
> > // it here for efficiency. There are no cross browser
> > issue
> > // with checking className.
> > if ((!cn ||
> > ((m = el.className) &&
> > (' '+m+' ').indexOf(' '+cn+' ')>-1)) &&
> > // If tn != '*' then el necessarily has tagname
> > property.
> > (tn == '*' || el.tagName.toLowerCase() == tn) &&
> > (!id || el.id == id)) {
> > ns[ns.length] = el;
> > }
> > }
> > return ns.reverse();
> > };
> > }
>
> Do you think it would be better to use my CSS-->JavaScript compiler as
> the fallback? IE is the most common browser so this fallback will be
> taken for many years to come.

Yes. I think that would be a good idea.

Peter Michaux

unread,
Dec 11, 2007, 3:39:50 PM12/11/07
to
On Dec 11, 12:20 pm, David Mark <dmark.cins...@gmail.com> wrote:
> On Dec 11, 1:16 pm, Peter Michaux <petermich...@gmail.com> wrote:
>
> > On Dec 11, 2:32 am, David Mark <dmark.cins...@gmail.com> wrote:
>
> > > On Dec 11, 4:28 am, David Mark <dmark.cins...@gmail.com> wrote:

[snip]


> > > I account for that possibility in my getAttribute
> > > wrapper. In other words, this example may look perfectly benign, but
> > > one line can currently throw a script error in IE and the other may in
> > > the future.
>
> > > var a = document.getElementById('myanchor');
>
> > How would the above line throw an error?
>
> If gEBI was not a featured method of the document object or if
> document were implemented as an ActiveX object.

I was assuming document.getElementById is available.

If document is an ActiveX object why would it error to call it's
getElementById method?


> > > if (a.href) { ... }
>
> > I assume this is the same error that would occur in the first "if" of
> > your post above if document.getElementById was an ActiveX object.
>
> If document was an ActiveX object. The error occurs when [[Get]] is
> called on methods of ActiveX objects, not on the objects themselves.

I'm lost.

If document is present and an ActiveX and has a getElementById
property, what you are saying is the following line will error

if (document.getElementById)


If document is present and an ActiveX and has a getElementById
property, what will happen in the next line? I hope not an error
because then there would be no point in having methods on an ActiveX
object

document.getElementById('foo')


[snip]

> > > return function(d, id, tn, cn) {
> > > // Need this because getEBI only takes document object
> > > // as optional second argument but getEBCS takes any
> > > // element or a document as second argument.
> > > els = (id && (d.nodeType == 9 || (!d.nodeType && !
> > > d.tagName))) ?
>
> > How does the above fallback work and in which browser?
>
> Any browser that does not support nodeType should return true for
> document nodes as they have no tagName property.

What I'm worried about is if the browser does not support nodeType and
some d that shouldn't have a tagName is used. Is this just a
documentation issue that says the second parameter to getEBCS(s,d)
must be a document or an element? That is fine with me.

David Mark

unread,
Dec 11, 2007, 3:55:23 PM12/11/07
to

I wasn't going to bother with commas for the moment. Nor am I
considering spaces inside of quoted attribute values. And speaking of
things I didn't consider, I see you are doing something with
parentheses. I don't think I have ever seen CSS selectors grouped
with parentheses.

All I am doing is adding a small amount of logic to each branch to
enable things like:

div.test div a
*>div a[href="test"]
div+div a span.test

I think I have come up with a clever and efficient way of doing this.
Even without the pre-compile optimizations you suggested, it seems to
be doing pretty well in terms of speed (at least in IE) and didn't
bloat either branch too badly.

>
> I think it is best to save these complex selectors for another time. I
> know I won't be using them and a code review and serious testing
> doesn't interest me right now.

I agree. I certainly won't be using them either. For now, we just
need to optimize the simple version with your Function constructor
technique. I will be interested to see how much it improves the
performance of both versions.

David Mark

unread,
Dec 11, 2007, 4:11:22 PM12/11/07
to

Yep.

>
> If document is present and an ActiveX and has a getElementById
> property, what will happen in the next line? I hope not an error
> because then there would be no point in having methods on an ActiveX
> object
>
> document.getElementById('foo')

No problem there. That invokes the internal [[Call]] method, not
[[Get]]. Try this out with window.external.addFavorite.

>
> [snip]
>
> > > > return function(d, id, tn, cn) {
> > > > // Need this because getEBI only takes document object
> > > > // as optional second argument but getEBCS takes any
> > > > // element or a document as second argument.
> > > > els = (id && (d.nodeType == 9 || (!d.nodeType && !
> > > > d.tagName))) ?
>
> > > How does the above fallback work and in which browser?
>
> > Any browser that does not support nodeType should return true for
> > document nodes as they have no tagName property.
>
> What I'm worried about is if the browser does not support nodeType and
> some d that shouldn't have a tagName is used. Is this just a
> documentation issue that says the second parameter to getEBCS(s,d)
> must be a document or an element? That is fine with me.

Yes.

It is loading more messages.
0 new messages