SitePoint Sponsor

User Tag List

Results 1 to 24 of 24
  1. #1
    SitePoint Enthusiast
    Join Date
    Oct 2006
    Posts
    97
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Chaning CSS styles on rollover with JavaScript

    Hello,

    I'm trying to create an effect of text being hidden and then revealed when you hover your mouse over it. I have achieved this using by styling anchors with CSS here: http://mythbustersresults.com/results/all_guess.html

    I don't think this is the best solution though because the anchors are clickable and they don't really go anywhere except the top of the page, which will probably annoy visitors.

    Is there a nice clean way to do this with JavaScript? I definately don't want to use images - I want the styling of the paragraph element to change. I've found solution that's very close here: http://alistapart.com/articles/dropdowns but I am not good enough with JS yet to modify it to my needs.

  2. #2
    I'll take mine raw silver trophy MikeFoster's Avatar
    Join Date
    Dec 2002
    Location
    Alabama, USA
    Posts
    2,560
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi volare,

    My first thought was to get all elements with class 'result' and add mouseover and mouseout listeners - but there are many of them so I went for a different approach. The following is just a rough idea but it works for me in Opera, Firefox, and IE.

    You'll need to remove all the "A" elements and just leave the text.

    Code:
    <style type='text/css'>
    .result {
      color: #AADDFF;
      background-color: #AADDFF;
    }
    </style>
    <script type='text/javascript'>
    var ele = null, re = new RegExp("(^|\\s)result(\\s|$)");
    xAddEventListener(document, 'mousemove', onMousemove, false);
    function onMousemove(e)
    {
      e = e || window.event;
      var t = e.target || e.srcElement;
      if (t != ele) {
        while (t && t.nodeName.toLowerCase() != 'p') {
          t = t.parentNode;
        }
        if (t && re.test(t.className)) {
          if (ele) {
            ele.style.color = '#AADDFF';
            ele.style.backgroundColor = '#AADDFF';
          }
          ele = t;
          var s = t.className;
          var c = '#FF00FF'; // default to 'nottested'
          if (s.indexOf('multi') != -1) {
            c = '#888';
          }
          else if (s.indexOf('busted') != -1) {
            c = '#F00';
          }
          else if (s.indexOf('plausible') != -1) {
            c = '#FFC000';
          }
          else if (s.indexOf('confirmed') != -1) {
            c = '#00A300';
          }
          t.style.color = c;
          t.style.backgroundColor = '#FFF';
        }
        else {
          if (ele) {
            ele.style.color = '#AADDFF';
            ele.style.backgroundColor = '#AADDFF';
          }
          ele = null;
        }
      }
    }
    // The following are from Cross-Browser.com
    function xGetElementById(e)
    {
      if(typeof(e)=='string') {
        if(document.getElementById) e=document.getElementById(e);
        else if(document.all) e=document.all[e];
        else e=null;
      }
      return e;
    }
    function xAddEventListener(e,eT,eL,cap)
    {
      if(!(e=xGetElementById(e)))return;
      eT=eT.toLowerCase();
      if(e.addEventListener)e.addEventListener(eT,eL,cap||false);
      else if(e.attachEvent)e.attachEvent('on'+eT,eL);
      else e['on'+eT]=eL;
    }
    </script>
    As I look back over my code I see that it is quite messy and could be optimized - but just consider it a proof of concept

    Also, there will be problems if Js is disabled - but I ran out of time

  3. #3
    I'll take mine raw silver trophy MikeFoster's Avatar
    Join Date
    Dec 2002
    Location
    Alabama, USA
    Posts
    2,560
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Another idea would have been to leave the "A" elements, give them "cursor:default" in css, and use Js to intercept clicks on those links which would then cancel the default action. The benefit to this is that it would still work with Js disabled.

  4. #4
    I'll take mine raw silver trophy MikeFoster's Avatar
    Join Date
    Dec 2002
    Location
    Alabama, USA
    Posts
    2,560
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    The more I think about it, the more I dislike my original solution. I'll have to think about it some more.

  5. #5
    SitePoint Enthusiast
    Join Date
    Oct 2006
    Posts
    97
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thank you very much Mike! I will be trying that script out soon. And once you've thought about it more, let me know please.

  6. #6
    SitePoint Enthusiast
    Join Date
    Oct 2006
    Posts
    97
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    It's up and it seems to be working perfectly. Do you happen to know any statistics about the number of people who disable JavaScript? Is that a significant concern?

  7. #7
    SitePoint Wizard Pepejeria's Avatar
    Join Date
    Jan 2005
    Location
    Too far up north
    Posts
    1,566
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    This could also be solved by using CSS only. Instead of using anchors, you could use any other html elmenent and then use :hover on that element. This will not work in IE 6 though, since that browser belongs in a museum.

    One alternative solution could be to use the CSS solution + use a behavior to "patch" IE 6. Mike, what do you think about this approach?

    Drawback with this would be that the CSS wouldn't validate though to the behavior, but this could be solved by adding the behavior with JavaScript.

  8. #8
    I'll take mine raw silver trophy MikeFoster's Avatar
    Join Date
    Dec 2002
    Location
    Alabama, USA
    Posts
    2,560
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Yes, Pepe, I like the CSS solution better - but I don't have any experience with IE behaviors.

    The pure CSS solution is pretty easy. The problem is that IE6 (and below) and any other browsers that don't support the hover pseudo on a 'P' will only apply the ".result" rule and then none of the results will be visible at all. This is easy to fix for IE using conditional comments:

    Code:
    <![if gte IE 7]>
    <style type='text/css'>
    .result {
      color: #ADF;
      background-color: #ADF;
      width: 210px;
    }
    .result:hover {
      background-color: #FFF;
    }
    .multi:hover {
      color: #888;
    }
    .busted:hover {
      color: #F00;
    }
    .plausible:hover {
      color: #FFC000;
    }
    .confirmed:hover {
      color: #00A300;
    }
    .nottested:hover {
      color: #F0F;
    }
    </style>
    <![endif]>
    
    <!--[if lt IE 7]>
    <style type='text/css'>
    .result {
      background-color: #FFF;
      width: 210px;
    }
    .multi {
      color: #888;
    }
    .busted {
      color: #F00;
    }
    .plausible {
      color: #FFC000;
    }
    .confirmed {
      color: #00A300;
    }
    .nottested {
      color: #F0F;
    }
    </style>
    <![endif]-->
    Have a look at my test of the above. For IE6 and below it simply does not hide the results.

    Actually, the most cross-browser solution is to use the 'A' elements as you were originally.

  9. #9
    SitePoint Enthusiast
    Join Date
    Oct 2006
    Posts
    97
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    hmm...I would rather use purely CSS too, but loosing IE6 functionality is huge, no? I read in November that it has 49.9&#37; market share, which I imagine has gone down slightly but not that much.

  10. #10
    I'll take mine raw silver trophy MikeFoster's Avatar
    Join Date
    Dec 2002
    Location
    Alabama, USA
    Posts
    2,560
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Have a look at my test again. IE6 gets the Js solution and other browsers get the CSS solution.

    There's still one thing to fix tho - the case when Js is disabled in IE6<

  11. #11
    SitePoint Addict Mirek Komárek's Avatar
    Join Date
    Dec 2006
    Location
    Prague
    Posts
    210
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    In this case you can do just this
    1/in all browser css add
    Code:
    .ienote{display:none}
    2/to conditional coments for lower then IE7
    Code:
    .result {
      color: #adf;
      background-color: #adf;
      width:0;margin:0;padding:0 !important;
    }
    .hovernote{display:none}
    .ienote{display:block}
    3/In html ie note
    Code:
    <p class="ienote"> (Doubleclick the shaded areas to see the results)</p>
    Or use javascript solution ;-)
    Anyway try to think about ctrl+a

  12. #12
    SitePoint Addict Mirek Komárek's Avatar
    Join Date
    Dec 2006
    Location
    Prague
    Posts
    210
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Sorry MikeFoster I have not seen your answer, when I sent this funny post above.

  13. #13
    I'll take mine raw silver trophy MikeFoster's Avatar
    Join Date
    Dec 2002
    Location
    Alabama, USA
    Posts
    2,560
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi Mirek. No problem You have a good suggestion!

    Quote Originally Posted by MikeFoster View Post
    There's still one thing to fix tho - the case when Js is disabled in IE6<
    I think I've got that fixed now :-)

  14. #14
    SitePoint Wizard Pepejeria's Avatar
    Join Date
    Jan 2005
    Location
    Too far up north
    Posts
    1,566
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I never used to like behaviors before, but now i tend to use them to "patch" IE. For example for transparent png support etc. No need to wait for window.onload to attach a fix.

    So when there is some missing CSS support I would do the following:
    Code:
    <html>
    <head>
    <style>
    p:hover,p.hover { color:red; }
    p { behavior:url(hover.htc); }
    </style>
    </head>
    
    <body>
    <p>hover me</p>
    
    </body>
    </html>
    The htc file:
    Code:
    <public:component lightWeight="true">
    <public:attach event="onmouseover" onevent="toggle();"/>
    <public:attach event="onmouseout" onevent="toggle();"/>
    <script>
    function toggle() {
    	element.className = element.className == "" ? "hover" : "";
    }
    </script>
    </public:component>

  15. #15
    I'll take mine raw silver trophy MikeFoster's Avatar
    Join Date
    Dec 2002
    Location
    Alabama, USA
    Posts
    2,560
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Very interesting, Pepejeria. I'll have to look into that some more. Thanks!

  16. #16
    SitePoint Enthusiast
    Join Date
    Oct 2006
    Posts
    97
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Wow

    I am learning so much from this thread. Thanks for all of the wonderful ideas everyone.

    I've been trying to setup the solution as Pepejeria describes. It seems like that may end up being the simplest - if I can get it to work. I set up a test for it here: http://mythbustersresults.com/hovertest.html

    The effect isn't working for me properly in IE. The paragraph becomes visible but it's not styled as planned. I havn't altered the .htc file (will I need to?). Here is stuff in the head:
    Code:
    <style>
    p.result { width: 210px; color: #AADDFF; background-color: #AADDFF; }
    p.multi:hover,p.multi.hover { color: #888; background-color: #FFF; }
    p.confirmed:hover,p.confirmed.hover { color: #00A300; background-color: #FFF; }
    p.busted:hover,p.busted.hover { color: #F00; background-color: #FFF; }
    p.plausible:hover,p.plausible.hover { color: #FFC000; background-color: #FFF; }
    p.nottested:hover,p.nottested.hover { color: #FF00FF; background-color: #FFF; }
    p.result { behavior:url(http://mythbustersresults.com/hover.htc); }
    </style>
    Is using p.class.class legit?
    Last edited by volare; Jan 5, 2007 at 15:34. Reason: Another Question

  17. #17
    SitePoint Wizard Pepejeria's Avatar
    Join Date
    Jan 2005
    Location
    Too far up north
    Posts
    1,566
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Didn't look much into it, but I noticed it fails on your url, but works locally? Odd.

  18. #18
    SitePoint Member
    Join Date
    Dec 2006
    Posts
    3
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi Guys,

    Don't mean to butt in but i have a similar problem im working through.
    Been reading this thread and have attempted to mock some of your solutions to no avail.

    I have a nested list that could have an infinite amount of nesting i.e:

    <ul id="nav">
    <li><a href="#">australia</a>
    <ul>
    <li><a href="#">Victoria</a>

    <ul>
    <li><a href="#">melbourne</a>
    <ul>
    <li><a href="#">fed square</a></li>

    </ul>


    </li>

    </ul>
    </li>


    </ul>
    </li>

    <li><a href="#">Next list element</a></li>

    <!-- etc. -->

    </ul>

    What i have been trying to do is highlight children of each of the ul's on hover.

    For example if i hover over Australia, the background color on:
    Australia, Victoria, Melbourne & Fed square all change to red.

    If i hover over Victoria, the background color on only:
    Victoria, Melbourne & Fed square change to red.

    If i hover over Melbourne, the background color on only:
    Melbourne & Fed square change to red.

    If i hover over Fed square (being the last in the tree), the background color on:
    Fed square only changes to red.

    I have attempted modifying the suckerfish method(http://www.htmldog.com/articles/suckerfish/dropdowns/) with no success and am currently trying to learn javascript from the "Javascript Anthology".

    Any help with this would be greatly appreciated.

    many thanks
    doug

  19. #19
    I'll take mine raw silver trophy MikeFoster's Avatar
    Join Date
    Dec 2002
    Location
    Alabama, USA
    Posts
    2,560
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi Doug,

    Yes, your post is quite off-topic. Oh well...

    Welcome to SPF!

    Here is a function, xWalkUL, which I used to implement this UL-based menu, xTreeMenu. Perhaps it will give you some ideas.

    Again... Welcome aboard!

  20. #20
    SitePoint Member
    Join Date
    Dec 2006
    Posts
    3
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi Mike,

    Thanks for your help, but for a javascript novice like myself i found that brilliant and a bit too heavy going.

    I Have been trying to do something like below where i detect the parent, and apply the rollover based on the parent, this doesn't seem to achieve what i thought it would. Instead of detecting the parent i think i need to find the children and apply the rollover style to that. Not too sure how to do that any suggestions?

    thanks

    doug

    function rollover()

    {

    var theList = document.getElementById("nav");

    var theListElement = theList.getElementsByTagName("li");

    for (var i=0 ; i< theListElement.length; i++)
    {

    var parent = theListElement[i].parentNode;


    if(parent.getAttribute("id") == "sub")
    {

    theListElement[i].onmouseover=function()
    {
    this.className+=" red";
    }

    theListElement[i].onmouseout=function()
    {
    this.className=this.className.replace(/ ?red/g, '');
    }


    }




    }


    }

  21. #21
    SitePoint Wizard
    Join Date
    Mar 2001
    Posts
    3,537
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    In non-IE browsers, you can make the Suckerfish dropdown menu work for your situation by using the style rule:

    li:hover > ul {display: block;}

    That selector says to apply the specified style to a <ul> if it is a child of an <li> that is being hovered over. The '>' selects only the child elements. Children are the first level of descendents. On the other hand, a selector like this:

    li:hover ul

    applies to any ul that is a descendant anywhere in the <li>'s hierarchy. Unfortunately, IE does not understand the '>' symbol in a css selector, so it will ignore the whole css code fragment; and if you try:

    li:hover ul {display:block};

    all the submenus will get displayed because that says to display all <ul>'s that are descendants of the <li> being hovered over. So, for IE, you need a js solution.

    For IE, you can apply javascript in the same way that the Suckerfish dropdown menu does. I got the javascript to work by first obtaining the 'nav' <ul>, and then obtaining all the <ul>'s contained therein, like this:
    Code:
    var navMenu = document.getElementById("nav");
    var ulArray = navMenu.getElementsByTagName("ul");
    Then, I looped through the ulArray, and accessed each <ul>'s parentNode, i.e. the <li>, and assigned it an onmouseover function. Inside the onmouseover function, you need to set the <ul>'s display to block to reveal the menu, but I couldn't figure out an easy way to get a reference to the <ul> in the code inside the onmouseover function.

    You would think it would be easy to get a reference to the <ul> inside the onmouseover function because you have the ulArray already, and you might try:
    Code:
    ulArray[i].parentNode.onmouseover = function(){ulArray[i].style.display = "block";}
    but the problem is that the onmouseover function will execute some time in the future, which is long after the surrounding function has finished executing, and the surrounding function contains the ulArray. That means the surrounding function will be out of scope. Therefore, it causes problems when the code inside the onmouseover function tries to refer to a variable, i.e. ulArray[i], in a function that no longer exists. Getting a reference to the <ul> can be accomplished by using an advanced javascript technique that involves what is called a "closure", but I assume you are not familiar with closures.

    So, another way to get a reference to the <ul> from inside the onmouseover function is to use the parent node to get the parent's childNodes, and one of the parent's childNodes will be the <ul>:
    Code:
    ulArray[i].parentNode.onmouseover = function()
    {
          var children = this.childNodes;
    }
    (Note: when you use 'this' inside the onmouseover function it refers to the element that is being moused over, i.e. the parent node which is one of the <li>'s.)
    The first element in the children array that has a nodeName=="UL"(must be caps) will be the <ul> you want to display.

    Once you have a reference to the <ul>, all you have to do is display the <ul>:
    Code:
    if(children[j].nodeName == "UL")
    {
    	children[j].style.display = "none";
    	break;
    }
    Also, once you have the <ul>, you don't need to keep looping through the children array, so you can break out of the loop.

    You can add the onmouseouts using identical code. Afterwards, you will be able to add submenus anywhere in the html, and they will display properly without having to change any of the javascript code.

    However, your page won't work for people who have js disabled, and as you get more advanced with javascript, you will want to design your webpage to work for those users too, as well as users who don't use a mouse and therefore can't mouseover anything, which complicates things even more.
    Last edited by 7stud; Jan 8, 2007 at 01:03.

  22. #22
    SitePoint Wizard
    Join Date
    Mar 2001
    Posts
    3,537
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ok, I was just reading ppk's new javascript book, and I was trying to figure out why he coded an example the way he did, and I suddenly realized that it was to avoid having to explain the "closure" problems that crop up in your situation, i.e. the problems that can arise when you assign event handler functions to html elements in a loop.

    In the example I read, ppk avoided the problems by attaching values he needed later to the html element he was dealing with. I don't know if you are aware of this, but in js you can make up a property name for an html element and then assign a value to it. For instance, you can do this:
    Code:
    var targetDiv = document.getElementById("myDiv");
    targetDiv.someMadeUpName = "hello world";
    then you can access the stored value like this:
    Code:
    alert(targetDiv.someMadeUpName);
    That trick provides a clean and efficient way to store a reference to a <ul> in the parent <li>--that's the <ul> you want to display when the parent <li> is moused over. That will obviate the subsequent need to loop through the parent <li>'s childNodes to get a reference to the <ul> as I described above. The trick will work like this:

    You start off as before:
    Code:
    var navMenu = document.getElementById("nav");
    var ulArray = navMenu.getElementsByTagName("ul");
    The ulArray contains references to all the <ul>'s so having to look them up again later(by cycling through the childNodes of each parent <li>) is inefficient. Instead, you can attach each <ul> reference to its parent <li> now:
    Code:
    var parent = null;
    for(var i = 0, len = ulArray.length; i < len; ++i)
    {
    	parent = ulArray[i].parentNode; 
    	parent.targetUL = ulArray[i];
    where targetUL is just a made up name, which holds a reference to the <ul> that you want to display when the parent <li> is moused over. Then you can assign a simple mouseover function to each parent <li>:
    Code:
    for(var i = 0, len = ulArray.length; i < len; ++i)
    {
    	parent = ulArray[i].parentNode;
    	parent.targetUL = ulArray[i];
    			
    	parent.onmouseover = function ()
    	{
    		this.targetUL.style.display = "block"
    	}
    Once again note that when you use the 'this' keyword inside an event handler function, it will refer to the html element upon which the event is occurring, which in this case is the parent <li> that the user mouses over. Therefore, this.targetUL is a reference to the <ul> that you want to display. That's a clean, efficient, less complex, and hopefully more readily understandable method for achieving the js solution.

  23. #23
    SitePoint Enthusiast
    Join Date
    Oct 2006
    Posts
    97
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    MikeFoster: I ended up using your javascript solution. Thanks very much for the personalized help - it was more than I expected. The site actually got a huge amount of traffic over the weekend and I haven't heard any complaints so I assume it is working well in the wild.

  24. #24
    I'll take mine raw silver trophy MikeFoster's Avatar
    Join Date
    Dec 2002
    Location
    Alabama, USA
    Posts
    2,560
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi volare,

    You are very welcome


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •