DOM element selectors

D

Derek Basch

I have a simple XHTML document that I would like to apply javascript
functionality to:

<body>
<div class="length">9</div>
<div class="sequence">AAAAAAATTTAAA</div>
<div class="prediction">1.68509 1.60743 1.32631 1.38815 1.24905</div>

<div class="length">5</div>
<div class="sequence">TTTAAAAAA</div>
<div class="prediction">2.4898</div>

<div class="length">7</div>
<div class="sequence">TTTAAASSSSAAA</div>
<div class="prediction">2.4898 5.625 2.589 5.287</div>
</body>

Say that I wanted to select all of the <div> tags of class "length" and
operate on them. My options are limited.

1)
If I wanted to use document.getElementsByTagName() I could make each
"length" class <div> tag into a different, distinct, tag. <h1> for
"length", <h2> for "sequence", etc.. This seems rigid and
non-descriptive of the nature of the element. Also, what happens when I
run out of <h*> tags to utilize?

2)
If I want to use document.getElementById() I need to ensure that each
"length" <div> has a unique "id" attribute (length_1, length_2, etc..).
Then, create an array of these elements and operate on them.
(ref.
http://groups-beta.google.com/group...frm/thread/1012a90fa6c82f96/8082854908132dea?.
)
This seems cumbersome and failure prone.

3)
Finally, I could give each "length" <div> tag an "id" attribute of
"length" and select them using document.getElementsByName(). This is
not valid XHTML. Quote from W3 validator:

"An "id" is a unique identifier. Each time this attribute is used in a
document it must have a different value. If you are using this
attribute as a hock for style sheets it may be more appropriate to use
classes (which group elements) than id (which are used to identify
exactly one element)."

Why is there no DOM method for selecting a class of elements? What is
my best strategy for dealing with this issue? Thanks everyone.
 
S

Spats30

If you truly write XHTML code and have the <xml> declaration at the,
sounds to me like you would be better off saving this as an XML
document. Then create a PHP, ASP, JSP or whatever page to use a
server-side language to pull in the data (XML file) where you can
better programatically convert things to their proper H tag.
 
M

Michael Winter

Derek Basch wrote:

[snip]
1) If I wanted to use document.getElementsByTagName() [...]

You'd do something like

var divs = document.getElementsByTagName('div'),
type = 'length',
re = new RegExp('(^|\\s+)' + type + '(\\s+|$)');

for(var i = 0, n = divs.length; i < n; ++i) {
if(re.test(divs.className)) {
/* Have DIV of class 'type' */
}
}

[snip]
3) Finally, I could give each "length" <div> tag an "id" attribute
of "length" and select them using document.getElementsByName().
This is not valid XHTML. [...]

It wouldn't work, either. Unsurprisingly, the getElementsByName method
only returns elements which have a matching name attribute. With
XHTML, this is further restricted to only *form controls* which have a
matching name attribute.

Mike
 
D

Derek Basch

Thanks for the solution Mike.

Spats30, that is the type of thing that I am trying to avoid. The data
actually does start out as a XML document transformed to XHTML using
XSL. Using <h*> tags would be sloppy for the reasons I previously
stated.

Seeing that you have to create a less that elegant RegExp to solve this
problem am I correct in assuming that the DOM is in need of something
like document.getElementsByClassName() ?
 
M

Michael Winter

Derek Basch wrote:

[snip]
Seeing that you have to create a less that elegant RegExp to solve
this problem [...]

You don't /have/ to. The regular expression, /(^|\s+)<class
name>(\s+|$)/, is just the safe way of doing it: it ensures that you
don't get partial matches (as you would with the indexOf method), and
it's more flexible than a simple comparison. However, if you can
guarantee that a simpler approach is possible, then use it.
am I correct in assuming that the DOM is in need of something like
document.getElementsByClassName() ?

It might be useful in some cases, but it's trivial to implement. The
only advantage to a defined method that I can think of (at the moment)
is that it would be implemented natively so it would execute faster.

Mike
 
R

RobG

Derek Basch wrote:
[...]
Seeing that you have to create a less that elegant RegExp to solve this
problem am I correct in assuming that the DOM is in need of something
like document.getElementsByClassName() ?

What you are trying to do is create a group of elements that you
can access with a single identifier, much how tag name can be
used to return a collection of elements with a particular tag.

Using the class attribute is a bit if a hack, as class is meant
for applying style attributes, not creating a collection of
elements.

What is "missing" is a way of creating a group - "groupid"?
Then a method such as "document.getElementsByGroupId('aGroup')"
might return a collection of the group so identified.

Perhaps you might suggest it to the XHTML working group, or
discuss it here:

<URL:http://lists.w3.org/Archives/Public/www-html-editor/2005JanMar/>
<URL:http://lists.w3.org/Archives/Public/www-html/>
 
D

Derek Basch

Thanks! That answers my question. I ran into this idea a few years ago
and had given up on it.
Derek Basch wrote:
[...]
Seeing that you have to create a less that elegant RegExp to solve this
problem am I correct in assuming that the DOM is in need of something
like document.getElementsByClassName() ?

What you are trying to do is create a group of elements that you
can access with a single identifier, much how tag name can be
used to return a collection of elements with a particular tag.

Using the class attribute is a bit if a hack, as class is meant
for applying style attributes, not creating a collection of
elements.

I more or less realized that when I grouped the tags by using a class
attribute. I actually thought about writing something like:

For this example the XHTML class attribute does not refer to the class
selector functionality of CSS.
What is "missing" is a way of creating a group - "groupid"?
Then a method such as "document.getElementsByGroupId('aGroup')"
might return a collection of the group so identified.

A groupid attribute would be ideal. It would be nice to have an array
returned with one line of code like
document.getElementsByGroupId('aGroup').

Crazy second thought....

Why couldnt the class attribute be used as the selector? The id
attribute is already used as a selector in both the DOM and CSS
contexts. And isnt the idea of a class:

a collection of things sharing a common attribute; "there are two
classes of detergents"
http://www.cogsci.princeton.edu/cgi-bin/webwn?stage=1&word=class
 
R

RobG

Derek Basch wrote:
[...]
Crazy second thought....

Why couldnt the class attribute be used as the selector? The id
attribute is already used as a selector in both the DOM and CSS
contexts. And isnt the idea of a class:

a collection of things sharing a common attribute; "there are two
classes of detergents"
http://www.cogsci.princeton.edu/cgi-bin/webwn?stage=1&word=class

Not really so crazy. the HTML 4.01 spec says:

class = list
This attribute assigns a class name or set of class names to
an element. Any number of elements may be assigned the same
class name or names. Multiple class names must be separated
by white space characters.
Animatable: yes.

The class attribute assigns one or more class names to an
element. The element may be said to belong to these classes.
A class name may be shared by several element instances. The
class attribute has several roles:

* As a style sheet selector (when an author wishes to assign
style information to a set of elements).
* For general purpose processing by user agents.

Which would seem to suggest that class should be able to be used
exactly as you have described.

I understand Mike's point that it is fairly trivial to use
getElementsByTagName then process them to roll-your-own
collection (actually by building an array you get something more
versatile than a collection anyway), but it gets more complex if
you have to get different element tags so you may have to
recurse down all the children of the body tag to get what you
want.

As an interim measure, why not write your own
getElementByClassName() that does exactly that? Here's a start
below. It should be possible to declare the cArray in the
initial function, then make the recursion routine an internal
function (gets away from global array).

Also when testing the match of the class name, it needs to use a
regular expression to match the class as part of a
space-delimited string (i.e. the element may have multiple class
names).

I'll leave that up to you (or Mike, if he's still watching).

Uncomment the debug to watch the nodes being processed.

No doubt this can be optimised further, but I think you get the
idea. Properly optimised, it is perhaps 6 or 7 lines of code.

<html><head>
<title>getElementsByClassName</title>
<script type="text/javascript">

function getElementsByClassName(c) {
cArray = []; // Global scope intentional
var n = document.getElementsByTagName('body')[0];
doTree(n,c);
return cArray;
}

function doTree(n,c) {
if (n.className && n.className == c)
cArray.push(n);

// Some debug...
// var ct = (n.className)? n.className : 'no class';
// alert(n.nodeName + ' : ' + ct);

for (var i=0; i<n.childNodes.length; i++) {
doTree(n.childNodes,c);
}
}
</script>
</head>
<body>

<input type="button" value="Click me" onclick="
var robArray = getElementsByClassName('rob');
alert(robArray.join('\n'));
">

<p class="rob">rob para</p>
<p class="rob">rob para2</p>
<div class="rob">rob div</div>
<div class="rob">rob div2</div>

</body>
</html>
 
R

RobG

RobG wrote:
[...]
Also when testing the match of the class name, it needs to use a
regular expression to match the class as part of a
space-delimited string (i.e. the element may have multiple class
names). [...]

replace:

if (n.className && n.className == c)

with:

var z = new RegExp('\\b' + c + '\\b');
if (n.className && z.test(n.className))

Here's the full script - OK, it's 14 lines not 7, but there you
go. Also made doTree internal so no globals. It is a little
slow, it takes about 0.5ms per element so for large documents
with say 1,000 elements, it will be quite slow.

Better to narrow the scope by enclosing the elements you want to
group in a div, then start there rather than with the body tag.

<script type="text/javascript">

function getElementsByClassName(c) {
var cArray = [];
var n = document.getElementsByTagName('body')[0];
var z = new RegExp('\\b' + c + '\\b');

function doTree(n) {
if (n.className && z.test(n.className))
cArray.push(n);
for (var i=0, len=n.childNodes.length; i<len; i++) {
doTree(n.childNodes);
}
}
doTree(n);
return cArray;
}

</script>
 
D

Derek Basch

Thanks Rob! That answers all my questions and then some! I will let you
know how that implementation goes. I will use getElementsByClassName to
dynamically build some tables.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top