Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Beginning JavaScript With DOM Scripting And Ajax - From Novice To Professional (2006)

.pdf
Скачиваний:
80
Добавлен:
17.08.2013
Размер:
17.27 Mб
Скачать

126

C H A P T E R 5 P R E S E N T A T I O N A N D B E H A V I O R ( C S S A N D E V E N T H A N D L I N G )

The style collection contains all the style settings of the current element that you can modify by using the property notation of the different CSS selectors. A rule of thumb for the property notation is that you remove the dash in the CSS selector and use camelCase for

the whole selector. For example, line-height becomes lineHeight and border-right becomes borderRight. Here’s a list of all available properties (notice that float becomes cssFloat!):

background, backgroundAttachment, backgroundColor, backgroundImage, backgroundPosition, and backgroundRepeat

border, borderBottom, borderTop, borderLeft, borderRight, and for each of them style, width, and color

color, direction, display, visibility, letterSpacing, lineHeight, textAlign, textDecoration, textIndent, textTransform, wordSpacing, letterSpacing

margin, padding (each for top, left, bottom, and right)

width, height, minWidth, maxWidth, minHeight, maxHeight

captionSide, emptyCells, tableLayout, verticalAlign

top, bottom, left, right, zIndex, cssFloat, position, overflow, clip, clear

listStyle, listStyleImage, listStylePosition, listStyleType

font, fontFamily, fontSize, fontStretch, fontStyle, fontVariant, fontWeight

You can read and write all of these using getAttribute() and setAttribute(); however, if you write them, it might be quicker to simply set the style attribute to a string value using JavaScript object property syntax. For the browser, both of the following examples are the same, but the latter might be a bit quicker to render and keeps your JavaScript shorter.

var warning=document.createElement('div');

warning.style.borderColor='#c00';

warning.style.borderWidth='1px';

warning.style.borderStyle='solid';

warning.style.backgroundColor='#fcc';

warning.style.padding='5px';

warning.style.color='#c00';

warning.style.fontFamily='Arial';

// is the same as

warning.setAttribute( 'style' , 'font-family:arial;color:#c00; padding:5px;border:1px solid #c00;background:#fcc');

C H A P T E R 5 P R E S E N T A T I O N A N D B E H A V I O R ( C S S A N D E V E N T H A N D L I N G )

127

While setting style attributes directly is frowned upon in modern web design (you are effectively mixing behavior and presentation and you make maintenance a lot harder), there are situations where you will have to set style attributes directly via JavaScript—for example:

Fixing browser shortcomings when it comes to CSS support

Dynamically changing the dimensions of elements to fix layout glitches

Animating parts of the document

Creating rich user interfaces with drag-and-drop functionality

Note You will hear about the first two later on in this chapter; however, you won’t find animation or drag- and-drop examples here, as these are very advanced JavaScript topics and need a lot of explanation that falls outside of the scope of this book. You will find examples you can use out-of-the-box in Chapter 11.

For simple styling tasks, you should avoid defining the look and feel in JavaScript in order to simplify maintenance of your script. In Chapter 3, we talked about the main feature of modern web development: separation of the development layers.

If you use a lot of style definitions in JavaScript, you mix the presentation and the behavior layers. If some months down the line the look and feel of your application has to change, you— or some third-party developer—will have to revisit your script code and change all the settings in it. This is neither necessary nor advisable, as you can separate the look and feel out into the CSS document.

You can achieve this separation by dynamically changing the class attribute of elements. That way you can apply or remove style settings defined in your site’s style sheet. The CSS designer does not have to worry about your script code, and you don’t have to know about all the problems browsers have when it comes to supporting CSS. All you need to communicate is the names of the classes.

For example, to apply a class called dynamic to an element with the ID nav, you change its className attribute:

var n=document.getElementById('nav'); n.className='dynamic';

Note Logically, you should also be able to change the class via the setAttribute() method, but browser support for this is flaky (Internet Explorer does not allow class or style as the attribute), which is why for the moment it is a good plan to stick with className. The name of the property is className and not class, as class is a reserved word in JavaScript and results in an error when used as a property.

128

C H A P T E R 5 P R E S E N T A T I O N A N D B E H A V I O R ( C S S A N D E V E N T H A N D L I N G )

You can remove the class by setting its value to an empty string. Again, removeAttribute() does not work reliably across different browsers.

As you may be aware, HTML elements can have more than one CSS class assigned to them. A construct of the following kind is valid HTML—and sometimes a good idea:

<p class="intro special kids">Lorem Ipsum</p>

In JavaScript, you can achieve this by simply appending a value preceded by a space to the className value. However, there is a danger that browsers will not display your class settings correctly, especially when adding or removing results in leading or trailing spaces in the className value. The following two examples might not display correctly in some browsers (especially in MSIE 5 on Macs):

<p class="intro special kids ">Lorem Ipsum</p> <p class=" intro special kids">Lorem Ipsum</p>

You can work around this problem with a helper method. Writing this helper method to dynamically add and remove classes should be easy: you append a class value preceded by a space if the className attribute is not empty and without the space if it is empty. You

remove the class name from the original value just like you would remove a word from a string. However, as you need to cater to browser problems with orphan spaces, it gets a bit more complicated than this. The following tool method is included in DOMhelp and allows you to dynamically add and remove classes from an element. It also allows you to test whether a certain class is already added to it.

function cssjs(a,o,c1,c2){ switch (a){

case 'swap': if(!domtab.cssjs('check',o,c1)){

o.className.replace(c2,c1)

}else{

o.className.replace(c1,c2);

}

break;

case 'add': if(!domtab.cssjs('check',o,c1)){

o.className+=o.className?' '+c1:c1;

}

break;

case 'remove':

var rep=o.className.match(' '+c1)?' '+c1:c1; o.className=o.className.replace(rep,'');

break;

case 'check':

var found=false;

var temparray=o.className.split(' '); for(var i=0;i<temparray.length;i++){ if(temparray[i]==c1){found=true;}

}

C H A P T E R 5 P R E S E N T A T I O N A N D B E H A V I O R ( C S S A N D E V E N T H A N D L I N G )

129

return found; break;

}

}

Don’t worry too much about the inner workings of this method—it will get clearer to you once you master the match() and replace() methods, which will be covered in Chapter 8. For now, all you need to know is how to use it, and for that you use the method’s four parameters:

a is the action that has to be taken and has the following options:

swap replaces one class with another.

add adds a new class.

remove removes a class.

check tests whether the class is already applied or not.

o is the object you want to add classes to or remove classes from.

c1 and c2 are the class names—c2 is only needed when the action is swap.

Let’s use the method to recode the earlier example—this time hiding and showing the address by dynamically applying and removing a class.

exampleClassChange.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

<html dir="ltr" lang="en"> <head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Example: Dynamically changing classes</title>

<style type="text/css">

@import "dynamic.css";

</style>

<script type="text/javascript" src="DOMhelp.js"></script>

<script type="text/javascript" src="classChange.js"></script>

</head>

<body>

<h3>Contact Details</h3> <address>

Awesome Web Production Company<br /> Going Nowhere Lane 0<br />

Catch 22<br /> N4 2XX<br /> England<br />

</address>

</body>

</html>

130

C H A P T E R 5 P R E S E N T A T I O N A N D B E H A V I O R ( C S S A N D E V E N T H A N D L I N G )

The style sheet contains, among others, a class called hide that will hide any element it gets applied to. In this example, you use the off-left technique (http://css-discuss.incutio.com/ ?page=screenReaderVisibility) for that, which is possibly the most accessible way of hiding content. The problem with altering the visibility or display properties to hide elements is that screen readers helping blind users may not make content available to them, although it is visible in a browser.

classChange.css (excerpt)

.hide{

position:absolute;

top:0; left:-9999px; height:0; overflow:hidden;

}

You specify the name of the class as a parameter at the start of the script, which means that if someone needs to change the name at a later stage, he won’t have to check the whole script.

If you develop a really complex site with lots of different classes to be added and removed, you could move them out into their own JavaScript include with their own object. For this example, such a move would be overkill—but we will come back to this option later.

Note Notice that DOMhelp already includes the cssjs() method, therefore you don’t need to include it in this example.

classChange.js

sc={

// CSS classes

hidingClass:'hide', // Hide elements

init:function(){

sc.head=document.getElementsByTagName('h3')[0];

if(!sc.head){return;}

sc.ad=DOMhelp.closestSibling(sc.head,1);

DOMhelp.cssjs('add',sc.ad,sc.hidingClass);

var t=DOMhelp.getText(sc.head);

var collapseLink=DOMhelp.createLink('#',t); sc.head.replaceChild(collapseLink,sc.head.firstChild);

C H A P T E R 5 P R E S E N T A T I O N A N D B E H A V I O R ( C S S A N D E V E N T H A N D L I N G )

131

DOMhelp.addEvent(collapseLink,'click',sc.peekaboo,false) collapseLink.onclick=function(){return;} // Safari fix

},

peekaboo:function(e){

if(DOMhelp.cssjs('check',sc.ad,sc.hidingClass)){

DOMhelp.cssjs('remove',sc.ad,sc.hidingClass)

}else { DOMhelp.cssjs('add',sc.ad,sc.hidingClass)

}

DOMhelp.cancelClick(e);

}

}

DOMhelp.addEvent(window,'load',sc.init,false);

Helping the CSS Designer

DOM scripting and separating the CSS out into classes that get applied and removed dynamically allows you to make the life of web designers a lot easier. Using DOM and JavaScript allows you to reach much further into the document than CSS selectors allow you to. One common request is a way to reach a parent element in CSS for hover effects, for example. In CSS this is impossible; in JavaScript it is pretty easy to achieve via parentNode. With JavaScript and DOM, you can provide the designer with dynamic hooks for her style sheet by altering the HTML content to apply classes and IDs, generate content, or even add and remove whole style sheets by adding or removing STYLE and LINK elements.

Styling Dynamic Pages Made Easy

It is very important to make it as easy as possible for the designer to create different styles for the scripting enhanced version of a site and the nonscripting version. The nonscripting version can be much simpler, and the styles needed for it tend to be fewer (for example, in the HTML address example you only need link styles defined for inside the H3 when JavaScript is enabled, as the link is generated via JavaScript). A really easy option to give the designer a unique identifier when scripting is enabled is to apply a class to the body or to the main element of the layout.

dynamicStyling.js—used in exampleDynamicStyling.html

sc={

// CSS classes

hidingClass:'hide', // Hide elements

DOMClass:'dynamic', // Indicate DOM support

init:function(){

132

C H A P T E R 5 P R E S E N T A T I O N A N D B E H A V I O R ( C S S A N D E V E N T H A N D L I N G )

// Check for DOM and apply a class to the body if it is supported if(!document.getElementById || !document.createElement){return;} DOMhelp.cssjs('add',document.body,sc.DOMClass);

sc.head=document.getElementsByTagName('h3')[0];

if(!sc.head){return;}

sc.ad=DOMhelp.closestSibling(sc.head,1);

DOMhelp.cssjs('add',sc.ad,sc.hidingClass); var t=DOMhelp.getText(sc.head);

var collapseLink=DOMhelp.createLink('#',t); sc.head.replaceChild(collapseLink,sc.head.firstChild); DOMhelp.addEvent(collapseLink,'click',sc.peekaboo,false) collapseLink.onclick=function(){return;} // Safari fix

},

peekaboo:function(e){

if(DOMhelp.cssjs('check',sc.ad,sc.hidingClass)){

DOMhelp.cssjs('remove',sc.ad,sc.hidingClass)

}else { DOMhelp.cssjs('add',sc.ad,sc.hidingClass)

}

DOMhelp.cancelClick(e);

}

}

DOMhelp.addEvent(window,'load',sc.init,false);

That way the CSS designer can define in the style sheet which settings to apply when JavaScript is disabled and overwrite them with others when JavaScript is enabled by using the body with the class name in a descendant selector:

dynamicStyling.css

*{

margin:0;

padding:0;

}

body{ font-family:Arial,Sans-Serif; font-size:small;

padding:2em;

}

C H A P T E R 5 P R E S E N T A T I O N A N D B E H A V I O R ( C S S A N D E V E N T H A N D L I N G )

133

/* JS disabled */ address{

background:#ddd; border:1px solid #999; border-top:none; font-style:normal; padding:.5em; width:15em;

}

h3{

border:1px solid #000; color:#fff; background:#369; padding:.2em .5em; width:15em; font-size:1em;

}

/* JS enabled */ body.dynamic address{

background:#fff;

border:none; font-style:normal; padding:.5em; border-top:1px solid #ccc;

}

body.dynamic h3{ padding-bottom:.5em; background:#fff; border:none;

}

body.dynamic h3 a{ color:#369;

}

/* dynamic classes */

.hide{

position:absolute;

top:0; left:-9999px; height:0;

}

134

C H A P T E R 5 P R E S E N T A T I O N A N D B E H A V I O R ( C S S A N D E V E N T H A N D L I N G )

The address example can now—depending on JavaScript and DOM being available or not— have two completely different looks (one of these having two states) as shown in Figure 5-2.

Figure 5-2. The three states of the address (nondynamic version, collapsed, and expanded)

This works nicely if your site is not overly complex and does not have many dynamic elements. For more complex sites, you could use a different style sheet for the non-JavaScript and the JavaScript versions and add the latter via JavaScript. This also has an added benefit: the low-level user does not have to load a style sheet that isn’t of any use to him. You can add dynamic style sheets by creating a new LINK element in the head of the document. In this example, you start by including a low-level style sheet.

exampleStyleSheetChange.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

<html dir="ltr" lang="en"> <head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Example: Dynamically applying new Style Sheets </title> <style type="text/css">

@import "lowlevel.css";

</style>

<script type="text/javascript" src="DOMhelp.js"></script>

<script type="text/javascript" src="styleSheetChange.js"></script>

</head>

C H A P T E R 5 P R E S E N T A T I O N A N D B E H A V I O R ( C S S A N D E V E N T H A N D L I N G )

135

<body>

<h3>Contact Details</h3> <address>

Awesome Web Production Company<br /> Going Nowhere Lane 0<br />

Catch 22<br /> N4 2XX<br /> England<br />

</adress>

</body>

</html>

The script checks for DOM support and adds a new link element pointing to the high-level style sheet:

styleSheetChange.js

sc={

// CSS classes

hidingClass:'hide', // Hide elements highLevelStyleSheet:'highlevel.css', // Style sheet for dynamic site

init:function(){

// Check for DOM and apply a class to the body if it is supported if(!document.getElementById || !document.createElement){return;}

var newStyle=document.createElement('link'); newStyle.setAttribute('type','text/css'); newStyle.setAttribute('rel','StyleSheet'); newStyle.setAttribute('href',sc.highLevelStyleSheet); document.getElementsByTagName('head')[0].appendChild(newStyle);

sc.head=document.getElementsByTagName('h3')[0];

if(!sc.head){return;}

sc.ad=DOMhelp.closestSibling(sc.head,1);

DOMhelp.cssjs('add',sc.ad,sc.hidingClass); var t=DOMhelp.getText(sc.head);

var collapseLink=DOMhelp.createLink('#',t); sc.head.replaceChild(collapseLink,sc.head.firstChild); DOMhelp.addEvent(collapseLink,'click',sc.peekaboo,false) collapseLink.onclick=function(){return;} // Safari fix

},