Search code examples
jqueryfont-awesome-5

fontawesome attributes not updating with jquery


I'm using font-awesome SVG, I had some issues with scope earlier, but now I am having a new issue - I asked about this issue in a comment on my other question and was told it was an entirely separate issue.

Here is my current code:

$('body').on('click','.switchButton', function(){
    $(this).attr('data-prefix', 'fas').attr('data-icon', 'spinner').addClass('fa-pulse');
    $.ajax({
        url: 'tasks/update_table.php',
        type: 'post',
        data: { "uid": $(this).attr('data-uid')},
        context: this,
        success: function(response) { 
            $(this).attr('data-icon', 'check-square').removeClass("fa-pulse");
            if(response == 1) {
                $(this).attr('data-prefix', 'far');
            } else {
                $(this).attr('data-prefix', 'fas');
            }
            console.log(this);
        }
    });
});

(Note: even the removeClass() above does not work, it seems like the element cannot be altered from inside the success function?)

Now, when I do console.log(this), I get some results that seem weird to me.

The first time I click on the icon, console.log() I get this result:

<svg class="svg-inline--fa fa-square fa-w-14 fa-lg switchButton fa-pulse" data-uid="1" aria-hidden="true" data-prefix="fas" data-icon="check-square" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" data-fa-i2svg="">

And every time after that, I always get this result:

<svg class="svg-inline--fa fa-spinner fa-w-16 fa-lg switchButton fa-pulse" data-uid="1" aria-hidden="true" data-prefix="far" data-icon="check-square" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" data-fa-i2svg="">

And the icon is never changed on the actual page, it just stays as the spinner. I don't really understand what is going wrong here. This seems like it should be such a simple thing to do but is not working for me.

The element is initially loaded on the page as:

<i data-uid="<?=$task['uid'];?>" class="far fa-square fa-lg switchButton"></i>

and I am using font-awesome CDN version v5.0.8.

I've tried several things, such as editing the attributes directly as you see here which I found is the correct way to do it from this StackOverflow question, and I also tried editing the actual class of the SVG object ( $(this) in context ), but that also had no effect.

When I inspect element on the object, chrome tells me the code is activated because the element tag sort of "flashes", but none of the data is actually changed.

What is going wrong?


Solution

  • See Update Below...

    Original answer:

    This behavior comes from a combination of the use of the 'this' keyword and the way that the animated font-awesome icons are handled. As we discussed in the comments, it replaces the tag with an one, however, in reading through their documentation I saw that it does so with each change, it's very dynamic.

    The problem is that when you pass the 'this' keyword into your ajax context, you're locking in that instance of the svg control, but it is replaced after that. So when you return 'this' you see that the class has changed successfully on the old control, but the new control remains having the spinner.

    The solution to this is to use "$('[data-fa-i2svg]')" instead of "$('this')" in your success callback function. That targets the current control.

    I found this solution here:

    https://fontawesome.com/how-to-use/svg-with-js

    It states "If you absolutely need to attach events to the icon you can use the data-fa-i2svg attribute but you need to allow for the dynamic creation of the svg element."

    Update:

    Using the "$('[data-fa-i2svg]')" selector doesn't work if you have multiple icons on the same page, as it will update them all.

    You can set a font awesome property that will nest the svg inside the original and then use a selector to get the child of that tag. This works, however I think GrumpyCrouton's adaptation of just using the $('#taskid-'+uidOfTask); is probably more elegant in this situation.

    Since this is marked as the solution I'm including his code below, but see his answer for further detail.

    success: function(response) { 
        var element = $('#taskid-'+uidOfTask);
        element.attr('data-icon', 'check-square').removeClass("fa-pulse");
        if(response == 1) {
            element.attr('data-prefix', 'far');
        } else {
            element.attr('data-prefix', 'fas');
        }
    }