From: Richard Cornford on
On Aug 11, 4:30 am, Denis McMahon wrote:
> On 10/08/10 21:57, Evertjan. wrote:
>
>> Denis McMahon wrote on 10 aug 2010 in comp.lang.javascript:
>
>>> At the moment I use named elements, and group the elements by giving
>>> them a common name, but this feels "wrong". For example:
>
>>> function vis(name, state)
>>> {
>>> var els = document.getElementsByName(name);
>>> for (var el in els) els[el].style.visible = state;
>>> }
>
>>> Has anyone got a "better" way to do this sort of thing?
>> class
>
> Yes, nice answer, but how do you refer to a class to change it's
> style rule?
>
> If I have to trawl through
> document.styleSheets[*].cssRules|rules[*].selectorText looking for
> any / every rule that matches my class to set the style element,
> that's not really "better".

You don't necessarily have to do that. Remember that when rules have
equal specificity the last rule overrides all previous ones. So just
adding a rule for a class at the end of the last (appropriate media
(i.e. probably not 'print' style sheets, etc.)) style sheet overrides
all previous equivalent rules for that class in that and all preceding
style sheets.

Richard.
From: Gregor Kofler on
Am 2010-08-11 12:49, williamc meinte:

>> (untested)
>> function gEBCN(cN) {
>> var els = document.getElementsByTagName("*");
>> var result = [];
>> var l = els.length;
>>
>> while(l--) {
>> if(els[l].className == cN) {
>> results.push(els[l]);
>> }
>> }
>> return result;
>> }
>>
>> The main difference: the "native" gEBCN() will return a live collection,
>> the custom version a snapshot.
>>
>>> If I have to trawl through
>>> document.styleSheets[*].cssRules|rules[*].selectorText looking for any /
>>> every rule that matches my class to set the style element, that's not
>>> really "better".
>>
>> Obviously not.
>>
>> Gregor
>
> I was going to post this question myself (i.e. what's the best getEBCN
> function?)...
>
> Good point about the difference between the native implementation
> returning a (live) node list vs. just a snapshot.
>
> Any solution must work with multiple values, though, e.g. class="foo
> bar" and I don't see how the function above will do that.

As below: Either with a RegExp or you could split() the className and
use Array.indexOf() (again not supported in some browsers) on the
resulting array.

> Crockford suggested this (calling his recursive walkTheDom function)...
>
> function getElemsByClassName2(className) {
> if (document.getElementsByClassName) {
> return(elem.getElementsByClassName(className));
> } else {
> var results = [];
> walkTheDOM(document.body, function (node) {
> var a, c = node.className, i;
> if (c) {
> a = c.split(' ');
> for (i = 0; i< a.length; i += 1) {
> if (a[i] === className) {
> results.push(node);
> break;
> }
> }
> }
> });
> return results;
> }
> }

Well, one could benchmark that, but since it is a fallback, I don't care
too much about speed (though the fallback will be used by the slowest
browsers, Internet Explorer 6 to 8).

> I saw a very long "ultimate" version somewhere (Robert Nyman?). I'd be
> interested to see what people use in their own work.

element.gEBCN() (transfered into a "static" array), document.evaluate(),
filtered element.gEBTN().

Gregor

--
http://vxjs.gregorkofler.com
From: Denis McMahon on
On 11/08/10 10:54, Gregor Kofler wrote:
> Am 2010-08-11 05:30, schrieb Denis McMahon:
>> On 10/08/10 21:57, Evertjan. wrote:
>>> Denis McMahon wrote on 10 aug 2010 in comp.lang.javascript:
>>>
>>>> At the moment I use named elements, and group the elements by giving
>>>> them a common name, but this feels "wrong". For example:
>>>>
>>>> function vis(name, state)
>>>> {
>>>> var els = document.getElementsByName(name);
>>>> for (var el in els) els[el].style.visible = state;
>>>> }
>
> This rather works by coincidence, since el will become any property of
> your els collection - and quite a few of these properties won't have a
> style property. (And most DOM elements don't have a name property.)
>
> A working approach:
>
> var l = els.length;
> while(l--) {
> els[l].style.visibility = state;
> }

Good catch, I usually use:

for (var i = 0; i < els.length; i++) els[i].style.visible = state;

Don't know why I wrote it with a for / in really.

> element.getElementsByClassName(cName) in recent enough browsers will
> give you all elements with the according class. Rest of the solution
> looks exactly like the one above.
> If getElementsByClassName() is not available, a simple function will do
> the job:
>
> (untested)
> function gEBCN(cN) {
> var els = document.getElementsByTagName("*");
> var result = [];
> var l = els.length;
>
> while(l--) {
> if(els[l].className == cN) {
> results.push(els[l]);
> }
> }
> return result;
> }
>
> The main difference: the "native" gEBCN() will return a live collection,
> the custom version a snapshot.

That's really the same as my current solution, but using class as n
identifier instead of name, which is marginally better, but I was hoping
for a means of setting the class style, rather than the element style of
each element in the class.

>> If I have to trawl through
>> document.styleSheets[*].cssRules|rules[*].selectorText looking for any /
>> every rule that matches my class to set the style element, that's not
>> really "better".
>
> Obviously not.

Indeed, but (unfortunately) I'm not convinced that any solution that has
me trawling through any collection of elements is really significantly
better (more efficient, more elegant) than the current solution.

I came up with this, which will work on (a) style rule(s) based on the
class in the selector. The method of finding the style rule(s) to change
doesn't strike me as great, but I can't see an obviously better one:

function changeClassStyles(theClass, theProp, theVal)
{
if (theClass.charAt(0) != ".") theClass = "." + theClass;
var ss = document.styleSheets;
for (var ssnm = 0; ssnm < ss.length; ssnm ++)
{
if (ss[ssnm].cssRules) // non ie
{
var rls = ss[ssnm].cssRules;
}
else if (ss[ssnm].rules) // ie
{
var rls = ss[ssnm].rules;
}
if (rls)
{
for (var rlnm = 0; rlnm < rls.length; rlnm ++)
{
var rl = rls[rlnm];
var select = rl.selectorText;
/*
// for complex eg. 'ele.class:pseudo' selectors and
// simple ie. '.class' selectors
var classre = /\.[a-z0-9]/gi;
var clss = select.match(classre);
for (var clsnm = 0; clsnm < clss.length; clsnm ++)
{
if (theClass == clss[clsnm])
{
eval("rl.style." + theProp + " = theVal");
}
}
*/
///*
// for simple ie. '.class' selectors
if (theClass == select)
{
eval("rl.style." + theProp + " = theVal");
}
//*/
}
}
}
}

What I'd really like is:

document.classes[classname].style.property = value

but I can see that classes aren't a valid collection of themselves,
rather they represent a one-or-many to one-or-many link between elements
and styles.

I think any dom representation of an html class attribute or css class
selector would need a collection of rules and a collection of elements,
to maintain the mapping. A class would be added to document.classes
either (a) when a style rule selector first referenced it or (b) when an
element class attribute first referenced it, with the style rule or
element being added to it's appropriate member collection. Subsequent
style rules or elements that featured the class would also be added to
the collection.

Maybe:

var l = document.classes[classname].rules.length;
while (l--)
{
eval ("document.classes[classname].rules[l]." + theProp + " = theVal");
}

or even better, using a construct like:

document.classes[classname].rules[l].properties[theProp] = theVal;

(Yes, I know style rules don't have a properties collection either, I
just think it would be nice if they did.)

Rgds

Denis
From: Denis McMahon on
On 11/08/10 13:39, Richard Cornford wrote:

> You don't necessarily have to do that. Remember that when rules have
> equal specificity the last rule overrides all previous ones. So just
> adding a rule for a class at the end of the last (appropriate media
> (i.e. probably not 'print' style sheets, etc.)) style sheet overrides
> all previous equivalent rules for that class in that and all preceding
> style sheets.

Ooh, nice, hadn't thought of that approach.

Rgds

Denis McMahon
From: Thomas 'PointedEars' Lahn on
Denis McMahon wrote:

> function changeClassStyles(theClass, theProp, theVal)
> {
> if (theClass.charAt(0) != ".") theClass = "." + theClass;
> var ss = document.styleSheets;
> for (var ssnm = 0; ssnm < ss.length; ssnm ++)

for (var ssnm = 0, len = ss.length; ssnm < len; ++ssnm)

is more efficient. You might even want to consider

for (var ssnm = ss.length; ssnm--;)

or, as Gregor suggested,

var ssnm = ss.length;
while (ssnm--)

instead.

> {
> if (ss[ssnm].cssRules) // non ie
> {
> var rls = ss[ssnm].cssRules;
> }
> else if (ss[ssnm].rules) // ie

You should use multi-line pre-comments instead of single-line post-comments,
and the comments should contain at least "W3C DOM" and "MSHTML DOM". IE or
not IE ─ that is _not_ the question.

> {
> var rls = ss[ssnm].rules;
> }

Since the /IfStatement/ type-converts its condition to boolean, it is easier
to maintain and not more error-prone to write

var o = ss[ssnm],
rls = o.cssRules || o.rules;

instead.

> if (rls)
> {
> for (var rlnm = 0; rlnm < rls.length; rlnm ++)

See above for the optimization. And it makes no sense to me to insert
whitespace between a *unary* operator and its operand, even if the grammar
allows it.

> {
> var rl = rls[rlnm];
> var select = rl.selectorText;
> /*
> // for complex eg. 'ele.class:pseudo' selectors and
> // simple ie. '.class' selectors
> var classre = /\.[a-z0-9]/gi;

That is hardly sufficient. Check the CSS (2.1) grammar.

> var clss = select.match(classre);
> for (var clsnm = 0; clsnm < clss.length; clsnm ++)
> {
> if (theClass == clss[clsnm])
> {
> eval("rl.style." + theProp + " = theVal");

Ouch. Please read the FAQ about eval(), and from then on use only this
syntax:

rl.style[theProp] = theVal;

Note that the `style' property is not required to be implemented per W3C DOM
Level 2 Style CSS here. Use .getPropertyValue() as fallback.

> [...]
> What I'd really like is:
>
> document.classes[classname].style.property = value

No, you won't. Do not augment host objects, such as the one that `document'
refers to. (Doing otherwise is an clear indicator of the cluelessness of a
library author.)

> but I can see that classes aren't a valid collection of themselves,
> rather they represent a one-or-many to one-or-many link between elements
> and styles.

ISTM rather easy to read all stylesheet selectors into a mapping object on
load. I have started writing an object type to that effect, but the query
method (findSimpleSelector) is not ready yet:

<http://pointedears.de/websvn/filedetails.php?repname=JSX&path=%2Ftrunk%2Fcss-
commented.js>

> [...]
> eval ("document.classes[classname].rules[l]." + theProp + " = theVal");
> [...]

You are not ready for this yet.


PointedEars
--
realism: HTML 4.01 Strict
evangelism: XHTML 1.0 Strict
madness: XHTML 1.1 as application/xhtml+xml
-- Bjoern Hoehrmann
First  |  Prev  |  Next  |  Last
Pages: 1 2 3 4 5
Prev: FYI: Douglas Crockford: Loopage
Next: Getting there....