Using DOM Scripting to Plug the Holes in CSS
A talk I gave at @media 2006 in London.
Jeremy Keith: Can everyone hear me ok? Is this on? Is everyone reading me correctly? Okay, then I think, I think we’ll dive right in.
Good morning. Thank you all very much for coming to the techy one instead of the designery one. A bit of a tough decision. I’m kind of hoping that all of the techy people will go to the design one and all of the design people will go to the techy one. So we’ll see who goes that way. It’s not too late to change your mind if you want to run out, go for it.
One of the other things that was happening this last year, was I had this book published. So, as you can see, I have a couple of copies here, which I am going to be giving away to lucky members of the audience. At the end of my talk, we’ll have some Q and A, some fights, some arguments, and discussion hopefully. And if anyone asks good questions or makes good points, I’ll give them a copy of the book. And also I’ll do a couple of spot prizes as well throughout. So actually, I’m going to start with the spot prize straight away. Spot prize will go to the first person who can tell me who this is. If you can name the whole band, that’d be great, but I’m actually looking specifically for the singer.
No, not me. Not me. My future self, but not me. Not Frankie Valley, no.
Who? Nope. Well, the title at the top is a clue. That’s a song, this guy had a hit with it way back in the day.
Yes. Well done, sir. Come up and claim your prize. Buck Owens. This is Buck Owens and the Buckaroos is the name of the group. Now, this isn’t going to be country music 101. There is a reason why I have Buck Owens up here.
So in theory we have different technology to do different things. So in theory we have HTML for our structure. Start with your content, you mark it up, it’s for structure. It’s for semantics. And then you have CSS and that’s for your presentation. It’s all about how things would look. And then DOM Scripting is all about behavior. It’s all about how things should behave when the user interacts with the page. That’s the theory. Now in practice, sometimes it doesn’t quite work out that way. You start using CSS for behavior, you know? Hover? That’s behavior. And sometimes you need to use DOM Scripting for presentation. So it’s kind of doing a little Buck Owens. You’re going to take the regular guitar and use it like a bass.
So that’s what we’re going to do today. We’re going to take DOM Scripting and we’re going to kind of fill in some of the holes of the presentation layer.
Now they’re not too dissimilar, CSS and DOM Scripting. In fact there’s a lot of equivalence. If you think about it, both of them have to do with targeting elements in the page and doing something with the elements. It’s just the syntax that is slightly different. Since CSS we use selectors. We’ve got the element selector and all that. And with the DOM we use methods. It’s just another word for functions, if you can attach it to an object. As an example, we have the ID selector in CSS. So hash ID, then curly braces. You put your style declarations in there. Well that’s directly equivalent to the this DOM method, getElementByID. It’s a bit more verbose, it takes longer to type it out. But it’s a lot more readable, a lot more like the English. So an example would be: I want to get an element with the ID “footer” and do something with it. In CSS, it would be hash footer, I’d open my curly braces and I’d put in my style declarations. In the DOM, it’s document.getElementByID[“footer”]. So they’re both doing kind of the same thing. They’re getting an element based on an ID. An ID selector.
You’ve got the element selector in CSS, just the name of the tag, open your curly braces… when you’re in CSS. Well in the DOM, you’ve got getElementsByTagName and you pass it the name of the tag. So again, direct equivalence. So in CSS you might have p, this would get all your paragraphs in your document. To get all your paragraphs in your document using CSS, p curly braces. To do it using the DOM, document.getElementsByTagName[“p”].
We can combine things. It’s running off the screen, let me bump down the font size. So in CSS you can say, get me all the a elements in the element with the ID “nav”. So you’d go ID selector “nav” follow with element selector a. So you chained two of them together to be more specific. And you can do that with DOM Scripting as well. document.getElementsByID[“nav”].getElementsByTagname[“a”] does exactly the same thing. Gets all of the a elements within the element with the ID nav. So there’s a lot of equivalence.
What else? There’s the universal selector. In CSS we use the star, the one eyed jacks of CSS. And in the DOM it’s the same. You still use getElementsByTagName, just pass it the asterisk and you get all the elements in the whole document. Which is exactly what the element selector does there, the universal selector.
And what else is there? There’s the class selector in CSS. Dot classname. You’ve got all the elements that have that class name. Then in the DOM you don’t have documents.getElementsByClassname. We don’t have it. Which is a real shame. Because it’d be really, really useful. But when the DOM does not provide, we can make up for that ourselves. We can write our own. So, actually most people do write their own. It’s like building the better mouse trap with DOM Scripting. Everyone has written a getElementsByClassname function at some stage.
So here’s my attempt at building a better mouse trap. I’m going to write a function called getElementsByClassname. Actually I’m going to make it a method of the document. So I’m going to apply it just like any other DOM method. So let’s see. document.getElementsByClassname is the same as this function. And it’s the function that takes the single argument — the name — and that’ll be a class name. And pretty soon I’m creating an array, I’m running through all the elements for the document. I’m doing the getElementsByTagName[“*”]; get everything in the document. Then I check to see if it matches that name. By using a really crappy search there. The indexOf is probably not the best way of doing this. If anybody is better with regular expressions, you probably can come up with a much, much better way of doing this than me. I’m useless with regular expressions so I didn’t even go there. If you want to get the documents by class name, don’t use this. Just Google other people’s. People have written much better versions than me. Jonathon Snook has a nice one out there. There’s some other good ones if you just Google it.
This is an example of the kind of thing you can do. Basically I’m creating a new DOM method because it wasn’t provided straight out of the box. And that’s something you can do a lot. You can combine the existing building blocks to do your own thing. And that’s really useful.
So now I’ve got what I need. I’ve got this equivalent to the CSS class selector. So I’ve got my toolbox all ready to go. I’ve got getElementByID, I’ve got getElementsByTagname, and now I’ve got getElementsByClassName. So now I’ve got my arsenal all set. These are the weapons we’re going to use to plug the holes of CSS using DOM Scripting.
So, lets go. Let’s take a look at the problems we might encounter. Whoops, excuse me. So, here’s something that theoretically possible in CSS. But we’re not there yet. Striping tables.
I must thank Eric for setting me up perfectly with his key note. He segued very nicely into this I thought. One of the things we can’t yet do in CSS is to get the zebra table effect. Where every second row is a different color. We will be able to do it in CSS3. There’s the nth child pseudo class… property… I don’t know. But it’s getting pretty complex now, CSS3 at this stage. You’ve got this 2n+1. These are all basically the same way of saying the same thing. I’m not going to get into the syntax of CSS3. But essentially what you can do, you can select elements based on their order. Whether it’s odd, even, the third element, the fifth element, whatever. And that’s going to be really, really useful. One of the reasons it’s useful is to be able to say things like this. Get the basically every second table row and apply a class to it. For instance, apply this background color to every second row of the table. That’s going to be awesome. That’s going to be great. What we’re going to be able to do with that, is we’re going to be able to get these zebra tables, these striped tables.
So let’s say you have a table like this. Here’s a table of the schedule for this morning. It’s a fine table, it’s got the headers, it’s got the table rows and all that. It’s readable enough but the idea with striped tables, you make it more readable, you make it a bit more clear by having this color distinction between the rows. And that’s what we’re trying to get to. We can’t do that yet with CSS alone. We’re not quite there. We will be able to do it with CSS once CSS3 is out. So another 50 years and we’ll be able to do this no problem. But for now, we’re going to have to rely on DOM Scripting.
So I’m amending my plan slightly. Instead of setting the style directly, I’m going to set a class on the row. So to do that, what I need to do is to add a class. Because I just don’t want to give it a class. Because there might be classes that are already on the row. In fact, in my example table, there is already a class on every row. There’s a class vevent because it’s all marked up with microformats as an hCalendar. Which unfortunately the official schedule on the @media website isn’t, and that’s a shame. Maybe Tantek will give Patrick a scolding for that later on. So I want to add a class — not replace a class, add a class. So I’ve written a little function to do that. It takes two arguments. What’s the element I want to give a class to and what’s the name of the class? I just check, does it have a class name yet, then if it doesn’t, then great, then we just give it a class. If it does, then I just add the space separator and I join on the new class name.
Let’s move on to another issue, problem, hole that we need to fix. Multiple background images, because we’re not there yet. Though Safari does support this by the way. How many people know that Safari does support? Safari does support multiple background images. In CSS3, this will be possible. You’ll be able to give more than one background image to the same element. And more than one background repeat position. And of course, there’s a reason why you want to do this. Because let’s say you’re building the coolest Web 2.0 Ajax app. And as well as having gradients, you gots to have rounded corners.
Now rounded corners are referred to as the Kobayashi Maru scenario. And the book for anybody that can explain the Kobayashi Maru scenario. I think his hand was up first.
[crowd inaudibly answers]
I think I want the exact film.
[crowd in unison answers]
You got it, Star Trek 2: The Wrath of Khan. The Kobayashi Maru scenario is essentially an unwinnable situation. Well, Captain Kirk won it but he cheated. He cheated. Come on, Star Trek 2: The Wrath of Khan? Who doesn’t remember this?
So given that, let’s take a look how we might use DOM Scripting to do this. Here’s what we want to happen. So this is an element before. So in this case, it’s a blockquote element. And I’m not going to give any prizes for naming the poem but if anybody can recite a few lines?
A few lines after this?
[crowd speaks, inaudibly]
Let’s move on to something else. Generated content. Mmm. Now this is actually CSS2. And this is supported in a couple of browsers today. I can’t keep track of which ones. Generated content with CSS? I don’t know. That sounds like behavior to me. I’m not sure this is the right place to have content generated. I don’t know if CSS is the right tool for this. But anyway, for example you can do stuff like this. You can use the :after pseudo class thingymajiggy to add content using this content technique I’ve already showed you. So, as an example, a blockquote, see another blockquote. So here’s a blockquote and it’s also a very subtle plug for d.Construct, which is happening again this year by the way. Keep the first week of September free in your calendar and come on down to Brighton because we’re going to have an awesome time. But this is marked up in a blockquote. Now blockquotes are wonderful, wonderful elements for marking up semantic meaning. Not for layout. Don’t use it to indent content, that’s evil. No, it’s a wonderful way of marking up some piece of text you’re quoting. And then there’s the cite attribute. Now, how many people are familiar with the cite attribute of blockquotes? See? You’re a savvy audience. There’s a lot of people who aren’t familiar with this. It’s totally understandable why somebody wouldn’t know about the cite attribute. The reason why it’s understandable is because the default browser behavior. Because the default browser behavior is to do bugger all. It completely ignores the cite attribute. It’s like hidden meta data now. The user can’t actually do much with it. I think in Firefox you can right click and go to it or something. But there aren’t many browsers doing anything with it by default, with the wonderful cite attribute. So what I’d really like to do is to show this is where it came from, this is my source. That information is already there in the cite attribute but I want to make it visible. Because nobody likes invisible meta data. Right? So, if I had CSS2, if I had full CSS2 support, I could do that very, very easily using CSS. As a matter of fact, I can do that in Safari. Safari does have this generated content stuff.
So that’s cool, but most browsers don’t or a lot of browsers don’t. So again, we’re going to use DOM Scripting to do this. We’re going to create markup or going to create something and dump it into the page. So here’s my plan, and again we’re going to write it out in English first. Get all the blockquotes in the document, because I want it to apply to all blockquotes and not just blockquotes of a certain class. Loop through each blockquote, get the cite attribute. Now if the cite attribute exists, then in that case, I want to create a string of text that say that word source, followed by the source, the actual value from the cite attribute. So that’s the plan. That’s the plan in English. Here it is in DOM Scripting terms. I’ll need getElementsByTagName because I’ll need to loop through all the blockquotes. I’ll need to use getAttribute because I’m looking for an attribute on an element, in this case the site attribute. And this time I want to create a text node. I don’t want to create an element, I actually want to create a string of text. So there’s a method called createTextNode which is similar to createElement but instead of creating a new element, you create a new text node, like it sounds. And then I’m going to put it back into the blockquote so I’m going to use appendChild again. So here’s how it might look. I looped through all the blockquotes, get elements by tag name, loop through each one I get the cite attribute. If that exists, if that works, then I’m going to create a text node with the value of the string source plus whatever the cite attribute is and I’m going to put that at the end of my blockquote. And the result is this. I get to see the source of the quote. Which is good. I’d actually go a little bit further than that. I’d get a little fancier and I would actually make it a clickable hyperlink because it’s not much good having it as a string of text. And I’d probably apply a class so I can style that differently and float it over to the right like that. So now I can actually go and visit that link. So the expanded version is a little longer but it’s pretty much the same idea. You know, loop through all the blockquotes, get the cite attribute, if the cite attribute actually exists then, but now I just do a few more things. I create a text note but I also create an “A” element; I create a paragraph to put it in, because blockquotes do actually need to have block level elements in there for the doctype I’m using. I set the href value, I’m appending all that. I’m setting the class name there of “attribution”; that’s in my style sheet; I can do that floating to the right. So it’s getting a bit more complex but the basic plan is still pretty much the same and at the end I just dump it into the blockquote so we get that nice clickable link to the source. So that’s good; that’s something we’ve managed to do. What else can we do?
Audience: Excuse me?
Jeremy Keith: Oh What?
Audience: I don’t want to seem a bit smart ass but you don’t want to use a cite tag for that?
Jeremy Keith: Cite tag? Oh you’re right. That would actually be the semantically correct thing to do, wouldn’t it? I guess? Hmm if only we had a mark up expert in the room. I could. I’m using a “P” element but we could argue about angels on the head of pins and stuff. That’s worth… This is the smart arse award for… Sorry I do actually have a choice of books. If nobody wants my book it’s totally understandable. Hey you might have it already. You can request to have one of Stuart Language’s DHTML Utopia books. Forget the title, it’s not about DHTML. It’s all about scripting stuff. It’s cool.
Okay, so moving on from that one. Max width. Oh Max… I could go on on a rant here now because I love liquid layouts. I love love love liquid layouts. I think they’re more webby than fixed width. Liquid layouts are the way to go. But most people don’t implement liquid layouts. And one of the reasons is because “At really big sizes it just gets unreadably long… blah blah blah.” Well max width would solve that, the CSS declaration max width, and say once it gets to a certain width then we can, for instance, set a size. So on the body type, it could say it’s never going to get bigger than 1600 pixels, for instance. This is supported in most browsers today but not the one that everybody’s using so that’s a bit annoying. But it will be supported in IE7, correct? Yes, which is like… If I had one request for IE7 it was going to be max width so my prayers are answered. There’ll be no more excuses for the fixed width people in a couple of years.
So this is the idea that you have a maximum width that your body, for instance, or some element can reach before it just stops expanding. And the plan to implement that in DOM Scripting would be really straightforward. Find the current width of the browser. If it’s greater than 1600 pixels then set that style declaration on the body, set that width, so using DOM Scripting to plug the hole in CSS.
Alternately you’ve got min width. Same thing, also CSS2 spec, also not supported in the browser everybody’s using. And similar syntax there, and the plan would be much the same. Get the current width of the browser and if it’s less than 800 pixels then set a style of 800 pixels on the body. Great. So we can have a bit more control over how big things get. Which is perfect. ‘Cause the designers don’t like the fluid layouts ‘cause there’s just not enough control over how big things get. Well this will allow, you know, it will allow enough control on the part of the designer to limit how big or how small things can get but still allow the user to determine how they want to see things, which, in my opinion, always trumps the will of the designer, but that’s just me.
So this case, you have to use the Browser Object Model, which I’m not that familiar with but I don’t have to be because The Man In Blue, Cameron Adams, has already done… Is he here, or has he gone next door to the designers? I hope he’s gone next door. Yeah, he’s gone next door. Well you can buy him a drink tonight cause he’s formed this really nice technique called resolution dependent layout. I don’t think I’ve got a net connection so I can’t show you some of the examples, but do check out website for examples from Rammstein, Industrial German Rockers who use this. If the browser width is a certain width you get one layout, if you shrink the browser you get a different layout. So it’s kind of a nice compromise. It’s a pretty cool solution. It’s pretty neat. I think Dave… Dave, you’ve been using this on Rosenfeld Media, I think? Yeah. So it’s, you know, even Dave Shea’s using this. It’s cool. It’s cool.
And I think I’m going to stop it there and ask you guys if you have questions, ideas, arguments, and remember there’s books to be had, there’s choices. Oh we have a microphone, by the way, so if you just stick up your hand, I’ll pass the microphone around. Okay, Eric Meyer has a question for me. This gentlemen over here. Brace myself.
Eric Meyer: So you did that thing early on with the document dot get elements by class name equals function blah blah blah?
Jeremy Keith: Yes.
Eric Meyer: I was just wondering, could you have done element dot add class equals function blah blah blah or document dot add class equals function blah blah blah?
Eric Meyer: Can you jump to slide 20?
Jeremy Keith: Slide 20? Okay. See if the technology is up to it you know. That S5 thing.
Eric Meyer: Well you hit two zero and hit return.
Jeremy Keith: Yeah, okay. Ooh, keyboard support. Yeah.
Eric Meyer: Yeah well it’s in there.
Jeremy Keith: Yeah… slide twenty, you wanted?
Eric Meyer: Well apparently I wanted to go further forward. Basically the bit where you were calling add class in the middle of the loop.
Jeremy Keith: Okay, so here’s the function itself, add class, and here’s… right.
Eric Meyer: So here, instead of calling add class, you could have done, elem dot add class blah if you had written it the other way?
Jeremy Keith: Yeah I think I would have had to rewrite this a little bit yeah but absolutely, yeah.
Jeremy Keith: You’re most welcome. Yeah, no that probably would have been a better way to do it thinking about it but, meh, this works. I’m not the world’s best coder.
Okay, I’ve got a, there’s a hand… who was first with the hands up down here? I’m not sure. Give the microphone to one of these gentlemen down here. Oh Eric, do you want the book or are you perfectly happy with… here have the book.
Audience: Hi. About Eric’s keynote presentation earlier on and Eric obviously went through the years and showed the restrictions, the things that he found frustrating with CSS. What do you find frustrating with the DOM script and what would you like to see in the future that would make life easier for you, you know, in the same way that CSS has advanced?
Jeremy Keith: Yeah, not much…
Audience: … with CSS over the years. What do you find frustrating with the DOM script at the moment and what would you like to see in the future that would make life easier for you in the same way that CSS has advanced?
Jeremy Keith: Yeah, not much to be honest because, as you see, even when stuff isn’t provided by the DOM you can just make up for it yourself. So it would be nice if there was a getElementsByClassname method.
Audience: That’s not ideal. You would obviously want some kind of invoked function there.
Audience: Thank you very much.
Jeremy Keith: This gentleman at the front had his hand up pretty quickly I think. You have to be sharp on the draw. Do you want to come up now or you can come up later, whichever you like. Ok, great.
Audience: Is this on? I think converting PowerPoint to clean and valid XHTML…
Jeremy Keith: Do you use Dave Raggett’s Tidy?
Audience: No, I wrote my own.
Jeremy Keith: Smart.
Audience: Last week I wrote a small function to automatically expand the fonts and the images so that the slide fits the browser window.
Jeremy Keith: Sort of like this one here where the font size will change?
Audience: That kind of thing. It works fine in Internet Explorer and Opera and in Mozilla but in Firefox it seems to get confused. It seems to be drawing the page too fast before it applies the job.
Jeremy Keith: So events are getting fired in the wrong order?
Audience: What am I doing wrong?
Audience: Is there something I can do?
OK, hands up. Who’s got another question? Who was first with their hands up. Maybe back there, one of those two who can fight it out.
Jeremy Keith: That’s fair enough.
Jeremy Keith: That seems to work fine and I believe that Chris Wilson will be with me on this that that’s the way to go with serving up different style sheets as well as conditional comments because they only work in IE. The thing is then you’re doing something in the markup. That’s the Kobayashi Maru, isn’t it? You’ve got to mess with something. Either you’re going to mess with the CSS or you’re going to mess with the markup and there you’ve had to add those comments to do the expression.
Audience: I created the comments to do an IE only style sheet and then put the expression in that.
Jeremy Keith: OK. So it’s kind of passing it along.
Audience: It was a reluctant decision. I was just interested in what…
Jeremy Keith: That’s what I’m saying. You don’t look for, or it can call itself anything it likes.
Jeremy Keith: Don’t talk to me about the BOM. I’m just here to talk about DOM Scripting. [laughter]
Jeremy Keith: Or you just write an angry email to Håkon and say fix this. The BOM stuff, like I say, is a bit more cabalistic, a bit more like you have to remember Firefox doesn’t do that, Opera doesn’t do that. But, yeah, that all has to do with the Browser Object Model which maybe you shouldn’t be messing with in the first place. Why mess with the user’s browser? Anyway, another question. Ann, you have a question back there? Somebody pass a microphone. Who can reach first? Right in the middle of the walkway. OK, Ann make it good.
Ann McMeekin: Hi. I am like any person who is into designing or into the technology stuff. The add ons and possibilities of the software are fantastic but the thing that’s concerning me with my other hat or my accessibility hat is where do the fallbacks come in? Is there something that, if the method for generating content, is it something that can actually be used initially to hide content from use at the moment of screen readers that don’t support, that definitely don’t support the DOM being updated? Can it hide the content from visual view but leave it there for screen readers or other access technology? Why don’t they catch up?