Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

The Magical this Variable & currentTarget

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Turning the icon red is jolly good and all, but since we'll soon make an AJAX call, it would be way jollier if we could turn that icon into a spinning loader icon. But, there's a problem.

After the trash icon, type "Delete":

... lines 1 - 2
{% block body %}
<div class="row">
<div class="col-md-7">
... lines 6 - 12
<table class="table table-striped js-rep-log-table">
... lines 14 - 22
{% for repLog in repLogs %}
<tr>
... lines 25 - 27
<td>
<a href="#" class="js-delete-rep-log">
<span class="fa fa-trash"></span>
Delete
</a>
</td>
</tr>
... lines 35 - 38
{% endfor %}
... lines 40 - 48
</table>
... lines 50 - 51
</div>
... lines 53 - 59
</div>
{% endblock %}
... lines 62 - 83

Now we have a trash icon with the word delete next to it. Back in our JavaScript, once again, console.log() the actual element that was clicked: e.target:

... lines 1 - 62
{% block javascripts %}
... lines 64 - 65
<script>
$(document).ready(function() {
... lines 68 - 69
$table.find('.js-delete-rep-log').on('click', function (e) {
e.preventDefault();
$(e.target).addClass('text-danger');
console.log(e.target);
});
... lines 76 - 79
});
</script>
{% endblock %}

e.target is Fooling Us!

Now, behold the madness! If I click the trash icon, e.target is a span. But if I click the delete text, it's actually the anchor! Woh!

True to what I said, e.target will be the exact one element that originally received the event, so click in this case. And that's a problem for us! Why? Well, I want to be able to find the fa span element and change it to a spinning icon. Doing that is going to be annoying, because if we click on the trash icon, e.target is that element. But if we click on the word delete, then we need to look inside of e.target to find the span.

Hello e.currentTarget

It would be WAY more hipster if we could retrieve the element that the listener was attached to. In other words, which js-delete-rep-log was clicked? That would make it super easy to look for the fa span inside of it and make the changes we need.

No problem! Change e.target to e.currentTarget and high-five yourself:

... lines 1 - 62
{% block javascripts %}
... lines 64 - 65
<script>
$(document).ready(function() {
... lines 68 - 69
$table.find('.js-delete-rep-log').on('click', function (e) {
e.preventDefault();
$(e.target).addClass('text-danger');
console.log(e.currentTarget);
});
... lines 76 - 79
});
</script>
{% endblock %}

Yep, this ends up being much more useful than e.target. Now when we refresh and click the trash icon, it's the anchor tag. Click the delete icon, it's still the anchor tag. No matter which element we actually click, e.currentTarget returns the original element that we attached the listener to.

Enter: this (versus currentTarget)

In fact, try this: console.log(e.currentTarget === this):

... lines 1 - 62
{% block javascripts %}
... lines 64 - 65
<script>
$(document).ready(function() {
... lines 68 - 69
$table.find('.js-delete-rep-log').on('click', function (e) {
e.preventDefault();
$(e.target).addClass('text-danger');
console.log(e.currentTarget === this);
});
... lines 76 - 79
});
</script>
{% endblock %}

Refresh! And click anywhere on the delete link. It's always true.

There's a good chance that you've been using the this variable for years inside of your listener functions to find the element that was clicked. And now we know the true and dramatic story behind it! this is equivalent to e.currentTarget, the DOM Element that we originally attached our listener to.

Ultimately that means that we can say, $(this).addClass('text-danger'):

... lines 1 - 62
{% block javascripts %}
... lines 64 - 65
<script>
$(document).ready(function() {
... lines 68 - 69
$table.find('.js-delete-rep-log').on('click', function (e) {
e.preventDefault();
$(this).addClass('text-danger');
});
... lines 75 - 78
});
</script>
{% endblock %}

That will always add the text-danger link to the anchor tag.

And finally, we can easily change our icon to a spinner! Just use $(this).find('.fa') to find the icon inside of the anchor. Then, .removeClass('fa-trash'), .addClass('fa-spinner') and .addClass('fa-spin'):

... lines 1 - 62
{% block javascripts %}
... lines 64 - 65
<script>
$(document).ready(function() {
... lines 68 - 69
$table.find('.js-delete-rep-log').on('click', function (e) {
e.preventDefault();
$(this).addClass('text-danger');
$(this).find('.fa')
.removeClass('fa-trash')
.addClass('fa-spinner')
.addClass('fa-spin');
});
... lines 79 - 82
});
</script>
{% endblock %}

Refresh! Show me a spinner! There it is! It doesn't matter if we click the "Delete" text or the trash icon itself.

So, use the this variable, it's your friend. But realize what's going on: this is just a shortcut to e.currentTarget. That fact is going to become critically important in just a little while.

Now that we've learned this, remove the "delete" text... it's kinda ugly:

... lines 1 - 2
{% block body %}
<div class="row">
<div class="col-md-7">
... lines 6 - 12
<table class="table table-striped js-rep-log-table">
... lines 14 - 22
{% for repLog in repLogs %}
<tr>
... lines 25 - 27
<td>
<a href="#" class="js-delete-rep-log">
<span class="fa fa-trash"></span>
</a>
</td>
</tr>
... lines 34 - 37
{% endfor %}
... lines 39 - 47
</table>
... lines 49 - 50
</div>
... lines 52 - 58
</div>
{% endblock %}
... lines 61 - 85

Leave a comment!

2
Login or Register to join the conversation
Default user avatar
Default user avatar Yan Yong | posted 5 years ago

Thank you so much for showing me how 'this' actually works in js. Good work.

1 Reply

Haha, right!? I promise, now JavaScript will be WAY more fun ;).

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial uses an older version of Symfony... but since it's a JavaScript tutorial, the concepts are still ? valid!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.2.0",
        "symfony/symfony": "3.1.*", // v3.1.10
        "twig/twig": "2.10.*", // v2.10.0
        "doctrine/orm": "^2.5", // v2.7.1
        "doctrine/doctrine-bundle": "^1.6", // 1.10.3
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.2
        "symfony/swiftmailer-bundle": "^2.3", // v2.4.0
        "symfony/monolog-bundle": "^2.8", // 2.12.0
        "symfony/polyfill-apcu": "^1.0", // v1.2.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "friendsofsymfony/user-bundle": "~2.0@dev", // dev-master
        "doctrine/doctrine-fixtures-bundle": "~2.3", // v2.4.1
        "doctrine/doctrine-migrations-bundle": "^1.2", // v1.2.1
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "friendsofsymfony/jsrouting-bundle": "^1.6" // 1.6.0
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.1.1
        "symfony/phpunit-bridge": "^3.0" // v3.1.6
    }
}
userVoice