Looking for the previous guiStuff?
It's still here, the content didn't go anywhere. You may want to check out this new guiStuff though -- It's rather informative.
References/Tutorials:
Intro Documents:
guiStuff:
::Stuff for the multi-spec coder;
Coding, formats, standards, and other practical things.
Home // JavaScript // The DOM: Part 3 - Styles and Implementations
<!-- JavaScript
The DOM: Part 3 - Styles and Implementations
The last feature of the DOM discussed in Part 2 was the setting of Attributes. As mentioned, when you're setting an attribute of an element node, you can either use thesetAttribute() method, or just call the attribute as a 'property' of the element (that is: myElement.align = "right";). When you want to style an element using JavaScript as part of your DOM-centric script, you have this choice as well.The DOM defines several methods which deal with the style object of an element, including
style.setProperty(). However, these methods are not property supported in Internet Explorer, which is why it would be impractical, in this context, to cover the strict DOM methods that deal with the style object. Instead, I'll focus on how to deal with the style object as a property of element nodes, which is widely supported across browsers.The Style Object as a Property
When you access an element's style object, you may get or set CSS Style properties by their 'scripted name'. For example, if you wanted to access an element's background-color, you would usemyElement.style.backgroundColor. If the style property name is made up of two words, such as background-color, then its scripted name would remove the hyphen (-) and make the first letter of the second word Upper Case. So the property font-family would turn into fontFamily. Single-word properties remain as they are: color remains color, and padding remains padding.Remember that when you're addressing element nodes, you're addressing the elements themselves in HTML/XHTML, so you simply call the style object as if it was an object of the element node, rather than a node in and of itself. At that point, you just call any of the style object's properties:
var objP = document.createElement("p");
var objText1 = document.createTextNode("Some Text.");
objP.style.backgroundColor = '#DDD';
objP.appendChild(objText1);
document.body.appendChild(objP);
This is just like addressing an element that's already within the document, because in a sense, it is in the document, it's just not part of the Document Tree yet.
It's important to clarify that setting an element's style in this manner is the equivalent of assigning it inline styles. That is, the literal HTML would be:
<p style="background-color:#DDD;">Some Text</p>
There is nothing whatsoever preventing you from setting an element's
class or id attributes, however, if you wish to use predefined styles. You would do this using the setAttribute() method described in Part 2. If you wanted to set the class attribute, for example, to a style that has already been defined by a style sheet, you would do so like this:
var objP = document.createElement("p");
var objText1 = document.createTextNode("Some Text.");
objP.setAttribute("class", "myCSSClass");
objP.appendChild(objText1);
document.body.appendChild(objP);
One of the advantages of setting inline styles, however, is the element of encapsulation. You're not effecting the CSS namespace, you're just effecting the elements that you want. And since this is JavaScript, you can loop and condition code so that you end up with a short script that has a large impact on the document.
Suppose you wanted to add a paragraph to your page telling the user something along the lines of "Your submission is being handled." right when they clicked the Submit button on a form, just in case there's any lag in server response. You could create a function that would encase the entire process in JavaScript, to be called by an event like the
onclick on the Submit button:
function submitWasPressed(){
var objP = document.createElement("p");
var objText1 = document.createTextNode("Your submission is being handled.");
objP.style.backgroundColor = '#DDD';
objP.style.width = '270px';
objP.style.padding = '10px';
objP.style.fontFamily = 'Trebuchet MS';
objP.style.fontSize = '16px';
objP.style.borderColor = '#BBB';
objP.style.borderStyle = 'solid';
objP.style.borderWidth = '1px';
objP.appendChild(objText1);
document.body.appendChild(objP);
//someElement.parentNode.insertBefore(objP, someElement);
}
All of the styling was done on-the-fly, which means that adding this script to a new page would mean just dropping the function into the page. This is not, of course, a real-world example of a complete, well-rounded function. Such a function should contain browser support checking, inserting the node into the correct position within the document, and of course styling the element according to the page's design. The insertion position could be passed to the function as an argument, making the function portable within the site. As well, one could set the styles as 'defines' vars (variables at the begining of the script designed to be set by the user according to their needs, even if they don't know exactly how the script works), and add additional DOM functions that use the same styles for other operations that would need to keep the same theme around the page.
The point is that it is easy and effective to encapsulate DOM-centric scripts in a way that will add functionality in a portable, and adaptable fashion to a site, while remaining a 'black box' for whomever maintains the site on a regular basis -- they need not know, nor care, how they function -- they just have to know how to trigger them and they will effect the page's content, not just behaviour (and this especially applies if you're the one maintaining the site regularly, since it will save you the time and effort of re-designing the functionality over and over again).
(Technical Semantics: Actually, the concept of 'black-boxing' functionality is just a term used when dealing between two languages/formats, and its meaning is almost the same as object-oriented programming's 'isolation', where the internal mechanism is intentionally hidden from the 'implementing developer' using the class/methods, so that changes can be made while the interface to those functions remains the same.)
Search and Augment
An extremely powerful function which programming using the DOM provides is the ability to scan an entire document, look for specific patters, and replace certain simple structures with much more involved ones. Many of these functions can be achieved using server-side programming, but at the cost of, at times, document sizes several times as large as the original.The example I'll give here is sometimes referred to as 'super-styling'. This is when the effect you'd like to achieve with a certain element, in a general sense, isn't possible using CSS alone, without significant changes to the markup (and thus structure) as well.
I'll take the example of images. Consider the following markup:
<img src="myImage.jpg" title="frame_me" alt="This is some ALT text." />
You can see one oddity right away -- one normally doesn't assign a
title attribute to an image. This is exactly why it was chosen: a browser would normally just ignore this attribute value, but it's still valid HTML/XHTML. If one were to write a script that would grab just images with a title attribute set to a specific string, it would be like they were assigned a class of sorts.I'd like to, at this point, show off the end result before going into the code, so you can see the point of the whole process. I've used the above markup to place three images on the page. They each have their
title attribute set to 'frame_me', and they each have their own alt text.After the three images there's a text link that says, of all things, 'Zap Em!'. Click the link.
There's no 'reverse' script, so if you want to see that again, reload the page (or just read through the rest of the article and you'll be able to do this and a lot more yourself!).
Here's the function that was called by the link:
function FrameImages(){
var images = document.getElementsByTagName("img");
for(var i=0; i<images.length; i++){
if(images[i].title == "frame_me"){
var imageAlt = images[i].alt;
var objP = document.createElement("p");
var objBR = document.createElement("br");
var objImg = document.createElement("img");
var objText1 = document.createTextNode(imageAlt);
objImg.src = images[i].src;
objImg.style.borderColor = '#BBB';
objImg.style.borderStyle = 'solid';
objImg.style.borderWidth = '2px';
objImg.style.marginBottom = '5px';
objP.style.backgroundColor = '#DDD';
objP.style.width = '350px';
objP.style.padding = '10px';
objP.style.fontFamily = 'Trebuchet MS';
objP.style.fontSize = '16px';
objP.style.borderColor = '#CCC';
objP.style.borderStyle = 'solid';
objP.style.borderWidth = '2px';
objP.style.textAlign = 'center';
objP.appendChild(objImg);
objP.appendChild(objBR);
objP.appendChild(objText1);
images[i].parentNode.replaceChild(objP, images[i]);
}
}
}
Before I get into the code, consider that there could have been 50 images around the page. All that was needed was for the
IMG elements to have their title attribute set to 'frame_me'.So what happened here? Well, the first thing that the
FrameImages function does is look for all the IMG elements around the page and collect them into a NodeList Array using getElementsByTagName(). It then starts a loop that goes through all of them. The first thing that occurs within the loop is a condition: does the image have its title attribute set to 'frame_me'? If it doesn't, then this isn't meant for that image, and it'll move one. This is why this is safe to run this script on just about any page -- there's an infinitesimal chance that someone else decided to start setting title attributes to images, and to set them to 'frame_me', at that. If you really want to, you can replace 'frame_me' with 'frame_me_code4096' (or, to save space, something like 'fr567c123'), so that infinitesimal becomes infinitesimal to the power of -9...What I'm trying to say is that these type of tree-scanners are easy to design so that they only effect exactly what you wan them to effect, and nothing else.
If the image indeed does have its
title attribute set to 'frame_me', then it's time to go to work. First, the function creates a variable by the name of imageAlt and saves the current image's ALT attribute's value in it. Then, it creates three element nodes: objP, objBR, and objImg. It also creates a text node: objText1, and immediately assigns the contents of imageAlt (which is a String) to it.Now that the nodes are prepared, it's time to style them. First though, the
IMG element that was created, objImg, is assigned a src attribute value: the src of the original image. From this point onward, everything up to the appendChild() calls are style assignments. The image gets a border and a margin, while the P element gets a slew of CSS properties assigned to it, all in the element.style.cssProperty form.Once that process is done, the image, the
BR element, and the text node are appended to the P element in turn. Finally, the original image itself is replaced using the replaceChild() method with the newly created "Framed Image".While this was possible to achieve using only CSS, you would have had to modify the markup around every image within the page. If you wanted to change the structure at a later date, it would have meant changing the structure around, again, every image within the page. This function could be applied site-wide, and then changed to form a completely new structure by modifying one spot. Moreover, you could easily use this function as part of a 'skinning' scheme around your site by adding conditions within it that tell it which style and structure to apply according to a user's preferences.
Oh, and did I mention that there's nothing preventing you from styling the image using CSS in the first place? You could assign either inline CSS styles or apply a CSS class to these images. Maybe you'd only like to trigger this function in certain situations, and have the image styled using only CSS the rest of the time.
Notes About the Code
There are a few things to pay attention to when you're coding DOM scripts similar to the one above. First, note that I used property-style addressing, such aselement.title and element.alt. This is to please Internet Explorer. IE doesn't like the DOM's proper setAttribute() and getAttribute() methods, which is a shame, because it's what XML requires you to use. However, in browser environments, this doesn't matter, so just use element.attribute to keep IE happy.Second, you may be wondering what would have been wrong with simply using the original image as part of the new structure, or just cloning it? That's because even a clone would have the
title attribute set to 'frame_me', which means it would make the entire loop recursive. You could, of course, clone the image and then change the title attribute, but if it's just a single element with no children, then it's just not worth the hassle. If, on the other hand, you're dealing with a structure or branch (an element with descendants), then it's well worth cloning it and then either changing, or removing the title attribute, so that you don't have to 'manually reconstruct' an entire sub-tree. Mind you that changing the title attribute's value is easier than removing it, since removing an attribute in IE (which, as you'd expect, doesn't support the removeAttribure() method) requires some hacking, which just isn't worth the effort.Lastly, this function, just like the ones that came before it, don't check for compatibility. It will work in most modern browsers, but the term 'modern browser' doesn't cover everything. Make sure that your functions check that what they're about to perform is something that the browser the user is using is capable of. I'm not going to get into browser sniffing tactics here, since that's a very broad subject in and of itself, but basically you're supposed to check that unless you know that a certain browser, of a certain version or above, supports what you're about to do, don't initialize the function. This is what fallback design is for, and you have to plan for some scenario like this, even if it is telling the user that the browser version they are currently using does not support this functionality.
I've Said it Before, and I'll Say it Again...
There are endless possibilities, and this is literally the tip of the iceberg. Using the DOM you can design pages that take a fraction of a second to load and act as fully-functional applications. I can't stress enough how much experimentation is important: try the weirdest things in every possible browser just to see if you can get away with it. If you get the slightest inkling that a site is using DOM scripting then burrow through their code -- you might find something you haven't thought of, or it may spark an idea to do somthing cool that's completely different -- you never know!Return to the JavaScript section, or go the to Main page.