jQuery Hide/Close Link

code 0 comments suggest edit

UPDATE (12/26): I updated this post to use the href instead of the rel attribute

It’s Christmas day, and yes, I’m partaking in the usual holiday fun such as watching Basketball, hanging out with the family and eating our traditional Alaskan king crab Christmas dinner. But of course it wouldn’t be a complete day without writing a tiny bit of code!

code

Today I’ve been working on improving the UI here and there in Subtext. One common task I run into over and over is using an anchor tag to trigger the hiding of another element such as a DIV. It happens so often that I get pretty tired of hooking up each and every link to the element it must hide. Being the lazy bastard that I am, I thought I’d try to come up with a way to do this once and for all with jQuery and a bit of convention.

Here’s what I came up with. The following HTML shows a DIV element with an associated link that when clicked, should hide the DIV.

<div id="hide-this">
    This here DIV will be hidden when you click on 
    the link
</div>
<a href="#hide-this" class="close">This is the link that hides the DIV</a>

The convention here is that any anchor tag with a class “close” is going to have its click event hooked up to close another element. That element is identified by the anchor tag’s rel href attribute, which contains the id of the element to hide. This was based on a suggestion by a couple of commenters to the original version of this post where I used a rel attribute. I like this much better for two reasons:

  • The href value is a hash which is already in the correct format to be a CSS selector for an ID.
  • I’m not using the href value in the first place, so might as well make use of it.

Yeah, this is probably an abuse of this attribute, but in this case it’s one I can live with due to the benefits it produces. The rel attribute is supposed to define the relationship of the current document to the document referenced by the anchor tag. Browsers don’t do anything with this attribute, but search engines do as in the case with therel value of “no-follow”~~.~~

However in this case, I feel my usage is in the spirit of this attribute as I’m defining the relationship of the anchor tag to another element in the document. Also, search engines are going to ignore the value I put in there unless the id happens to match a valid value, so no animals will be harmed by this.

Now I just need a little jQuery script to make the magic happen and hook up this behavior.

$(function() {
    $('a.close').click(function() {
        $($(this).attr('href')).slideUp();
        return false;
    });
});

I happened to choose the slide up effect for hiding the element in this case, but you could choose the hide method or fadeOut if you prefer.

I put up a simple demo here if you want to see it in action.

I’m just curious how others handle this sort of thing. If you have a better way, do let me know. :)

Tags: jQuery, JavaScript

Found a typo or error? Suggest an edit! If accepted, your contribution is listed automatically here.

Comments

avatar

50 responses

  1. Avatar for Steve Naughton
    Steve Naughton December 25th, 2009

    cool will definatly try it :)

  2. Avatar for Pita.O
    Pita.O December 25th, 2009

    I just picked up more than just this cool feature. I also picked up giving additional thought to convention as a way to produce re-usable scripts. Less is definitely more!
    BTW: Can we take this one step further and eliminate the 'rel' attribute. Let the capture just pick up the the first parent 'div' to the 'a'.
    Conceptually:
    -- markup
    <div>
    <h2>Blah blah blog</h2>
    [hide]
    </div>
    -- js
    $(function() {
    $('a.close').click(function() {
    $(this).parent().slideUp();
    });
    });

  3. Avatar for Bobby
    Bobby December 25th, 2009

    Just curious, seen code b4 that had unknown attributes in image tags that jQuery and even javascript can pickup. For instance:

    <img src="someimage.jpg" hoverimg="somehoverimg.jpg" customattrib="sometext">

    Just curious does this not work with Hrefs? Right now to busy/lazy to try but Its what i was thinking when you were saying you were misusing the rel attrib.
    Either way nice article
    Thanks

  4. Avatar for Keith
    Keith December 25th, 2009

    do you have one too many </h2> ???

  5. Avatar for Aaron Robinson
    Aaron Robinson December 25th, 2009

    Phil,
    Why not just have the element that you want to adorn with a [hide] link have the special class and then have jQuery inject the appropriate link? (with the added benefit of not having to worry about putting in those links, or bastardizing the rel attribute...)
    [code]
    <div class="closable">
    we should get a link at the end to hide this...
    </div>
    <div>
    this one doesn't get a link
    </div>
    <div class="closable">
    this one gets a link too...
    </div>
    [/code]
    [code]
    $(function() {
    $('.closable').each(function() {
    var closable = this;
    $('')
    .attr('href', '#')
    .html('[click to hide]')
    .click(function() {
    $(closable).hide();
    return false;
    })
    .appendTo(closable)
    ;
    });
    });
    [/code]
    - Aaron

  6. Avatar for Aaron Robinson
    Aaron Robinson December 25th, 2009

    That got a little mangled... I uploaded working example to:
    http://www.idealaunch.com/upload/closable.htm

    - Aaron

  7. Avatar for Rick
    Rick December 25th, 2009

    why not use the href itself? As in, a href #related-results

  8. Avatar for Dave Ward
    Dave Ward December 25th, 2009

    If you're always going to be closing the nearest parent div, go with something like this:
    $('.close').click(function() {
    $(this).closest('div').slideUp();
    });

    If you can't assume that the closest parent div is the container, tag the containers with a class and ascend to that:
    $('.close').click(function() {
    $(this).closest('div.collapsable').slideUp();
    });

    It would be a good idea to use live() if there will potentially be many of these on the page or if they may be added dynamically after $(document).ready() fires. Also, you'll want to either preventDefault() or return false to avoid the browser "navigating" to the top of the page and appending a # to the location.
    $(document).ready(function() {
    $('a.close').live('click', function(evt) {
    evt.preventDefault();

    $(this).closest('div.collapsable').slideUp();
    });
    });

  9. Avatar for Ali
    Ali December 25th, 2009

    as @Bobby mentioned Y not just use a costume attribute :
    <div id="related-results">
    <h2>Blah blah blog</h2>
    [hide]
    </div>

    and :
    $(function() {
    $('a.close').click(function() {
    $('#' + $(this).attr('tergetID')).slideUp();
    });
    });

  10. Avatar for Joe Chung
    Joe Chung December 25th, 2009

    There is an HTML 5 element to do that sort of thing (DETAILS), but none of the browsers support it the last time I checked so its specification isn't that useful. It's similar to the header content control in Silverlight.
    The rel attribute has some well-defined, well-observed semantics in HTML, e.g., used to distinguish LINK elements for CSS and RSS/ATOM content, and, as you noted, search engines heed it as well.
    The Visual Studio Web developer tools paid attention to nofollow, which made it handy for using externally hosted stylesheets in a page without interfering with the design surface.

    &lt;link rel="stylesheet nofollow" type="text/css" href="yui.yahooapis.com/.../reset-min.css" /&gt;

    I don't know if this behavior is documented anywhere (probably is). I just happened across it last year while experimenting with YUI's CDN-hosted CSS.

  11. Avatar for Abe Miessler
    Abe Miessler December 25th, 2009

    I'll have to give it a shot.

  12. Avatar for Cengiz Han
    Cengiz Han December 25th, 2009

    I am using css clasess to identify this kind of divs (or whatever)
    and jQuery does the adding element and handling event job.
    A sample; (same formatted code http://codepaste.net/jkzdha )
    <div id="related-results" class="auto-closable-div">
    <h2>Blah blah blog</h2>
    </h2>
    </div>
    $(function() { //ready

    $('.auto-closable-div').each(function() {
    var thisDiv=this;
    var hideAnchor= document.createElement("a");
    $(hideAnchor).attr("title", "hide this div")
    .attr("href", '#')
    .text("[Hide]")
    .appendTo(thisDiv)
    .click(function() {
    $(thisDiv).slideUp();
    return false;
    });
    });

    }); //ready

  13. Avatar for Martin S.
    Martin S. December 25th, 2009

    The slideUp effect is great!
    On a side note tho:
    One should really know it sets the style of ALL matching elements to display: none - that's great to know when debugging.
    I ended up using hide() instead, not as smooth but solved a problem for me.

  14. Avatar for willvv
    willvv December 25th, 2009

    Nice job Phil, although as some of the comments say, maybe you can avoid the "bastardization" of the rel attribute by specifying a class on the container div that must be hidden and use the closest() function.
    I think this approach is better than using a custom attribute (at least you have valid markup).
    Thanks for the tips :P especially on x-mas :P

  15. Avatar for Salman Farsi
    Salman Farsi December 25th, 2009

    Hi,
    Cool piece of work. Really it is better approach rather using custom attributes.
    Regards

  16. Avatar for Chris Airey
    Chris Airey December 25th, 2009

    That is how I would normally do it, but why use rel? JQuery happily works with custom attributes. The attribute can have a descriptive name also such as "Target". You have also limited it to the use the ID of the element. I would have the attribute handle anything ".class", "#id" etc...
    The parent div selector can work, if it's always going to be the parent. If it's to be reusable, you may not always desire that effect.

  17. Avatar for Collin
    Collin December 25th, 2009

    Rather than introducing custom attributes or possible standard conflicts why not make use of the already in use, href attribute? The hash is both a link to a page reference and a jQuery id selector, so how about this?
    HTML:

    <table id="test-table">
    <tr id="row1">
    <td>Col 1 | Row 1</td>
    <td>Col 2 | Row 1</td>
    </tr>
    <tr id="row2">
    <td>Col 1 | Row 2</td>
    <td>Col 2 | Row 2</td>
    </tr>
    <tr id="row3">
    <td>Col 1 | Row 3</td>
    <td>Col 2 | Row 3</td>
    </tr>
    </table>
    Hide Row 2 |
    Hide Row 3

    Javascript:

    $('a.close').click(function(event) {
    var item = $($(this).attr('href'));
    if(item.length != 1) { return; }
    item.hide(); // or slide, fade, etc...
    return event.preventDefault();
    });

    This is great for mapping ids to actions but not so great for more complex jQuery selectors.
    The preventDefault() may or may not be great for event propagation but it does work great for basic function calls and won't tag your url incorrectly with a non-existent anchor reference.

  18. Avatar for Collin
    Collin December 26th, 2009

    Those two links should have read:

    &lt;a href=&quot;#row2&quot;&gt;Hide Row 2&lt;/a&gt; |
    &lt;a href=&quot;#row3&quot;&gt;Hide Row 3&lt;/a&gt;

    Sorry about that.

  19. Avatar for Cengiz Han
    Cengiz Han December 26th, 2009
  20. Avatar for Cengiz Han
    Cengiz Han December 26th, 2009

    okaay :) I am not spamming:)
    I just created a little sample for you.
    http://codepaste.net/jkzdha
    p.s. : don't publish this one :)

  21. Avatar for haacked
    haacked December 26th, 2009

    Hi all. The reason I don't use the parent DIV is it's very likely that the link is not within the DIV I want to close. It just happened to be in this case.
    However, I do like using the hash of the element. I'll try that out.

  22. Avatar for rana
    rana December 26th, 2009

    Nice tips. Liked it...

  23. Avatar for Tim Murphy
    Tim Murphy December 26th, 2009

    Nice Job.
    How about adding unhide once hidden?

  24. Avatar for haacked
    haacked December 26th, 2009

    I forgot to mention, the reason I don't just add a custom attribute like "target-id" to the anchor tag is I want my mark-up to be valid XHTML.

  25. Avatar for Paul
    Paul December 26th, 2009

    Your code looks good, but I would use slideToggle() apposed to slideDown(). This way the user can also show the content after it is hidden.
    $(function() {
    $('a.close').click(function() {
    $($(this).attr('href')).slideToggle();
    return false;
    });
    });
    You can also add a couple CSS classes to give the user some graphical element and idea visual that the links can be used to show and hide text. Create a class for .close and one for .show with images like + and - then do something like this:
    //Switch the "Open" and "Close" state per click
    $('a.close').toggle(function(){
    $(this).addClass("show");
    }, function () {
    $(this).removeClass("show");
    });
    //Slide up and down on click
    $('a.close').click(function(){
    $(this).next(".toggle_container").slideToggle();
    });

  26. Avatar for Paul
    Paul December 26th, 2009

    One fix in the code example:
    //Slide up and down on click
    $('a.close').click(function(){
    $($(this).attr('href')).slideToggle();
    });

  27. Avatar for Ali
    Ali December 27th, 2009

    Good trick !

  28. Avatar for SS
    SS December 27th, 2009

    Will this jquery work inside a ajax updatepanel (aspx page).
    I had an issue with show/hide using ddaccordion jquery script not working inside updatepanel...
    please le me know any suggestions

  29. Avatar for Craig
    Craig December 27th, 2009

    @SS, I had a similar problem using some of the jQuery UI stuff, where it broke the UpdatePanel. The reason is that when the jQuery UI generates the required HTML elements, it often moves and appends content to the body of the document (at the bottom), placing them outside of your form tag. This breaks the AJAX stuff, and probably some regular postback stuff too. (Didn't investigate that.) Most of the above samples should work fine with UpdatePanel, because they do not move form fields around.
    I was using the dialog() functionality, and the only workaround I found that allowed me to use the jQuery UI with UpdatePanel was the terrible hack of creating hidden fields outside of the dialog div (that's a regular control with "display:none" set, not an actual "hidden" field, which ASP.NET does not render on the client) and linking the fields in the dialog to the hidden fields, so that their values updated as the dialog fields did. I then referenced the hidden fields on the server side so that the UpdatePanel continued to work as expected.
    I didn't use that hack in the final code. I removed the UpdatePanel and did jQuery AJAX requests to a web service instead. Much cleaner.

  30. Avatar for Sumit Thomas
    Sumit Thomas December 27th, 2009

    Nice one Phil!
    I've been using this technique for a while. I agree with some of the comments. I would prefer showToggle() to slideUp()
    Check out this post 2leggedspider.wordpress.com/...
    Cheers,
    Sumit

  31. Avatar for rtpHarry
    rtpHarry December 27th, 2009

    Hey Phil - I tried to submit the following via your contact form - I dont know if you have tried it since the blog was wiped out but it failed with an error screen (also you don't have detail error messages turned off).
    The original message I sent is below:
    Hey, just a couple of typo's in your latest article (jQuery Hide/Close Link). I didn't post them in the public comments because i am never sure if it comes off as being petty :P
    "hooking up each and very link" - very should be every.
    "The following HTML shows a DIV element which contains a link (with the link text “[hide]” that when clicked, should hide the DIV." - no closing bracket & anchor text is not [hide] in the sample you refer to.
    Merry Christmas anyway Phil; the closest I came to working on xmas day was to install software on to my new laptop so well done!

  32. Avatar for haacked
    haacked December 27th, 2009

    @rtpHarry thanks for the corrections. Corrections are always appreciated! :)

  33. Avatar for Dan Atkinson
    Dan Atkinson December 28th, 2009

    Just echoing what others have said regarding the use of slideToggle, instead of slideUp.

  34. Avatar for rcastagna
    rcastagna December 28th, 2009

    Hey, Phil...I can definitely see the need for a close, but why not use the .toggle() which would allow for showing the hidden div again. I know, it starts to approach a rudimentary accordion, but I would hate to have to reload an entire page just to get the hidden content back into view.
    Just my two cents...after taxes, probably worthless!
    Ric

  35. Avatar for Brian
    Brian December 28th, 2009

    I personally don't like the use of the href attribute here particularly in terms of unobtrusiveness. The link has two different behaviours depending on whether I have javascript enabled or not. With script it hides a div. Without script it scrolls to the div. Not exactly in tune.
    You have two scenarios when wanting to show/hide elements like this.
    1. You want to hide the div with and without script: therefore the href must point to a url with a querystring for example that allows the server side to hide it and for jquery you have a class value of something like "close id-1234" so jquery can assign the hide functionality to all links that have a class of close and that hide function can get the id by parsing out the id-1234 bit.
    2. You want to hide the div when script is on and dispense with the functionality with it off: Here you mark the closeable divs with a class of "closeable" for example and use jquery to add the links on the fly after each one.

  36. Avatar for Rob Grant
    Rob Grant December 28th, 2009

    2 ways:

    <div>
    hide

    stuff stuff stuff


    </div>

    1) if you can, traverse the document structure relatively. Definitely the best way:

    $(".close").click(function(){
    $(this).next().hide();
    });

    2) otherwise, use an id naming convention, so each area has a unique id, and each link has the same id with an appended salt (in my code example I've prepended "hides-").
    When they click the hide link, strip the "hides-" from the id and hide the resulting text.

    $(".close").click(function(){
    var tohide = $(this).attr("id");
    $("#" + tohide.substring(7)).hide();
    });

  37. Avatar for Steven
    Steven December 30th, 2009

    There is a problem with this solution - with javascript turned off (or in a screen reader), the link directs to the element that you actually intended to hide. For this reason, I much prefer Aaron's solution.

  38. Avatar for Jagdip Ajimal
    Jagdip Ajimal January 1st, 2010

    The solution is not bad, but not 100%. For example, I have 3 divs that can be shown/hidden. When I use href's, the slide happens nicely. But the page also focuses onto the link (even with the return false). This causes the use to be confused.
    I am going to use a custom tag instead.
    Another issue that was mentioned above was ASP.NET AJAX and the UpdatePanel. When you place jQuery in the UpdatePanel, the jQuery will only work on the first pageload. If you do a postback, then the jQuery breaks. I have a solution for this as follows:

    On the Page:

    <h4>Conditions Precedent (Show)</h4>
    <div id="ShowHide1">
    Hide me.
    </div>


    In my master page:
    <script type="text/javascript">
    $(document).ready(function() {
    attachHideShowLinks();
    // Need the following to ensure the
    // jQuery works after an ajax postback
    Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler);
    function EndRequestHandler(sender, args) {
    attachHideShowLinks();
    }
    });

    function attachHideShowLinks() {
    //Need unbind to ensure anything outside of the UpdatePanel
    //doesnt get the jQuery attached twice
    $(".HideShowLink").unbind();
    $(".HideShowLink").click(function() {
    $(this).html() == "Show" ? $(this).html("Hide") :
    $(this).html("Show");
    $($(this).attr('href')).slideToggle("normal");
    });
    }
    </script>

  39. Avatar for David Rhodes
    David Rhodes January 5th, 2010

    We do something similar but use the jQuery metadata plugin to store the element to close with the link class, like so
    // Expandable Content
    // eg class="expandable {ctrl:'ID OR CLASS OF ELEMENT TO TOGGLE'}"
    // class="expandable {ctrl:'.expandableContent'}"
    $(".expandable").click(function() {
    $($(this).metadata().ctrl).toggle();
    });

  40. Avatar for David Rhodes
    David Rhodes January 5th, 2010

    Oops, missed an example
    <div id="hide-this">
    This here DIV will be hidden when you click on
    the link
    </div>
    This is the link that hides the DIV

  41. Avatar for David Rhodes
    David Rhodes January 5th, 2010

    [code]<div id="hide-this">
    This here DIV will be hidden when you click on
    the link
    </div>
    This is the link that hides the DIV[/code]

  42. Avatar for Tim Hobbs
    Tim Hobbs January 8th, 2010

    Got in late to the post, but I think I see a few suggestions for toggle rather than hide. I'd have to agree. Much like your new "undo" plug-in, your user may wish to show what they just hid.

  43. Avatar for Hugo
    Hugo May 18th, 2010

    While fun to play around with, I tell other developers to only implement jQuery show/hide if they really need to. It must serve a purpose on your page other than to just show-off. Because, it can compromise page load speeds to some extent.

  44. Avatar for oempak
    oempak May 27th, 2010

    now this is what I'm looking for, thanks :)

  45. Avatar for 71Cinemax
    71Cinemax August 3rd, 2010

    I've been using this technique for a while. I agree with some of the comments.

  46. Avatar for alibaba
    alibaba November 23rd, 2012

    Thanks for such a great article here. I was searching for something like this for quite a long time and at last I’ve found it on your blog.

  47. Avatar for jarvis
    jarvis May 13th, 2013

    i am facing a problem here : i am using php while loop to display the boxes ... now the problem is that all of them hav a same id "post" which i want to toggle now can u help me with that

    while($row = mysql_fetch_assoc($result))
    {
    echo'<div id="con_1">
    <div id="h1">'. $row['cat_name'] .':<cl>Hide</cl></div>
    <div id="box">
    <div id="post">
    <div id="post_left">
    '. $row['post_sub'] .'
    </div>
    <div id="post_right">
    '. $row['user_name'] .'::::::::::::

    '. $row['post_date'] .'::::::::::   '. $row['post_time'] .'::::::::::::
    </div>
    </div>
    </div>
    </div>';
    }

  48. Avatar for jarvis
    jarvis May 13th, 2013

    well it is fine found the sollution on my own!!! :) thanks by the way for the nice code!!!

  49. Avatar for haacked
    haacked May 13th, 2013

    Glad you figured it out. By the way, having multiple elements with the same "id" is invalid HTML. You really shouldn't do that as it can cause all sorts of problems down the road. Use class="post" instead. :)

  50. Avatar for Robert
    Robert February 22nd, 2014

    Its been years since you wrote this this post. But I just found it. And it just helped me to solve a problem I was trying to solve for almost 5 hours. Thank you!