16 replies [Last post]
Tyssen
Tyssen's picture
Offline
Moderator
Brisbane
Last seen: 7 years 7 weeks ago
Brisbane
Timezone: GMT+10
Joined: 2004-05-01
Posts: 8201
Points: 1386

Link

The problem is to do with the main navigation tabs on this page; they change colour on :hover and I'd like the same to happen on :focus when you tab onto them (and also in IE when you go back to the page you're on).
The problem is that the tabs are made up of two images, one attached to the anchor and one to the list item. It's the one on the list item which doesn't change on :focus or :active.
Is this correct behaviour, will I have to ditch the colour change, or is there a way around it?

How to get help
Post a link. If you can't post a link, jsFiddle it.
My blog | My older articles | CSS Reference

Tags:
Chris..S
Chris..S's picture
Offline
Moderator
Last seen: 9 years 10 weeks ago
Timezone: GMT+1
Joined: 2005-02-22
Posts: 6078
Points: 173

:focus and :active work

:focus and :active work differently from :hover. Not all elements can receive :focus or be :active and browsers don't propagate these two pseudo elements further up the DOM tree.

The js events onfocus, onblur and onactive do propagate up the DOM tree.

You can get the effect you are after in two ways:
- with the same html structure and by triggering the focus and active changes with javascript
- nesting a second element within the <A> and using it plus the <A> element for your background. Then you'll be able to use descendent selectors with a:focus to alter the background.

Tyssen
Tyssen's picture
Offline
Moderator
Brisbane
Last seen: 7 years 7 weeks ago
Brisbane
Timezone: GMT+10
Joined: 2004-05-01
Posts: 8201
Points: 1386

Thanks Chris, I went for

Thanks Chris, I went for another span inside the anchor as it seemed to be neater and involve less markup than this:

<script type="text/javascript">
function changeBG(id){
  if(document.getElementById)
  document.getElementById(id).style.backgroundPosition = '0 -84px'  ;
}
function blurBG(id){
  if(document.getElementById)
  document.getElementById(id).style.backgroundPosition = '0 0'  ;
}
</script>

<li id="nav-books" onfocus="changeBG('nav-books')" onblur="blurBG('nav-books')"><a href="/reading/" rel="nofollow"><img src="/images/nav-book.gif" width="36" height="36" alt="" />Books</a></li>

Or is there a better way to write the javascript? (Although I guess the extra span method is probably still better as it won't fail if js is turned off.)

How to get help
Post a link. If you can't post a link, jsFiddle it.
My blog | My older articles | CSS Reference

Chris..S
Chris..S's picture
Offline
Moderator
Last seen: 9 years 10 weeks ago
Timezone: GMT+1
Joined: 2005-02-22
Posts: 6078
Points: 173

CSS only would be better

Unobtrusive javascript. You already have the key components for it in your "thin.js". You attach event handlers to the elements that need them via an onload event handler. The event handler functions can then be defined in the external javascript file and the attachment can happen there too.

Tyssen
Tyssen's picture
Offline
Moderator
Brisbane
Last seen: 7 years 7 weeks ago
Brisbane
Timezone: GMT+10
Joined: 2004-05-01
Posts: 8201
Points: 1386

In the interests of learning

In the interests of learning something more about js, I've been messing about a bit more with the js option and come up with this:

function linkBG() {
	var lists = document.getElementById("tab-nav1").getElementsByTagName("li");
	for (var i=0; i<lists.length; i++) {
        var list = lists[i];
           list.onfocus = function () {this.style.backgroundPosition = '0 -84px';}
           list.onblur = function () {this.style.backgroundPosition = '0 0';}
    }
	var anchors = document.getElementById("tab-nav1").getElementsByTagName("a");
	for (var i=0; i<anchors.length; i++) {
       	var anchor = anchors[i];
           anchor.onfocus = function () {
			this.style.backgroundPosition = '100% -84px';
			this.style.color = '#FF0';
		}
           anchor.onblur = function () {
			this.style.backgroundPosition = '100% 0';
			this.style.color = '#006CB1';
		}
	}
}
window.onload=linkBG;

This works OK in FF, but in IE, the list items don't change. I'd also like to be able to exclude list items which have an ID of #active from the change but don't know how to implement that.

How to get help
Post a link. If you can't post a link, jsFiddle it.
My blog | My older articles | CSS Reference

Tyssen
Tyssen's picture
Offline
Moderator
Brisbane
Last seen: 7 years 7 weeks ago
Brisbane
Timezone: GMT+10
Joined: 2004-05-01
Posts: 8201
Points: 1386

Another question about the

Another question about the same page but a different (although related) aspect: On the index page (and elsewhere too), the links in the centre coloured boxes shift their containing boxes in IE6 when you tab onto them or when you click them.
The trigger for this is adding a background-color on :focus & :active (removing the bg fixes the problem). But it only affects the links in the centre column.
I thought it would probably be a hasLayout issue so I've been adding height: 1% all over the place but with no success.
So, is this a hasLayout problem or something else? And if it is hasLayout, why isn't it responding to the usual treatment or have I just missed the right element?

How to get help
Post a link. If you can't post a link, jsFiddle it.
My blog | My older articles | CSS Reference

Chris..S
Chris..S's picture
Offline
Moderator
Last seen: 9 years 10 weeks ago
Timezone: GMT+1
Joined: 2005-02-22
Posts: 6078
Points: 173

for the js question ...

Wrap your event handler attachment code in a condition that checks the id.

e.g.

<snip>
var list = lists[i];
if (list.id != "active") {
list.onfocus = function () {this.style.backgroundPosition = '0 -84px';}
list.onblur = function () {this.style.backgroundPosition = '0 0';}
}
<snip>

Do something similar for the link elements, although this time you will need to check the id of the link's parent element, e.g. anchor.parentElement.id != "active"

Chris..S
Chris..S's picture
Offline
Moderator
Last seen: 9 years 10 weeks ago
Timezone: GMT+1
Joined: 2005-02-22
Posts: 6078
Points: 173

for the another question

I don't see any shifts in IE.

Did drupal swipe some of your post text, the "with)" seems like its promising more?

fyi, in FF, the payment methods bit (#right?) drops down below the rest of the main content (its ok in IE6).

Chris..S
Chris..S's picture
Offline
Moderator
Last seen: 9 years 10 weeks ago
Timezone: GMT+1
Joined: 2005-02-22
Posts: 6078
Points: 173

Sorry, I do see it. I don't

Sorry, I do see it.

I don't think its hasLayout. But the quick test is to do

#centre * { zoom: 1; }

that will put hasLayout on every element.

I think you'll need to take the advice you gave out earlier in the week, remove stuff bit by bit until it goes away.

Tyssen
Tyssen's picture
Offline
Moderator
Brisbane
Last seen: 7 years 7 weeks ago
Brisbane
Timezone: GMT+10
Joined: 2004-05-01
Posts: 8201
Points: 1386

Chris..S wrote:fyi, in FF,

Chris..S wrote:
fyi, in FF, the payment methods bit (#right?) drops down below the rest of the main content (its ok in IE6).

Is this at a window size smaller than 1000px? Because that's what the thin.js does - moves the third column below the other two.

How to get help
Post a link. If you can't post a link, jsFiddle it.
My blog | My older articles | CSS Reference

Tyssen
Tyssen's picture
Offline
Moderator
Brisbane
Last seen: 7 years 7 weeks ago
Brisbane
Timezone: GMT+10
Joined: 2004-05-01
Posts: 8201
Points: 1386

Chris..S wrote: <snip> var

Chris..S wrote:

<snip>
var list = lists[i];
if (list.id != "active") {
list.onfocus = function () {this.style.backgroundPosition = '0 -84px';}
list.onblur = function () {this.style.backgroundPosition = '0 0';}
}
<snip>


I was trying something like that but didn't have the syntax quite right. Unfortunately, it doesn't seem to work although it seems like it should.
I also still have the problem of the changes not affecing the list item correctly in IE.
I've put together a test page that shows the two different methods you outlined in your first post here.

How to get help
Post a link. If you can't post a link, jsFiddle it.
My blog | My older articles | CSS Reference

Tyssen
Tyssen's picture
Offline
Moderator
Brisbane
Last seen: 7 years 7 weeks ago
Brisbane
Timezone: GMT+10
Joined: 2004-05-01
Posts: 8201
Points: 1386

OK, I'm a little bit closer.

OK, I'm a little bit closer. I changed anchor.parentElement.id for anchor.parentNode.id which solved that problem but the problem with IE remains - the list item part doesn't change, only the anchor.
When you tab onto the links, only the anchor gets the dotted line, so does that mean that the list item itself doesn't actually receive focus? And does that mean that this method won't actually work in IE, or is it the script itself that is missing something?

How to get help
Post a link. If you can't post a link, jsFiddle it.
My blog | My older articles | CSS Reference

Chris..S
Chris..S's picture
Offline
Moderator
Last seen: 9 years 10 weeks ago
Timezone: GMT+1
Joined: 2005-02-22
Posts: 6078
Points: 173

try level 2.0 event handlers

Try using the 'attachEventListener()' function from your thin.js to attach your event handlers.

In DOM Level 2, events trickle down (not in IE) and bubble up, allowing events to be handled at other nodes in the document. I am not sure if this means those events also bubble up to event handlers directly attached to elements using node.on####=function(){}; syntax (Level 0 event handlers). Directly attached event handlers have no way of knowing the original target so things might get wierd if they did receive them. I guess a simple experiment would show one way or the other. Smile

Tyssen
Tyssen's picture
Offline
Moderator
Brisbane
Last seen: 7 years 7 weeks ago
Brisbane
Timezone: GMT+10
Joined: 2004-05-01
Posts: 8201
Points: 1386

I thought I had it then

I thought I had it then realised that IE needed the change to be attached to the hover as well as the focus because the hover was occurring on the list item. This is what I've got so far:

function linkBG() {
	var anchors = document.getElementById("tab-nav1").getElementsByTagName("a");
	for (var i=0; i<anchors.length; i++) {
       	var anchor = anchors[i];
	if (anchor.parentNode.id != "active1") {
        anchor.onfocus = function () {
        this.parentNode.style.backgroundPosition = '0 -84px';
	this.style.backgroundPosition = '100% -84px';
	this.style.color = '#FF0';
	}
        anchor.onblur = function () {
	this.parentNode.style.backgroundPosition = '0 0';
	this.style.backgroundPosition = '100% 0';
	this.style.color = '#006CB1';
	}
}
}
}
window.onload=linkBG;

I can accomplish what I want by simply repeating the onfocus and onblur functions and changing them to onmouseover and onmouseout but is there a way to combine the functions and not reuse code?

How to get help
Post a link. If you can't post a link, jsFiddle it.
My blog | My older articles | CSS Reference

thepineapplehead
thepineapplehead's picture
Offline
Guru
Last seen: 9 weeks 4 days ago
Timezone: GMT+1
Joined: 2004-06-30
Posts: 9674
Points: 810

Would you mind terribly if I

Would you mind terribly if I moved this to OT, Ty?

Verschwindende wrote:
  • CSS doesn't make pies

Tyssen
Tyssen's picture
Offline
Moderator
Brisbane
Last seen: 7 years 7 weeks ago
Brisbane
Timezone: GMT+10
Joined: 2004-05-01
Posts: 8201
Points: 1386

It did start off being about

It did start off being about CSS, but has kinda strayed a bit since then, so I've moved it. Wink
And just to update anyone who might've been following it, once I worked out that 'this' gets passed to functions just like any other variable, I got to this which works for both mouse and keyboard on & off states:

function linkBG() {
	var anchors = document.getElementById("tab-nav1").getElementsByTagName("a");
	for (var i=0; i<anchors.length; i++) {
       	var anchor = anchors[i];
		if (anchor.parentNode.id != "active1") {
            anchor.onmouseover = function () { action(this, "over"); }
			anchor.onfocus = function () { action(this, "over"); }
            anchor.onmouseout = function () { action(this, "off"); }
			anchor.onblur = function () { action(this, "off"); }
		}
	}
	
	function action(obj,state) {
		if (state == "over") {
         		obj.parentNode.style.backgroundPosition = '0 -84px';
			obj.style.backgroundPosition = '100% -84px';
			obj.style.color = '#FF0';
		} else {
			obj.parentNode.style.backgroundPosition = '0 0';
			obj.style.backgroundPosition = '100% 0';
			obj.style.color = '#006CB1';
		}
	}
}
window.onload=linkBG;

How to get help
Post a link. If you can't post a link, jsFiddle it.
My blog | My older articles | CSS Reference

Tyssen
Tyssen's picture
Offline
Moderator
Brisbane
Last seen: 7 years 7 weeks ago
Brisbane
Timezone: GMT+10
Joined: 2004-05-01
Posts: 8201
Points: 1386

If anyone had an interest in

If anyone had an interest in this topic (although it was probably only me & Chris..Drunk, I realised after reading another article that, while my js worked, it wasn't separating the presentation from the behaviour because the styles were embedded in the script. So I rewrote it along the lines of the one used to make Suckerfish dropdowns work in IE and I've documented the whole thing here.

How to get help
Post a link. If you can't post a link, jsFiddle it.
My blog | My older articles | CSS Reference