SitePoint Sponsor

User Tag List

Results 1 to 7 of 7
  1. #1
    SitePoint Member
    Join Date
    Feb 2008
    Posts
    4
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    showing and hiding content... but with a slight 'twist'

    Hi I have a question that can no doubt be answered in no time by Javascript and Prototype experts out there. Here's the deal:

    I have the following HTML in a project I am working with:

    Code HTML4Strict:
    <div id="item1" class="item">
    	<p>Lorem ipsum dolor sit amet consectetuer id ut tempus vitae vel. Odio vitae volutpat feugiat a sed consectetuer tempor urna amet malesuada. Wisi Nullam mauris pharetra ac ut gravida quis vitae libero pede.</p>
    	<dl>
    		<dt><a href="#" onmouseover="comment_show('item1');">items</a></dt>
     
    		<dd>
    			<ul>
    				<li>item 1</li>
    				<li class="hideme">item 2 (hidden)</li>
    				<li class="hideme">item 3 (hidden)</li>
    			</ul>
    		</dd>
    	</dl>
    </div>

    The CSS looks like this:
    Code CSS:
    div { width: 600px; margin: 10px; float: left; border: 1px solid #e8e8e8; padding: 10px; }
    dt { float: left; width: 5em; }
    ul { list-style-type: none; }
    .hide { display: none; }
    .show { background:#f8f8f8; }
    .show .hide { display: block; }

    and the Prototype-driven Javascript, like this:

    Code JavaScript:
    function comment_show(id) {
    	$(id).addClassName('show');
     
    	Event.observe($(id), 'mouseout', function() {
    	  $(id).removeClassName('show');
    	});
    }

    What I would like to happen, is when the user mouses over the 'items' link (in the <dt> container), the extra list items appear and do not disappear until the mouse pointer leaves the div container. I can do this if the inline (I know, it should be unobtrusive! :-)) mouseover call is placed on the dl or even the div itself, but I would like (well, the client wants!) it on the link. Does anyone know how to make this work?

    I hope that makes sense, as I am not really a Javascript guy! :-)

    cheers,

    Stuart

  2. #2
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,729
    Mentioned
    104 Post(s)
    Tagged
    4 Thread(s)
    I have taken your idea and extended on it.

    I know that you care about standards, because you mentioned that you're aware that inline events are to be avoided. In light of this, I have modified things so that if javascript is not available the information still appears on the page.

    The idea is to have a class name "hideme" that provides the desired background color, then to add and remove a "hide" class. The "add" class is no longer required because the appropriate stylings are attached on to the "hideme" class instead, which is more semantically appropriate.

    Code css:
    .hideme { background:#f8f8f8; }
    .hide { display: none; }

    Now that the content is still available should there be no javascript, we can use javascript to attach an event to the link and to hide the appropriate content when the page loads.

    Code html4strict:
    <dl>
    	<dt><a id="item1" href="#">items</a></dt>
    	<dd>
    		<ul>
    			<li>item 1</li>
    			<li class="hideme">item 2 (hidden)</li>
    			<li class="hideme">item 3 (hidden)</li>
    		</ul>
    	</dd>
    </dl>

    Because hiding the content is going to be done from both an event and also on page load, it makes sense to put that task in to a separate function, and as we're doing it fro the hide function, we should do it for the show function as well.

    Because the event element and the elements we want to affect are somewhat separated, they accept an argument from where they will search for the elements to hide.

    Code javascript:
    function show(id) {
    	$(id).select('.hideme').each(function (el) {
    		el.removeClassName('hide');
    	});
    }
    function hide(id) {
    	$(id).select('.hideme').each(function (el) {
    		el.addClassName('hide');
    	});
    }

    When it comes to attaching the event, I have chosen to place the code at the bottom of the body. Other people choose to place the code in the head and wait until the page has loaded before attaching the event, but by placing the code at the bottom of the body we can attach the event long before the page has finished loading.

    After the events have been attached we can also hide the content. Remember that the page should have the content visible should there be no javascript, which means that we use javascript to hide the content.

    Code javascript:
    var parent = $('item1').parentNode.parentNode;
    Event.observe($('item1'), 'mouseover', function () {
    	show(parent);
    });
    Event.observe($('item1'), 'mouseout', function () {
    	hide(parent);
    });
    hide(parent);

    Here is the complete test page.

    Code html4strict:
    <html>
    <head>
    <title>Test</title>
    <style type="text/css">
    div { width: 600px; margin: 10px; float: left; border: 1px solid #e8e8e8; padding: 10px; }
    dt { float: left; width: 5em; }
    ul { list-style-type: none; }
    .hideme { background:#f8f8f8; }
    .hide { display: none; }
    </style>
    </head>
     
    <body>
    <div class="item">
        <p>Lorem ipsum dolor sit amet consectetuer id ut tempus vitae vel. Odio vitae volutpat feugiat a sed consectetuer tempor urna amet malesuada. Wisi Nullam mauris pharetra ac ut gravida quis vitae libero pede.</p>
    	<dl>
    		<dt><a id="item1" href="#">items</a></dt>
    		<dd>
    			<ul>
    				<li>item 1</li>
    				<li class="hideme">item 2 (hidden)</li>
    				<li class="hideme">item 3 (hidden)</li>
    			</ul>
    		</dd>
    	</dl>
    </div>
    <script src="js/prototype.js"></script>
    <script>
    function show(id) {
    	$(id).select('.hideme').each(function (el) {
    		el.removeClassName('hide');
    	});
    }
    function hide(id) {
    	$(id).select('.hideme').each(function (el) {
    		el.addClassName('hide');
    	});
    }
    var parent = $('item1').parentNode.parentNode;
    Event.observe($('item1'), 'mouseover', function () {
    	show(parent);
    });
    Event.observe($('item1'), 'mouseout', function () {
    	hide(parent);
    });
    hide(parent);
    </script>
    </body>
    </html>
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  3. #3
    SitePoint Member
    Join Date
    Feb 2008
    Posts
    4
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Many thanks for that, Paul - particularly in separating the javascript from the html content (the inline 'onmouseover' was really annoying me! )

    Unfortunately, though, this leaves me with the same problem (although with better code!) - when the mouse leaves the link, the extra list items disappear. I need the extra items to appear when the user moves the pointer over the link, and stay visible until the mouse pointer leaves the div container. I tried changing your:

    Code JavaScript:
    Event.observe($('item1'), 'mouseout', function () {
        hide(parent);
    });

    to:

    Code JavaScript:
    Event.observe(parent, 'mouseout', function () {
        hide(parent);
    });

    but that did not work - the same 'problem' exists (list items disappear when user moves away from link rather than the <dl>). It's almost as though the whole sequence is 'tied' to the initial call: if I attach the mouseover event to the div or the dl, the mouseout event works as required (only problem is, the client wishes it to appear when mousing over the link itself )

    Oh, and one other point (I've just realised that it will/may impact upon possible solutions) - there will be a list of these div's (that will probably become an actual <ul> ) on the page, and only the hidden items within the link's container should appear on mouseover (hope that makes sense!)

    Any ideas?

    Thanks,

    Stuart

  4. #4
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,729
    Mentioned
    104 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by shomfray View Post
    Many thanks for that, Paul - particularly in separating the javascript from the html content (the inline 'onmouseover' was really annoying me! )

    Unfortunately, though, this leaves me with the same problem (although with better code!) - when the mouse leaves the link, the extra list items disappear. I need the extra items to appear when the user moves the pointer over the link, and stay visible until the mouse pointer leaves the div container.
    Ahh, well that's getting beyond my skills with Prototype.

    What I suspect will be the solution is to place the event on the div container, activate the show part only when the anchor itself is the source of the event, and hide things only when the mouseout event occurs on an element that is not inside the div.

    Quote Originally Posted by shomfray View Post
    Oh, and one other point (I've just realised that it will/may impact upon possible solutions) - there will be a list of these div's (that will probably become an actual <ul> ) on the page, and only the hidden items within the link's container should appear on mouseover (hope that makes sense!)

    Any ideas?
    The code already allows for that type of situation. It deliberately affected things that are accessible from only two parents above the anchor, eg. just inside the div.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  5. #5
    SitePoint Member
    Join Date
    Feb 2008
    Posts
    4
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ahh, OK, but many thanks for your responses Paul.

    Quote Originally Posted by pmw57 View Post
    ... and hide things only when the mouseout event occurs on an element that is not inside the div.
    ...which is sort of where I'm having my problems

    I still don't understand why when the mouseover event is applied to the link, the mouseout event on the containing div does not appear to work - it's as though this container can no longer be 'seen'. Oh, well...

  6. #6
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,729
    Mentioned
    104 Post(s)
    Tagged
    4 Thread(s)
    The mouseout does work, but only too well.
    Every element inside the div is creating a mouseout event, and these all bubble up to the through the parent elements, to the div on which you have assigned the onmouseout event.

    When the onmouseover is on a matching onmouseout event on the div, that's the easy part because everytime you mouseout of an element inside the div, you mouseover another element which maintains the desired look.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  7. #7
    SitePoint Member
    Join Date
    Feb 2008
    Posts
    4
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    OK, finally a possible solution...

    Paul, the bubbling issue led me to do some more research, and I found some useful info (plus, some useful javascript!) on Pat Nakajima's article Basic event delegation in Prototype - particularly the delegators method.

    So, here's what I come up with. First, the javascript (also requires the first 'snippet' of code on Pat's article, remember)

    Code JavaScript:
    ElementBehaviors = {
      showHidden: function(event) {
        var element = event.element();
        $(element).up('li').addClassName('show');
        event.stop();
      },
     
      hideHidden: function(event) {
        var element = event.element();
        $(element).removeClassName('show');
        event.stop();
      }
    }
     
    function prepare_show_comment() {
    	var containers = $$('.item');
     
    	for (var i=0, len=containers.length; i < len; ++i) {
      	var id = containers[i].readAttribute('id');
     
    		$(id).delegators('mouseover', {
    		  'dt a': ElementBehaviors.showHidden
    		});
     
    		$(id).delegators('mouseout', {
    		  'li': ElementBehaviors.hideHidden
    		});
      }
    }
     
    function hide_extra_bits() {
      var hideElements = $$('li.hideme');
      for (var i=0, len=hideElements.length; i < len; ++i) {
      	hideElements[i].addClassName('hide');
      }
    }
     
     
    document.observe("dom:loaded", function() {
    	hide_extra_bits();
    	prepare_show_comment();
    });

    Not the cleanest in the world, perhaps, and clearly showing my lack of knowledge/experience with this type of coding, but it works. The CSS is the same as previously, but I made a slight change to the HTML, to show a list of items (3):

    Code HTML4Strict:
    <ul>
    	<li id="item1" class="item">
    		<p>Lorem ipsum dolor sit amet consectetuer id ut tempus vitae vel. Odio vitae volutpat feugiat a sed consectetuer tempor urna amet malesuada. Wisi Nullam mauris pharetra ac ut gravida quis vitae libero pede.</p>
    		<dl>
    			<dt><a href="#">items</a></dt>
     
    			<dd>
    				<ul>
    					<li>item 1</li>
    					<li class="hideme"><a href="#">item 2</a> (hidden)</li>
    					<li class="hideme"><a href="#">item 3</a> (hidden)</li>
    				</ul>
    			</dd>
    		</dl>
    	</li>
     
    	<li id="item2" class="item">
    		<p>Lorem ipsum dolor sit amet consectetuer id ut tempus vitae vel. Odio vitae volutpat feugiat a sed consectetuer tempor urna amet malesuada. Wisi Nullam mauris pharetra ac ut gravida quis vitae libero pede.</p>
     
    		<dl>
    			<dt><a href="#">items</a></dt>
     
    			<dd>
    				<ul>
    					<li><a href="#">item 1</a></li>
    					<li class="hideme"><a href="#">item 2</a> (hidden)</li>
    					<li class="hideme"><a href="#">item 3</a> (hidden)</li>
    				</ul>
    			</dd>
    		</dl>
    	</li>
     
    	<li id="item3" class="item">
    		<p>Lorem ipsum dolor sit amet consectetuer id ut tempus vitae vel. Odio vitae volutpat feugiat a sed consectetuer tempor urna amet malesuada. Wisi Nullam mauris pharetra ac ut gravida quis vitae libero pede.</p>
     
    		<dl>
    			<dt><a href="#">items</a></dt>
     
    			<dd>
    				<ul>
    					<li>item 1</li>
    					<li class="hideme"><a href="#">item 2</a> (hidden)</li>
    					<li class="hideme">item 3 (hidden)</li>
    				</ul>
    			</dd>
    		</dl>
    	</li>
    </ul>

    And there it is - something that works (words that probably send a shiver through every decent programmer in the world! )


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
  •