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]

Text1

Text2[/html]

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...

[js]$('b').filter(":not(p > b)") // both are selected. $('b').not('p > b') // both are selected. [/js]

Any hints?

Answer

Try this:

[js]$('b').filter(function() { return !$(this).parent('p').length; });[/js]

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:

[js]$('#mySelect option').filter(function() { return parseInt(this.value, 10) > 5; });[/js]

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:

[js]$('.delete').click(function() { $.post("test2.php",{id:txt},function() { this.something }; });[/js]

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:

[js]$('.delete').click(function(){ var $thisDelete = $(this); $.post("test2.php",{id:txt},function(){ $thisDelete.somejQueryMethod(); }); }); [/js]

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...

[js]var currImage = $(this).css('backgroundImage'); $(this).css({backgroundImage: currImage == 'up-arrow.jpg' ? 'down-arrow.jpg' : 'up-arrow.jpg'}); [/js]

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

[js]// vertically center something in the viewport // make it a plugin! (function($) { $.fn.vCenter = function(options) { var pos = { sTop : function() { return window.pageYOffset || document.documentElement && document.documentElement.scrollTop || document.body.scrollTop; }, wHeight : function() { return window.innerHeight || document.documentElement && document.documentElement.clientHeight || document.body.clientHeight; } }; return this.each(function(index) { if (index == 0) { var $this = $(this); var elHeight = $this.height(); var elTop = pos.sTop() + (pos.wHeight() / 2) - (elHeight / 2); $this.css({ position: 'absolute', marginTop: '0', top: elTop }); } }); }; })(jQuery); // end plugin // center an element - could go in your custom js file $(document).ready(function() { $('#foo').vCenter(); });[/js]

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.