Search code examples
javascriptjquerytooltipmaterialize

Unable to update Materialize.css tooltips using jQuery after click event


I am using Materialize.css on my portfolio site, and am using the npm package clipboard.js. I am using this within a floating action button, and the copy to clipboard functionality is working as intended (when user clicks the button, it copies my email to their clipboard like it should).

However, I want the tooltip to update from ""Click to copy my email to your clipboard!" to a success message like "Copied to your clipboard ✅". I have tried the code below and it won't actually update the page, though I can sometimes see the new message (it's just very inconsistent, which I don't want).

This is my html element:

<li>
 <a id="email" data-clipboard-text="[email protected]" class="btn-floating red waves-effect waves-light tooltipped" data-position="left" data-tooltip="Click to copy my email to your clipboard!"><i class="material-icons">mail</i></a>
</li>

and here's my javascript:

var clipboard = new ClipboardJS('#email');

    clipboard.on('success', function(e) {
        var anchorElement = $('#email');
        anchorElement.attr('data-tooltip', 'Copied to your clipboard ✅');
        anchorElement.addClass('success');
        anchorElement.tooltip();

        // Reset after a timeout
        anchorElement.mouseleave(function() {

            setTimeout( function(){
                anchorElement.attr('data-tooltip', 'Click to copy my email address to your clipboard!');
                anchorElement.removeClass('success');
                anchorElement.tooltip();
            }, 300);
        });

        e.clearSelection();
    });

I would like for the tooltip to show the updated value consistently, but I haven't been able to figure out what's wrong with the code I have. I can tell that it does update the html element, as I can sometimes see the updated text, but it's very inconsistent and for this feature to be worth using at all I need it to be very consistent.

Any help would be greatly appreciated!


Solution

  • The problem you're running into is that materialize.css adds in other DOM elements that eventually appear on screen as the tooltips themselves - elements that you aren't targeting right now - elements that you didn't write in your HTML. You're targeting the original data-attributes that materialize.css uses to create those other elements. Updating those data-attributes after render is too little too late... by then, the materialize.css library has already looked at those attributes and gotten what it needs.

    If you look at the official docs page on tooltips, we can investigate a little on that page. Open your dev console and scroll towards the bottom you'll see four DOM elements with the class material-tooltip - these were added by the library, and these are the DOM elements that actually get shown on screen.

    enter image description here

    Open those divs up and watch what happens to them when you mouse over the Bottom, Top, Left, Right buttons on the screen. As each tooltip appears, you should notice that the message-to-be-displayed gets injected as text into that element, and it animates some CSS properties.

    If you want to change the text being displayed, you can probably skip editing the data-attributes (though if the library occasionally refreshes the content, changing the attributes might still be a good idea)... instead, we need to edit the text being shown in ^^THESE^^ elements.

    If you only have one tooltip being displayed on that page, it should be as simple as something like this:

    $('.material-tooltip').innerText = 'Copied to your clipboard ✅'
    

    or if you have multiple on that page, you can attempt to identify which div is for which tooltip, but I suspect that could end up being unreliable. It would probably be safer to update ALL of the divs... the library will overwrite the content anyways next time it renders... which again, it gets from your data-attributes. Updating all of them would look something like:

    $('.material-tooltip').each( eachDiv => {
        eachDiv.innerText = 'Copied to your clipboard ✅'
    })