Off The Top Of My Head

Shadowed text with CSS

Posted in Uncategorized by waltermilner on November 3, 2009

The target was to make a navigation bar like that at www.apple.com, which looks like this:

apple navbar

Apple navbar

They did this by having all the links share a single background image, with the x and y offsets for each set so they show the correct part of that single image. In turn the text shadowing has been done in the graphics package that made the image.

I wanted something like this that could be done using CSS, without having to handcraft a background image for each link. That’s what we’re trying to do.

Thanks to Krijn Hoetmer I had a JavaScript function which would shadow some text. But the first problem would be that I would need to put both a JavaScript link and a style sheet link in the HTML – I wanted just one link. After some Googling and experimenting I had some JS which would dynamically load a style sheet:

var cssNode = document.createElement(‘link’);
cssNode.type = ‘text/css’;
cssNode.rel = ’stylesheet’;
cssNode.href = ‘menuBar1.css’;
cssNode.media = ’screen’;
cssNode.title = ‘dynamicLoadedSheet’;
document.getElementsByTagName(“head”)[0].appendChild(cssNode);

So menuBar1.css would style the menu bar, and this JS would load it.

Next task is to shadow the text on eack link. This works by getting the div which encloses the menu, getting each of the child nodes, and calling the shadow function for each one:

var menu=document.getElementById(“menu”);
var children=menu.childNodes;
for (i=0; i<children.length; i++)
{
applyShadow(children[i], ‘white’, 3,10, 7);
}

The applyShadow function is originally by Krijn Hoetmer is modified here slightly, to handle text which has some padding (third and fourth parameters are teh left and top padding:

function applyShadow(targetElement, shadowColor, shadowOffset,pside, ptop) {
if (typeof(targetElement) != ‘object’) {
targetElement = document.getElementById(targetElement);
}
var value = targetElement.firstChild.nodeValue;
targetElement.style.position = ‘relative’;
targetElement.style.zIndex = 1;
var newEl = document.createElement(’span’);
newEl.appendChild(document.createTextNode(value));
newEl.className = ’shadowed’;
newEl.style.color = shadowColor;
newEl.style.position = ‘absolute’;
newEl.style.left = ‘0px’;
newEl.style.top = shadowOffset + ‘px’;
newEl.style.zIndex = -1;
newEl.style.paddingLeft=pside+1+”px”;
newEl.style.paddingTop=ptop+1+”px”;
targetElement.appendChild(newEl);

}

The HTML this is applying to is:

<div id=”menu”><a href=”nowhere” >Link One</a><a href=”nowhere”>Link Two</a><a href=”nowhere”>Link Three</a><a href=”nowhere”>Link Four</a></div>

This is all done in one line – any spaces or CRs produce spurious white space text nodes in the DOM tree.

This shadows the text – but we need to insert graphics for the link separators.  This is a rather bizarre loop:

var count = children.length;
for (i=0; i<count*2; i++)
{
var image = document.createElement(‘img’);
image.setAttribute(’src’,'images/gap.gif’);
menu.insertBefore(image,children[i]);
i++;
}

we go for twice the number of child nodes that are there – because when we’ve finished, we’ll have twice that number. And we go i++ every time we add one. children is dynamically updated as we add nodes.

The style sheet is

#menu
{
position: absolute;
left: 20%;
right: 20%;
min-width: 32%;
background-image: url(images/linkbg.gif);
-moz-border-radius: 5px;
text-align: center;
height: 37px;
}
#menu a
{
position:relative;
color: black;
font-family: arial, helvetica, sans-serif;
font-size:11px;
padding-left: 10px;
padding-top: 10px;
padding-bottom:10px;
top:-15px;
}

The background image is 37 pixels high. We’ll only get round corners in FF – that’s IE’s problem. The result is:

menu bar

My version

Works in FF and IE

 

Who hates JavaScript?

Posted in Uncategorized by waltermilner on May 17, 2008

Well, I don’t hate it when things are going well. But the biggest problem is that there is no standard with a precise documentation. OK well there is, only its called ECMAScript, and no implementation of JavaScript or JScript in any browser is compliant. Which is little use.

I wanted to make a page element flicker to draw attention to it. Looked obvious – just a loop with something like

thing = document.getElementById(id);
thing.style.color=”black”;

alternated with another colour, and maybe a delay. Easy.

Problem is browsers repaint the window only when script execution ends. You can’t force a repaint in the middle of a script. So you have to Google, and this always brings a mix of complete garbage, partial garbage and little hints.

The solution is setTimeout. This seems irrelevant – setTimeout(f,n) calls function f after n milliseconds. But the point is that it looks like script termination, and is followed by a re-paint. So the two colour settings are simply a pair of mutually recursive functions which call each other with setTimeout, and needed a decremented countdown to stop it going forever. In other words:

function flicker(id)
{// this is the initial call
flickred(id, 30);
}

function flickred(id, c)
{
if (c<0) return; //if we are still going..
thing = document.getElementById(id);
thing.style.color=”red”; // change colour
// call other after 0.1 secs with decremented counter..
setTimeout(“flickwhite(‘”+id+”‘,”+(c-1)+”)”, 100);
}

function flickwhite(id, c)
{
if (c<0) return;
thing = document.getElementById(id);
thing.style.color=”white”;
setTimeout(“flickred(‘”+id+”‘,”+(c-1)+”)”, 100);
}

Only wasted about 2 hours!!