Javascript : Tippy.js not working for dynamic content
Asked Answered
K

4

10

I have the following tippy where on hover an Ajax call goes and fetches the data, Creates the content and shows it on. But it does not work for dynamic content because on page load the

<span class="more-tags otherPostTags" data-postId="{{$post->id}}">...</span>

comes static on page, But also it comes dynamic in a tab.

So the below code works for static

<span class="more-tags otherPostTags" data-postId="{{$post->id}}">...</span>

but does not work for dynamic.

<div id="template" style="display: none;">
    Loading a new image...
</div>

<span class="more-tags otherPostTags" data-postId="{{$post->id}}">...</span>

Tippy jquery :

const template = document.querySelector('#template');
const initialText = template.textContent;

const tip = tippy('.otherPostTags', {
    animation: 'shift-toward',
    arrow: true,
    html: '#template',
    onShow() {
        const content = this.querySelector('.tippy-content')
        if (tip.loading || content.innerHTML !== initialText) return
        tip.loading = true
        node = document.querySelectorAll('[data-tippy]');
        let id = node[0].dataset.postid;
        $.ajax({
            url: '/get/post/'+id+'/tags',
            type: 'GET',
            success: function(res){
                let preparedMarkup = '';
                res.tags.map(function(item) {
                    preparedMarkup +=
                        '<span class="orange-tag" style="background-color: '+item.color+'">'+
                            item.name +
                        '</span>';
                });
                content.innerHTML = preparedMarkup;
                tip.loading = false
            },
            error: function(error) {
                console.log(error);
                content.innerHTML = 'Loading failed';
                tip.loading = false
            },
        });
    },
    onHidden() {
        const content = this.querySelector('.tippy-content');
        content.innerHTML = initialText;

    },
    popperOptions: {
        modifiers: {
            preventOverflow: {
                enabled: false
            },
            hide: {
                enabled: false
            }
        }
    }
});

What am i missing here ?

Knopp answered 18/9, 2018 at 8:16 Comment(7)
do you see any error in the console?Pentalpha
@Pentalpha No, No errors in the console.Knopp
can you create a jsfiddle or something similar?Pentalpha
@Pentalpha Jsfiddle is not possible because the Api is on my local, Also loop instead of ajax will not work in that case.Knopp
difficult to debug in this case. One question why have you used[data-tippy] to obtain the id instead of [data-postId]Pentalpha
@Pentalpha Because its in their documentation like that, I tried that before but didn't worked that way.Knopp
I have created a codepen and used the same code as above and it works perfectly fine apart from the fetching data part. codepen.io/anon/pen/QVJLQz. Also I hardcoded the post idPentalpha
B
6

If you want Tippy to activate on new elements, you need to use event delegation. The Tippy documentation covers this (frustratingly, without an anchor to link to; search for "event delegation"). You use a parent container element and then tell Tippy what selector to use to match child elements. The example from the docs is:

tippy('#parent', {
  target: '.child'
})

...so for your example, use the container all the .otherPostTags elements are in (document.body in the worst case) and use .otherPostTags as the target:

tippy('selectorForParentElement', {
  target: '.otherPostTags'
});

Live Example:

tippy('#container', {
  target: '.otherPostTags'
});
var counter = 0;
var timer = setInterval(function() {
  ++counter;
  var tag = document.createElement("span");
  tag.title = "Tip for span #" + counter;
  tag.className = "otherPostTags";
  tag.innerHTML = "Span #" + counter;
  document.getElementById("container").appendChild(tag);
  if (counter === 6) {
    clearInterval(timer);
  }
}, 250);
.otherPostTags {
  color: white;
  background-color: #2020FF;
  border: 1px solid black;
  padding: 2px;
  margin-left: 2px;
  margin-right: 2px;
  border-radius: 4px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/tippy.js/2.5.4/tippy.css" rel="stylesheet"/>

<div id="container"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/tippy.js/2.5.4/tippy.min.js"></script>
Boring answered 18/9, 2018 at 8:24 Comment(5)
Thanks for the answer, I am hopeful now, Well i tried it but it didn't worked. Please extend your answer according to my code to save the world.Knopp
@Knopp - The second example is relevant to your code. Check that you're using a version of Tippy that has this feature; as the linked documentation says, it was first introduced in v2.1.Boring
I am using 2.5.4, So the version seems fine.Knopp
I have just tried it, Its not working. It gives me no error in the console. Also the tooltip is not showing and no request fires off. Need your help here :(Knopp
@Knopp - I've added a live example to the answer.Boring
M
17

This changed in a newer Tippy Version.

You need to import the delegate method from tippy.js:

import { delegate } from 'tippy.js';

Than initiate your tippy tooltips with the delegate method on a root element and set the selector of the elements that will have the actual tooltips via the target prop:

delegate( '#root', {
  target: '[data-tippy-content]'
} );

Make sure that the #root element actually exists in your App or use anything else like body for example. Then make sure to give the actual elements the data-tippy-content attribute or change the target selector accordingly.

Malfeasance answered 26/11, 2019 at 6:29 Comment(0)
B
6

If you want Tippy to activate on new elements, you need to use event delegation. The Tippy documentation covers this (frustratingly, without an anchor to link to; search for "event delegation"). You use a parent container element and then tell Tippy what selector to use to match child elements. The example from the docs is:

tippy('#parent', {
  target: '.child'
})

...so for your example, use the container all the .otherPostTags elements are in (document.body in the worst case) and use .otherPostTags as the target:

tippy('selectorForParentElement', {
  target: '.otherPostTags'
});

Live Example:

tippy('#container', {
  target: '.otherPostTags'
});
var counter = 0;
var timer = setInterval(function() {
  ++counter;
  var tag = document.createElement("span");
  tag.title = "Tip for span #" + counter;
  tag.className = "otherPostTags";
  tag.innerHTML = "Span #" + counter;
  document.getElementById("container").appendChild(tag);
  if (counter === 6) {
    clearInterval(timer);
  }
}, 250);
.otherPostTags {
  color: white;
  background-color: #2020FF;
  border: 1px solid black;
  padding: 2px;
  margin-left: 2px;
  margin-right: 2px;
  border-radius: 4px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/tippy.js/2.5.4/tippy.css" rel="stylesheet"/>

<div id="container"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/tippy.js/2.5.4/tippy.min.js"></script>
Boring answered 18/9, 2018 at 8:24 Comment(5)
Thanks for the answer, I am hopeful now, Well i tried it but it didn't worked. Please extend your answer according to my code to save the world.Knopp
@Knopp - The second example is relevant to your code. Check that you're using a version of Tippy that has this feature; as the linked documentation says, it was first introduced in v2.1.Boring
I am using 2.5.4, So the version seems fine.Knopp
I have just tried it, Its not working. It gives me no error in the console. Also the tooltip is not showing and no request fires off. Need your help here :(Knopp
@Knopp - I've added a live example to the answer.Boring
S
0

I was able to do it this way with Laravel:

@for($i = 0; $i < 10; ++$i)
  <br /> &nbsp;&nbsp;&nbsp;&nbsp;&lt;button data-tippy-content="&lt;img src='https://picsum.photos/300/300?random={{$i + 1}}' /&gt;">Text</button&gt;
  <br />
@endfor

<script>
  tippy('[data-tippy-content]', {
    trigger: 'click',
  });
</script>
Selfconfessed answered 24/9, 2022 at 7:53 Comment(0)
R
0

Tippy.js version 6. In the CDN (umd) version, it's available as tippy.delegate() https://atomiks.github.io/tippyjs/v6/addons/#event-delegation

Example:

tippy.delegate('parent', {
    target: '.child',
});
Rato answered 15/7 at 8:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.