Questions and Answers from the List

read 32 comments
I've been feeling guilty lately about my lack of posts to this blog. But when I looked at my profile for the jQuery Google Group and discovered that for the past six months I've posted an average of 100+ times each month, well, I decided to give myself a break. Since I'm sure some people who stumble upon this blog aren't subscribed to the Google group/mailing list, here are a few (edited) questions that have appeared there recently, along with my (edited) answers. I hope some of you find them helpful.

Question: Filter Function

I have this HTML:
HTML:
  1. <p>
  2.   <b>Text1</b>
  3. </p>
  4. <b>Text2</b>

How can I select the second <b>, which is not a child of a <p>? As I am a newbie, I played a little bit around with :not, filter(), but unfortunately...
JavaScript:
  1. $('b').filter(":not(p> b)") // both <b> are selected.
  2.  
  3. $('b').not('p> b')   // both </b><b> are selected.

Any hints?

Answer

Try this:
JavaScript:
  1. $('b').filter(function() {
  2.   return !$(this).parent('p').length;
  3. });

This finds all <b> elements and then filters them. The filter returns only those that do not (!) have a parent paragraph ( $(this).parent('p').length ).

Question: Filter by Option Value

I have several options in a select element, each whose value is an integer. I would love to be able to select those above a threshold, such as something like: $("#mySelect option[value>5]") I know I can do this with :gt(index) but I'd rather use the actual value for less hard coding and better readability. I think this would be great in other areas too. I'm sure there are lots of issues with this since the value is really just text, but a straight string comparison would probably do.

Answer

You could try something like this:
JavaScript:
  1. $('#mySelect option').filter(function() {
  2.       return parseInt(this.value, 10)> 5;
  3.   });

This should filter the option elements, selecting only those whose value is greater than 5.

Question: Puzzle of "this"

I am puzzled by the use of this somehow. For example:
JavaScript:
  1. $('.delete').click(function() {
  2.   $.post("test2.php",{id:txt},function() {
  3.       this.something
  4.     };
  5. });

Here, if I want to refer this to the $('.delete') element, what shall I write? In the debugger I found that this refers to the function body.

Answer

You could try this:
JavaScript:
  1. $('.delete').click(function(){
  2.     var $thisDelete = $(this);  
  3.   $.post("test2.php",{id:txt},function(){
  4.     $thisDelete.somejQueryMethod();
  5.   });
  6. });

The $thisDelete variable is now referencing the clicked element wrapped in a jQuery object. This is useful for attaching jQuery methods, chaining, etc. Oh, and by the way, the use of the "$" in $thisDelete isn't necessary, but it has become somewhat of a convention among jQuery developers, as it reminds us that we're dealing with a jQuery object. Now, when you use $thisDelete inside your $.post, it's still referring to the clicked element.

Question: Swap an Image on Click

I have a simple accordion set up, but I want to be able to change the header's background image from an up arrow state to down arrow state and back automatically. Any ideas on where to go from here?

Answer

After the .click() line [which slides the following div up or dow], you can do something like this...
JavaScript:
  1. var currImage = $(this).css('backgroundImage');
  2. $(this).css({backgroundImage: currImage == 'up-arrow.jpg' ? 'down-arrow.jpg' : 'up-arrow.jpg'});

The first line here stores the clicked header's background image in the "currImage" variable. The second line then sets the background image. It uses a ternary, or conditional, operator to check if the background image is "up-arrow.jpg". If it is, the image is set to "down-arrow.jpg"; if it isn't "up-arrow.jpg", the image is set to "up-arrow.jpg".

Question: How to Center a DIV Vertically in the Viewport

Thing is I got a project on which i want to show a preloader whenever something is sent or some event has been triggered. How could I set the preloader to show itself right on the middle of the screen?? I mean how could I show it on the right middle of the screen even if I got a huge scrollbar on the right side!?

Answer

So here is one way you could get vertical centering in the viewport [with sTop code borrowed from an old copy of Dimensions plugin]:
JavaScript:
  1. // vertically center something in the viewport
  2.  
  3. // make it a plugin!
  4. (function($) {
  5.   $.fn.vCenter = function(options) {
  6.     var pos = {
  7.       sTop : function() {
  8.         return window.pageYOffset
  9.         || document.documentElement && document.documentElement.scrollTop
  10.         ||  document.body.scrollTop;
  11.       },
  12.       wHeight : function() {
  13.         return window.innerHeight
  14.         || document.documentElement && document.documentElement.clientHeight
  15.         || document.body.clientHeight;
  16.       }
  17.     };
  18.     return this.each(function(index) {
  19.       if (index == 0) {
  20.         var $this = $(this);
  21.         var elHeight = $this.height();
  22.         var elTop = pos.sTop() + (pos.wHeight() / 2) - (elHeight / 2);
  23.         $this.css({
  24.           position: 'absolute',
  25.           marginTop: '0',
  26.           top: elTop
  27.         });
  28.       }
  29.     });
  30.   };
  31.  
  32. })(jQuery); // end plugin
  33.  
  34. // center an element - could go in your custom js file
  35.  
  36. $(document).ready(function() {
  37.   $('#foo').vCenter();
  38.  
  39. });

Take a look at the demo page I put together. Click on a "center" button, and see where the div goes. Scroll down and click on another button. The div still goes to the center of the viewable area. UPDATE: Because this didn't work properly in Opera 9.5 as reported in comment #4 below, I refactored the code for returning the scroll top and window height values. Please continue to report any errors that you discover. I also welcome suggestions for improvements to any of the code here.
comment feed

32 comments

  1. Oliver

    Thanks for all the hard work and these gems in particular. Much appreciated.

    Just one small critique: your code prettifier is over-zealously html-encoding characters.

  2. djot

    -
    Hi,

    I, for a long time, have the idea, to copy such "quick answers and solutions" from the forum and the irc also to a website as a repository to search for such things like above (selectors, ...)

    djot
    -

  3. Oliver, thanks for pointing out the problem with the code prettifier. I hope I have it fixed now. There were a few other issues as well with WordPress mangling the markup (again). Glad you liked the post. :)

  4. bullcrap0

    unfortunately your test page doesn't work with Opera 9.50...

  5. Hi bullcrap0,

    That is, indeed, unfortunate (as is your name ;) ).

    Apparently there was a problem with the way I was getting the viewport height. I stripped out the browser sniffing (not sure why it was in there in the first place, but I generally trust Brandon's code), downloaded and installed Opera 9.50 beta for Windows, and tested the page. I'm happy to report that it appears to be working in that browser now. I'll update the entry above to reflect the change in code.

    Thanks a lot for reporting this!

  6. Susan

    Karl

    I am subscribed to the google group and I love jQuery but I really struggle to modify small things. I have a cap frequency cookie function on a show hide slide toggle that doesn't use the jquery cookie plugin by Klaus. My cookie uses oneDay and I have tried everything I can think of to shorten the expire duration to less than 24 hours OR to have it reset at midnight. Any light you can shed would be most welcome. The working cookie show hide function is:

    $(document).ready(function() {
            initSlideboxes();
            function initSlideboxes()
    {
    if(!document.cookie.match("slidedToday")){
      var oneDay = new Date();
      oneDay.setDate(oneDay.getDate()+1);
      document.cookie="slidedToday=true;path=/;expires="+oneDay.toGMTString();
    
      $('#slidebar').slideDown(1000);
      setTimeout(function() {$('#slidebar').slideUp(10);}, 5000);
    }
            $('#slidebartrigger').click(function(){$
    ('#slidebar').slideToggle(); }); 
    
    };
    }); 
    
    A coworker who has not used jQuery suggested I try the following that doesn't work
    
    $(document).ready(function() {
            initSlideboxes();
            function initSlideboxes()
    {
    if(!document.cookie.match("slidedToday")){
      var oneDay = new Date();
      oneDay.setDate(oneDay.getDate()+1);
      document.cookie="slidedToday=true;path=/;expires="+oneDay.toGMTString();
    
      $('#slidebar').slideDown(1000);
      setTimeout(function() {$('#slidebar').slideUp(10);}, 5000);
    }
            $('#slidebartrigger').click(function(){$
    ('#slidebar').slideToggle(); }); 
    
    };
    });

    Thanks for all you do Karl!

  7. Hi Susan,

    Rather than use the "expires" attribute, you might want to try using "max-age" like so:

    var c = "slidedToday=true; max-age=" + (60*60*24) + "; path=/";
    document.cookie=c;

    The "max-age" attribute is measured in seconds, so here we're saying that the cookie is good for 24 hours, but you can set it for whatever time period you want.

    Hope that helps.

  8. Fantastic article with insights into jQuery. The vCenter plugin is a good demonstration on how to extend jQuery. Thanks!

  9. Susan

    Thanks Karl!

    I think I understand, and once I switched to my 2nd computer that had no cookies today I did see my slideDown slideUp just once but was still able to toggle. So I think I understood what I need to do

    I have

    if(!document.cookie.match("slidedToday")){
    var oneDay = new Date();
    oneDay.setDate(oneDay.getDate()+1);
    var c = "slidedToday=true; max-age=" + (60*60*24) + "; path=/";
    document.cookie=c;

    I do have another show hide that swaps images that wizzud helped me with on the jQuery group

    $(document).ready(function(){

    var slideTimeout // timer
    , sbTrigger = $('#slidebartrigger') // convenience
    , sbFirstSlide = true // indicates first time through
    ;
    function toggleSlideboxes(){

    if(slideTimeout) clearTimeout(slideTimeout);

    var isDown = sbTrigger.is('.closeSlide');

    $('#slidebar')['slide' + (isDown ? 'Up' : 'Down')]((isDown ? 3000 :
    1000), function(){

    if(sbFirstSlide){ sbTrigger.removeClass('firstSlide'); sbFirstSlide
    = false; }

    sbTrigger[(isDown ? 'remove' : 'add') +
    'Class']('closeSlide').one('click', toggleSlideboxes);

    if(!isDown) slideTimeout = setTimeout(toggleSlideboxes, 4000);
    });
    }
    toggleSlideboxes();
    });

    which swaps between a blank, close & replay tab GIF depending on where the slidebar is. Unfortunately I had no luck figuring out how to cap the frequency of my slide with this more complex show hide.

  10. for centering a div in the viewport, you might try the Thickbox plugin.
    it allows the coder to display various types of content including divs in a "dialog box"
    (for examples, follow the supplied link and scroll down to the examples section, which is red.

  11. DeaR

    Dear Karl,

    Thanx for all the knowledge you're sharing here. Great work.

    I always have some problems when using jQuery for Ajax and for other things. Maybe I'm not used to it yet, I'm always missing something.

    Let's see this one, I can't figure out why this one doesn't work. When I write script, I try to write everything and put them together in one file, but its doesn't work. see below

    function disappear() {
    $("#response").fadeOut("slow")
    }
    $(document).ready(function() {
    $("#message_me").click(function() {
    $("#form").fadeIn("slow")
    })
    $("#send").click(function() {
    var str = $("#input_form").serialize()
    $.post("send-mail.php",str,function(data) {
    $("#response").html(data).fadeTo("slow", 0.7)
    setTimeout("disappear()", 3000)
    })
    })
    $.get("counter.php", function(data) {
    $("#counter").html(data).fadeIn("slow")
    })
    })

    I saved the above script in a separate file, let's say "myscript.js". Then I imported it in my html file like this

    Then, when I run this, it's not working. I've been trying to find the error. It says "missing } in XML expression at $("#send").... but then I switched places between $("#send")... and $.get(..., then the error comes out like "missing } XML expression at $.get(... instead. So I don't know what's wrong, Is there any function that needs to be put in between somehow?

    Again, now I delete everything except for $.get(.... in myscript.js. And I put them in the html file manually like

    function disappear() {
    $("#response").fadeOut("slow")
    }
    $(document).ready(function() {
    $("#message_me").click(function() {
    $("#form").fadeIn("slow")
    })
    $("#send").click(function() {
    var str = $("#input_form").serialize()
    $.post("send-mail.php",str,function(data) {
    $("#response").html(data).fadeTo("slow", 0.7)
    setTimeout("disappear()", 3000)
    })
    })
    })

    Now then, it works fine.....

    Can you tell me why it's not working in the uppermost one. cuz I want to put everything in one script file, and not to show any script in the html file.

    Thank you very much for any responses, really appreciate your time.

    DeaR

  12. I did a quick search but didn't find anything relevant. I'm new to jQuery and I'm a PHP programmer in the spare time and a Java programmer at work. How much object oriented design does jQuery support? Could you perhaps elaborate on that? Thanks.

  13. pablo

    hi,
    I'm trying to figure out how to make this work with more then one paragraph,
    basically the second paragraph doesn't appear, i tried different combination that made it appear but once i closed the h2 and re-opened it the second paragraph would not show, i even had the same problem with a paragraph containing a floated image. the image came back but not the paragraph,

    if anyone could help me out, that would be great.

    thanks.

    HTML

    Latest Updates..

    Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aliquam leo. Fusce pharetra risus eu tellus. Ut turpis. Nunc congue lacinia dolor. Vivamus ipsum quam, egestas ac, luctus at, aliquam a, est. Donec fermentum enim id magna. Duis nisi orci, feugiat eget, imperdiet eu, condimentum id, leo. Donec cursus. Vivamus gravida nibh vitae dolor. Sed mattis. Pellentesque ante magna, egestas vel, pharetra in, vulputate ut, est. Quisque felis risus, iaculis in, blandit id, lobortis vel, purus.

    Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aliquam leo. Fusce pharetra risus eu tellus. Ut turpis. Nunc congue lacinia dolor. Vivamus ipsum quam, egestas ac, luctus at, aliquam a, est. Donec fermentum enim id magna. Duis nisi orci, feugiat eget, imperdiet eu, condimentum id, leo. Donec cursus. Vivamus gravida nibh vitae dolor. Sed mattis. Pellentesque ante magna, egestas vel, pharetra in, vulputate ut, est. Quisque felis risus, iaculis in, blandit id, lobortis vel, purus..

    header

    Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aliquam leo. Fusce pharetra risus eu tellus. Ut turpis. Nunc congue lacinia dolor. Vivamus ipsum quam, egestas ac, luctus at, aliquam a, est. Donec fermentum enim id magna. Duis nisi orci, feugiat eget, imperdiet eu, condimentum id, leo. Donec cursus. Vivamus gravida nibh vitae dolor. Sed mattis. Pellentesque ante magna, egestas vel, pharetra in, vulputate ut, est. Quisque felis risus, iaculis in, blandit id, lobortis vel, purus.

    JQUERY

    $(document).ready(function(){
    
    	$(".accordion h2:first").addClass("active");
    	$(".accordion p:not(:first)").hide();
    
    	$(".accordion h2").click(function(){
    
    	  $(this).next("p").slideToggle("slow")
    	  .siblings("p:visible").slideUp("slow");
    	  $(this).toggleClass("active");
    	  $(this).siblings("h2").removeClass("active");
    
    	});
    
    });
    
  14. Hi Pablo,

    The .next() method only selects the immediate next sibling. If all your h2 and p elements are siblings, you might need to use the nextUntil plugin:

    $.fn.nextUntil = function(expr) {
       var match = [];
    
       // We need to figure out which elements to push onto the array
       this.each(function(){
           // Traverse through the sibling nodes
           for( var i = this.nextSibling; i; i = i.nextSibling ) {
               // Make sure that we're only dealing with elements
               if ( i.nodeType != 1 ) continue;
    
               // If we find a match then we need to stop
               if ( jQuery.filter( expr, [i] ).r.length ) break;
    
               // Otherwise, add it on to the stack
               match.push( i );
           }
       });
    
       return this.pushStack( match, arguments );
    };

    Then you could do this:

      $(document).ready(function(){
    
      	$('.accordion h2:first').addClass('active');
      	$('.accordion h2:eq(1)').nextAll('p').hide();
    
      	$(".accordion h2").click(function(){
          var $theseParas = $(this).nextUntil('h2').slideToggle('slow');
    
        	$(this).toggleClass('active')
        	  .siblings("h2").removeClass('active').end()
            .siblings('p').not($theseParas).slideUp('slow');
      	});
    
      });

    A couple things: If you know the tag name of the element with class "accordion," you should probably use it in the selector. For example, "div.accordion" instead of just ".accordion." Also, you'll probably be better off if you wrap each group of paragraphs in its own div. It's easier to select a single div (no need for the nextUntil plugin) and the animation will be smoother.

  15. pablo

    Thank you very much Karl, I really appreciate it

    pablo

  16. pablo

    Hey karl, I noticed it doesn't work in ie6, works liek a charm in all the other browsers.

    Thanks

    pablo

  17. Hi Pablo,
    Do you have a page somewhere that I can look at? It will help me to troubleshoot for you if I can see what you're doing.

  18. pablo

    Hi Karl, everything is fine, it was basically an internet provider issue, sorry about te delay in replying...was out of town

    thanks again for the help

    hope all is well

    P

  19. Thanks for your code. Cheers from Peru

  20. Jon

    In going through the 'Learning jQuery' book, I did some experimenting with the content of 'Chapter 1/alice.html'. Specifically, I have this selector and statement...

    $(document).ready(function() {
    $('.poem-stanza > div:eq(1)').addClass('blue_txt');
    });

    The problem is that when this is applied only the FIRST INSTANCE of "second child div of class .poem-stanza" gets the new class. Please note that the alice.html file has TWO INSTANCES of '.poem-stanza > div:eq(1)'.

    YKCOWREBBAJ

    sevot yhtils eht dna ,gillirb sawT'
    ;ebaw eht ni elbmig dna eryg diD
    ,sevogorob eht erew ysmim llA
    .ebargtuo shtar emom eht dnA

    She puzzled over this for some time, but at last a bright thought struck her. "Why, it's a Looking-glass book, of course! And if I hold it up to a glass, the words will all go the right way again."
    This was the poem that Alice read.

    JABBERWOCKY

    'Twas brillig, and the slithy toves
    Did gyre and gimble in the wabe;
    All mimsy were the borogoves,
    And the mome raths outgrabe.

    QUESTION: Why does the 2nd instance get ignored (unprocessed)? How do I modify my statement so that the 2nd instance DOES get processed?

    THANKS!

    Jon

  21. Hi Jon,

    In that case, you might want to use $('.poem-stanza > div:nth-child(2)').addClass('blue_txt');

    Keep in mind that :nth-child(n) is 1-based, whereas all the other jQuery selectors are 0-based.

    Hope that helps.

  22. Maniquí

    I was having some issues with the vertical center plugin on IE7.
    For some reason, the content was succesfully being vertically centered, but also the width of the window was expanding (and so, adding an horizontal scroll bar).

    Finally, I did some changes in the plugin code and was able to fix it.
    The changes are this:

    //position: 'absolute',
    //marginTop: elTop,
    paddingTop: elTop
    //top: elTop

    As you can see, I commented out some lines and added the paddingTop.
    So far, it worked for me (on a particular situation) on IE6/7/Win, FF3, Safari/Win, Google Chrome.

    But, the question is: could you foreseen any problem with this change?
    Thanks.

  23. Maniquí

    Well, I've spotted a little bug in my approach (see comment above).
    Because of the way jquery.vcenter calculates the value for elTop, it can result on a negative value. Then, that negative value can't be used on paddingTop, because padding cannot have negative values.
    The workaround is easy. If elTop < 0, then set it to equal 0.
    Like this:


    [...]
    var elTop = pos.sTop() + (pos.wHeight() / 2) - (elHeight / 2);
    if (elTop < 0) {
    var elTop = 0;
    };
    $this.css({
    //position: 'absolute',
    //marginTop: elTop,
    paddingTop: elTop
    //top: elTop
    });
    [...]

  24. Adeel Shahid

    Is there a way by which I can find out the exact height of my webpage not the clientHeight from the quirks mode but the height which is cross browser compatible.

    But exact height I mean the complete height above the top scrolled area, area viewable and the area below which is yet to be scrolled.

  25. Hi Adeel. Try $(document).height();.

  26. Adeel Shahid

    Thanks Karl for the above comment but i was wondering a method outside jQuery in simple javascript,

    while here's a tribute to you, my first jQuery plugin - ripped from your post

    jQuery.fn.centerMe = function() {

    var sTop = function() {
    return window.pageYOffset || document.documentElement && document.documentElement.scrollTop || document.body.scrollTop;
    }

    var sLeft = function() {
    return window.pageXOffset || document.documentElement && document.documentElement.scrollLeft || document.body.scrollLeft;
    }

    var iHeight = function() {
    return window.innerHeight || document.documentElement && document.documentElement.clientHeight || document.body.clientHeight;
    }

    var iWidth = function() {
    return window.innerWidth || document.documentElement && document.documentElement.clientWidth || document.body.clientWidth;
    }

    return this.each(function(index) {
    var elmWidth = $(this).width();
    var elmHeight = $(this).height();
    var scrollTop = sTop();
    var scrollLeft = sLeft();
    var innerHeight = iHeight();
    var innerWidth = iWidth();

    var top = scrollTop + (innerHeight / 2) - (elmHeight / 2);
    var left = scrollLeft + (innerWidth / 2) - (elmWidth / 2);

    $(this).css({
    'position':'absolute',
    'top':top,
    'left':left
    });

    });
    }

  27. Rodrigue

    Hi Karl,

    very good post and quite useful to me as I was today trying to center an image on the screen, which is exactly what the answer to How to Center a DIV Vertically in the Viewport does.

    However the code snippet above has a syntax error. Line 17 should be removed, otherwise the curly bracket terminates the definition of vCenter. The code is correct on the jquery.vcenter.js used in the example page though.

    Thanks for all your very useful post all the same

  28. Good catch, Rodrigue! I updated the post with the fixed code.

  29. Drumsticks

    I, too, am having the same issue as DeaR.

    I keep getting "Missing } in XML expression." I looked over my JS code quite a bit, but can't seem to find anything wrong with it.
    It happens during the execution of the post function.

    Has anyone experienced this or may have any possible solution to this problem?

    Thanks.

  30. Thanks for all your very useful post all the same

Leave a Comment

IMPORTANT:

  • If you wish to post code examples, please wrap them in <code> tags.
  • Multi-line code should be wrapped in <pre><code> </code></pre>
  • Use &lt; instead of < and &gt; instead of > in the examples themselves. Otherwise, you could lose part of the comment when it's submitted.