Frank
Frank

Reputation: 53

Optimize Jquery with loops

I have this code:

            $('.counter_d').mouseover(function() {
                   $('#description').html('Counter');
             });
            $('.selector_d').mouseover(function() {
                   $('#description').html('Selector');
             });
             $('.date_d').mouseover(function() {
                   $('#description').html('Date');
             });

and several more, but I think the file could be smaller and even reusable using loops, but I'm not able to bind the description (HTML method) with the selector.

I want to use something like this:

              var selectors=['.counter_d','.selector_d','.date_d'];
              var description=['Counter', 'Selector', 'Date'];


              for(var i=0; i<selectors.length; i++)
                 $(selectors[i]).mouseover(function() {
                   $('#description').html(description[i]);
                 });

Any help? Thanks

Upvotes: 4

Views: 169

Answers (6)

Tadeck
Tadeck

Reputation: 137360

Since correct solutions have already been given (Andrew Whitaker's accepted answer being my favorite), I will actually tell you what is the problem with your code (yoshi's answer has some clues, but does not explain it in detail).

The problem

The problem is, that even though the value of i changes within the loop, when it is executed, i stays the same (the same as when the for loop has ended) when event handlers are executed.

What really happens within your code

The proof looks like this (see jsfiddle):

var selectors=['.counter_d','.selector_d','.date_d'];
var description=['Counter', 'Selector', 'Date'];

for(var i=0; i<selectors.length; i++)
    $(selectors[i]).mouseover(function() {
        $('#description').html(i); // i is always equal to 3
    });​

The problem is event handlers use i from outer scope, which at the time of handlers' execution is equal to 3. So even though the i variable has the value you wanted when handlers are attached, the value changes before they are executed.

The solution

To solve that, you can slightly modify your code to use anonymous functions that are immediately called by passing it then-correct value of i:

var selectors=['.counter_d','.selector_d','.date_d'];
var description=['Counter', 'Selector', 'Date'];
for(var i=0; i<selectors.length; i++)
    (function(i){
        $(selectors[i]).mouseover(function() {
            // i is the same as when the outer function was called
            $('#description').html(description[i]);
        });
    })(i); // executing function by passing current i
​

To prove that it works correctly: http://jsfiddle.net/ZQ6PB/

See more

JavaScript closure inside loops - simple practical example,

Upvotes: 1

Beetroot-Beetroot
Beetroot-Beetroot

Reputation: 18078

Constructing the HTML as follows (assuming the elements are spans) ...

<span class="d counter_d" data-desc="Counter">...</span>
<span class="d selector_d" data-desc="Selector">...</span>
<span class="d date_d" data-desc="Date">...</span>

... allows the javascript to as simple as this :

var $description = $("#description");

$(".d").mouseover(function() {
    $description.text($(this).data('desc'));
});

The class names '.counter_d','.selector_d','.date_d' are redundant and can be deleted unless they are required for some other reason - eg. styling.

Of course, if you had hundreds of such elements, then the HTML would be a pain to write handraulically. You would be better off with another approach.

Upvotes: 0

nuala
nuala

Reputation: 2688

The problem is the variable i is assigned to 3 at the time the mouseover callback is executing.
As description[3] is undefined no new HTML is assigned.
Fiddle enable your browser console to read the console.log!

I think a more elegant solution is to give the HTML elements an extra attribute description and in the mouseover callback simply do:

$('#description').html($(this).attr("description"));

(You see it in the fiddle above)

In my opinion you can even select all elements in a more elegant way and get rid off the loop as jQuery will handle this for you:

$(".counter_d, .selector_d, .date_d").mouseover(function() {
    $('#description').html($(this).attr("description"));
});

updated Fiddle

Upvotes: 1

Maksim Vi.
Maksim Vi.

Reputation: 9225

I'd go for something like this:

var obj = {
    'a': 'content a',
    'b': 'content b',
    'c': 'content c'
};

$('.a,.b,.c').mouseover(function() {
    $('#d').html(obj[this.className]);
});​

DEMO

Don't really like loop idea, since it makes it much less readable.

UPD: You can always expand the solution for more classes

var obj = {
    '.a': 'content a',
    '.b': 'content b',
    '.c': 'content c'
};

var selector = Object.keys(obj).join(',');
$(selector).mouseover(function() {
    $('#d').html(obj['.' + this.className]);
});​

DEMO

Upvotes: 0

Ohgodwhy
Ohgodwhy

Reputation: 50787

It looks like your selectors all end with _d. It seems reasonable that we could do the following.

$('[class$=_d]').mouseover(function(){
    var str = $(this).prop('class').split('_')[0];
    var desc = str.charAt(0).toUpperCase() + str.slice(1);
    $('#description').html(desc);
});

Upvotes: 0

Andrew Whitaker
Andrew Whitaker

Reputation: 126052

var selectors = {
    '.counter_d': 'Counter',
    '.selector_d': 'Selector',
    '.date_d': 'Date'
};


$.each(selectors, function (key, value) {
    $(key).mouseover(function () {
        $("#description").html(value);
    });
});

Example: http://jsfiddle.net/andrewwhitaker/bS28q/

Upvotes: 8

Related Questions