jQuery Tipsy won't work with jQuery.each() and live:true
Asked Answered
A

5

2

Note: This question was marked as solved once, but it figured out that upgrading to the latest jQuery was fixed only one issue. Please see the updated question below for the remaining issue.

Hi all,

I have just run into a weird issue with jQuery.Tipsy.

Here's a simplified demo fiddle: http://jsfiddle.net/6nWtx/7/

As you can see, the lastly added a.tipsy2 element does not get tipsyfied. The .tipsy2 elements are being tipsyfied within a jQuery.each() function and at this point I have the problem. Without the each() it works. Unfortunately, I need .each() to iterate through the elements to do some other stuff before I call tipsy().

Any suggestion?

Here's the source code of Tipsy: https://github.com/jaz303/tipsy/blob/master/src/javascripts/jquery.tipsy.js

IMHO the problem is using the combination of jQuery.each() and Tipsy option live:true

Update:

The other stuff I want to do before calling .tipsy() is checking for some optional configuration.

For example: <a href="#" title="This is a tooltip" class="tipsyfy delayed">Help</a>"

In this example I will add the following option to Tipsy: delayIn:1000 If there is no delayed class associated to the element this parameter will be delayIn:0.

Using the same logic, I want to specify the following classes as well: show-top, show-left, show-right, show-bottom for the Tipsy option called gravity.

Example: <a href="#" title="This is a tooltip" class="tipsyfy delayed show-left">Help</a>"

The full code:

$(".tipsyfy").each(function () {
    var a = "s",
        b = 0;
    if ($(this).hasClass("show-left")) a = "w";
    else if ($(this).hasClass("show-down")) a = "n";
    else if ($(this).hasClass("show-right")) a = "e";
    if ($(this).hasClass("delayed") && $(this).attr("data-delayIn") != null) b = $(this).attr("data-delayIn");
    $(this).tipsy({
        gravity: a,
        fade: true,
        live: true,
        delayIn: b
    })
})

And here is a full jsFiddle demo with all the stuffs I want to do: http://jsfiddle.net/xmLBG/1/

Almonte answered 2/3, 2012 at 9:31 Comment(2)
And i suppose you need to iterate over those elementsDilative
Yes, I want to do some other stuff with each element.Almonte
J
1

If you use jQuery 1.7.1 instead of 1.6.4 it will work. Maybe that live feature is relying on something buggy with the older versions, or some not-yet-implemented feature.

Update: from what I understood, you want the tipsy plugin to be called to every element with the .tipsyfy class, present now or added in the future. You don't want to (or can't) call it explicitly before insertion. You're trying to accomplish that using the live option of the plugin. Is that right?

If that's the case I can offer a workaround. I tried to use on (since jQuery's live is deprecated) to bind some code to the load event, but it didn't work, so I bound it to mouseenter and checked whether or not the plugin was already built for that element. If not, it builds it and re-triggers the event.

$(document).on("mouseenter", ".tipsyfy", function(e) {
    if ( !$(this).data("tipsy") ) {
        e.preventDefault();
        var a = "s",
            b = 0;
        if ($(this).hasClass("show-left")) a = "e";
        else if ($(this).hasClass("show-down")) a = "n";
        else if ($(this).hasClass("show-right")) a = "w";
        if ($(this).hasClass("delayed") && $(this).attr("data-delayIn") != null) b = $(this).attr("data-delayIn");
        $(this).tipsy({
            gravity: a,
            fade: true,
            live: true,
            delayIn: b
        }).trigger("mouseenter");
        return false;
    }
});            

Live example at jsFiddle.

For a small optimization, if the sole purpose of the .tispsyfy class is to instruct the plugin creation, and you don't need it afterwards, you can remove it prior to re-triggering the mouseenter. This way the checking code won't be called over and over again:

$(this).tipsy({...}).removeClass("tipsyfy").trigger("mouseenter");
Joyner answered 2/3, 2012 at 9:44 Comment(5)
OMG! Soooo easy. Thanks a lot!Almonte
Because I'm using classes to fine-tune the tipsy behaviour of specific elements. E.g.: <a class="tipsyfy top delayed" href="#">Tipsy showed delayed at the top</a> and <a class="tipsyfy" href="#">Tipsy default</a>Almonte
Hi. Thanks again for your suggestion but it figured out that upgrading to the latest jQuery will not fix all the issues. See my updated question and the updated jsFiddle demo. Thx in advance if you could help me.Almonte
I updated the answer with a workaround that should do what you want. AFAIK there's no way to use live or on with the load event, so this is the only way I could think of (short of manually calling .tispy() in the ajax callback).Joyner
That's a solution! The bounty is yours. Thx a lot! :)Almonte
I
0

As far as I can see, you don't need to iterate the nodelist. It looks like tipsy does that for you (see this jsfiddle, where in the first list every element gets its own tooltip (1,2,3).

Iberia answered 2/3, 2012 at 9:41 Comment(6)
That's because tipsy does the iteration for you, so if you iterate 'manually', it will interfere with that. See the edited jsfiddle (jsfiddle.net/KooiInc/B2qDN)Iberia
Thanks, but it was the older version of jQuery. Updating it to the latest solved the problem.Almonte
I don't think it's the version, but the inner workings of tipsy. To demnostrate, I updated the jsfiddle to demonstrate that each works with jQuery 1.6.4Iberia
Okay, once again. I wanted to iterate through each item that has a class .tipsyfy. Then on each item I wanted to do some other stuff before I call $(this).tipsy(...); and that didn't work with jQuery 1.6.xAlmonte
In that case removing the live property in the assignment within each would solve the problem for 1.6.4. Jsfiddle is updated again, just fyi.Iberia
As I stated in the question I must have the live option set to true. Anyway, thx for your effort but upgrading to a newer jQuery solved my issue.Almonte
T
0

Can't you do this instead? It is what you are asking.

$(".tipsy1,.tipsy2").tipsy({live:true,fade:true});
$(".tipsy2").each(function() {
    //do your stuff
});
Toxoid answered 2/3, 2012 at 9:48 Comment(0)
K
0

KooiInc is right,

<a class="tipsy1" href="#" title="Tipsy">TipsyLink</a>
<a class="tipsy1" href="#" title="Tipsy">TipsyLink</a>
<a class="tipsy1" href="#" title="Tipsy">TipsyLink</a>
<br />
<div id="container"></div>
<input id="add" type="button" value="ok">

And

$(".tipsy1").tipsy({live:true,fade:true});
$(".tipsy2").tipsy({live:true});
$("#add").click(function() {
    $("#container").append('<a class="tipsy2" href="#" title="Tipsy">TipsyLink</a>');
});

That will work fine

Kovach answered 2/3, 2012 at 9:50 Comment(0)
T
0

My guess is that Tipsy are uses some kind of direct mapping to the result, not using the live (in 1.6) or on in newer versions of jQuery.
So when your trying to apply the plugin to the links with the class tipsy2 it cant find any (cause your adding it to the DOM at a later stage in your code). The easiest fix to this is just to run the tipsy function at a later stage, perhaps on document.ready.

// this works
$(".tipsy1").tipsy({live:true,fade:true});

// add new tipsy element (ok)
$(document.body).append('<a class="tipsy1" href="#" title="TipsyAjax">AjaxTipsy1</a><br/>');

// add new tipsy element (not ok)
$(document.body).append('<a class="tipsy2" href="#" title="Tipsy">TipsyLink</a>');

$(document).ready(function () {
    $(".tipsy2").each(function(){
       // I'm doing some other logic here before I call .tipsy()
       $(this).tipsy({live:true,fade:true});
    })
});

(http://jsfiddle.net/8dg6S/7/)

Tremml answered 7/3, 2012 at 8:48 Comment(5)
Okay, one step further. But, the new elements will be added with some ajax call so after the document.ready callback run. If I add the new element after that it still not working, see: jsfiddle.net/8dg6S/8Almonte
Couldnt you run the tipsy plugin on each of the result sets you'll get back from your ajax call?Tremml
I shouldn't do that. That's why the live methods were introduced. But of course, if there's no proper solution, I'll do that.Almonte
I cant think of a better solution :\. Maybe you could look into how the Tipsy plugin is written and take a fork of it and modify it to your needs? It might be abit more work, but maybe it will payoff in the end...Tremml
Yeah, I'm doing it right now. Trying to figure out how does it work and where the problem sleeps... that's why the 50 bounty. Maybe someone can find it.Almonte

© 2022 - 2024 — McMap. All rights reserved.