Questions and Answers from the List

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> 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.     };
  19.     return this.each(function(index) {
  20.       if (index == 0) {
  21.         var $this = $(this);
  22.         var elHeight = $this.height();
  23.         var elTop = pos.sTop() + (pos.wHeight() / 2) - (elHeight / 2);
  24.         $this.css({
  25.           position: 'absolute',
  26.           marginTop: '0',
  27.           top: elTop
  28.         });
  29.       }
  30.     });
  31.   };
  32.  
  33. })(jQuery); // end plugin
  34.  
  35. // center an element - could go in your custom js file
  36.  
  37. $(document).ready(function() {
  38.   $('#foo').vCenter();
  39.  
  40. });

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.

20 Responses to “Questions and Answers from the List”

  1. Oliver Says:

    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 Says:

    -
    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. Karl Says:

    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 Says:

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

  5. Karl Says:

    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 Says:

    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. Karl Says:

    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. yaph Says:

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

  9. Susan Says:

    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. theperson10 Says:

    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 Says:

    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. Dodo Says:

    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. çeviri Says:

    thanks for codes

  14. pablo Says:

    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");
    
    	});
    
    });
    
  15. Karl Says:

    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.

  16. pablo Says:

    Thank you very much Karl, I really appreciate it

    pablo

  17. pablo Says:

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

    Thanks

    pablo

  18. Karl Says:

    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.

  19. pablo Says:

    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

  20. josedelaranda Says:

    Thanks for your code. Cheers from Peru

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <pre> <em> <i> <li> <ol> <strike> <strong> <ul>

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> Also, use &lt; instead of < and &gt; instead of > in the examples themselves. Otherwise, you could lose part of the comment when it's submitted.