How is the scope of an Event Listener defined in any attribute in HTML created?

Asked

Viewed 194 times

5

According to the documentation, when the Listener event is assigned as an HTML attribute, the specified code (in the attribute value) is wrapped in a function, with the following parameters:

  • event - for all attributes except onerror;
  • event, source, lineno, colno, and error - for onerror.

The this function is defined as the HTML element that contains the event attribute.


However, another question here on Sopt (now deleted[1]) made me curious because accesses the variable value inside one of these attribute handlers. Thus:

<input onkeyup="console.log(value)" />

Checking if there is one more argument, to my surprise, no. Really, as you put the documentation, only event is passed (since it is a Event attribute that is not onerror):

<input onkeyup="console.log([...arguments])" />

<!-- Escondi o console do snippet porque são muitas propriedades.
     Abra e veja no próprio console do navegador. -->

After further investigation, I realized that all properties of Event.target are also in scope. I stress that value is a property of that object.

So it seems to me there’s something like a with(event.target) somewhere, but that’s an assumption and there’s no confirmation. It is undoubtedly detail of implementation, since the exposure of these values can be done in another way. The problem is that, seeing the source of the executed function, there is nothing but the parameter event:

<input onkeyup="console.log(arguments.callee.toString())" />

I ask you:

  • How this happens (how is created the scope of this attribute Event Listener)?
  • This is specified?
  • In addition to (apparently) all properties of Event.target are available, I can access some more value?
  • What is the support for this in the browsers?
  • 2

    I believe that this is how to use the this where any object is the DOM element and events are parts of the "extended" interface, so value is the same as calling this.value, for a quick test <input onkeyup="console.log(this)" />, another test with an Element object property <input onkeyup="console.log(tagName)" /> or <input onkeyup="console.log(outerHTML)" />, in short, this is the natural behavior in several interfaces used in JS, already the arguments and Event does not have to do with the object directly on the test with onkeyup="console.log(value)"

  • 2

    @Guilhermenascimento, the problem is that there the access would have to be explicit this.value, for example. If (function() { console.log(value); }).call({ value: 50 }) will give error. I still keep the main question of how this "implicit" access is possible. And I know that the arguments it’s not about that, I used it only for demonstration purposes.

  • 3

    No, because you are confusing the behavior "within HTML", in it the this can be "implicit", It’s not an absolute Javascript, it’s "HTML" (note the quotes). I’m not quoting technical source or language specification, the test is practical, the "implicit" only means that in HTML (depending on how you use the attributes *on) the this will not need to be written (or is this "implicit")

  • 3

    @Guilhermenascimento, I searched for references, but found no reason to understand that the behavior of Javascript would change within the attribute. In my conception, if, within the attribute, Javascript code is executed, then it runs following the "JS rules". That’s why the doubt - the this, in JS, it is not implied (as you know), so I still can’t explain for sure. Apparently, really the properties of this are "scattered" to the scope of attribute Listener, but I still haven’t found a reference to it. Do you have any source to corroborate that last statement?

  • 1

    Okay, I’ll tell you, there’s a lot that came in HTML and Javascript (this by the way from Mozilla, the others are "similar/almost identical" dialects) that only started to be DOCUMENTED and standardized after, the behaviors in general were only being copied among the competitors, since the emergence of Javascript (and in microsoft Jscript), only a few years later came to have the "Ecmascript", W3.org already existed, but it was not they who initially defined the behavior of Javascript, since this came after HTML already exists ...

  • 1

    ... then one thing I assure you, there is no perfect organization, many things are being arranged or documented later, I can even find some reference in https://html.spec.whatwg.org/#Event-Handler-content-Attributes, but it will probably be vague (even if I cite more specific things like CSP), it does not mean that we will find everything cute and well written or determined as a rule, companies have been copying and even "unnecessary" things exist because of this ...

  • 1

    ... including grammar error in a Document property in JS: https://answall.com/q/123468/3635, so it is likely that I cannot provide "documented" answers to everything, which I can state are with tests, which I have already quoted in the first comment, this is "implicit" in "on" HTML attributes, as well as global functions and variables will also be, these were tests I did, in different browsers, including old browsers. That’s what I can answer for now, not everything in IT is done or planned to perfection. ;)

  • 1

    I had also found this same piece in HTML Standard, but it’s really vague. Anyway, I keep looking for a reference (and also waiting for some concrete answer, in case someone finds it). Thanks for the comments. :-)

  • 1

    I fully understand the motivation, but I still hope that you gave attention to the story about Mozilla’s Javascript, before joining "ECMA International" and that other browsers (in this case IE) had only an extremely similar dialect, so things were being "copied" from both sides, so expect to find much beyond, which may confirm something maybe would be to look at the source code of Chromium https://github.com/chromium/chromium and FF: https://archive.mozilla.org/pub/

Show 4 more comments

1 answer

3


TL;DR

The element containing the attribute Listener is used to derive the scope of the function that will be executed. That’s why the element properties (such as textContent, value etc) can be accessed directly - because they are literally part of the scope of function.

I point out that this answer is only for attribute Event handlers (that is, those defined directly in attributes in HTML). Events added via addEventListener function (similarly) differently. There is no modification of the scope of the function passed to the second argument of addEventListener.


Each event to be heard in the browser has a struct associated, called Event Handler[1], containing a field listener (which corresponds to the hearer event) and other field, value. For events added via attributes in HTML, the value that struct corresponds to what the specification calls Internal raw uncompiled Handler.

That one Internal raw uncompiled Handler is a tuple contains the following information: [2]

  • The body of the script (not compiled - "uncompiled"), which is the code that is inserted in the attribute value.
  • The location from which the script originated, in case an error needs to be reported.

Since the Listener is triggered, the browser performs the evaluation (compilation) of the script, which will be used for the creation of the function callback (which will be executed). For the creation of this function, account shall be taken of this algorithm [3], which makes use of the internal operation OrdinaryFunctionCreate (of Ecmascript).

To OrdinaryFunctionCreate is an internal Ecmascript operation with the following signature:

OrdinaryFunctionCreate (functionPrototype, ParameterList, Body, thisMode, Scope)

It is used to create the function callback. For this, the following arguments are passed:

  • functionPrototype is the Function.prototype (we are already familiar with this prototype with the other functions of Javascript, which also assume it).

  • ParameterList in the case of events that are not errors (such as onclick, onkeydown etc), corresponds to event.

  • Body the body of the function is generated dynamically through concatenations with the event name and the body. It is basically:

    function <<name>>(event) {
    <<body>>
    }
    

    In which <<name>> is the name of the function callback associated with the event (as onclick) and <<body>> is the Javascript code added to the event attribute in HTML.

  • thisMode is going on "non-lexical-this".

  • Scope The scope is defined as follows::

    • Initially, the object scope document;
    • If the element possessing the attribute Event Listener is within a form (has a form Owner), the subject of the form also is incorporated into the ecopo;
    • If the element possessing the attribute Event Listener is non-null, the object of that element also is incorporated into the scope.

Thus, one can affirm that the scope arises through this triple derivation (document → form owner → eventTarget). That is why, entirely, the scope will not be equal to this.

See the full algorithm to learn more.

Once the function callback is created, it is executed. For this invocation, the this is defined as the element containing the Event attribute[4].


Completion

We therefore conclude that values such as value, textContent (and all other properties present in the element that activated the event) are accessible "directly" this way because they are scope of function callback created by the browser. The scope concept is explained on MDN.

Do not confuse scope with this, which, roughly speaking, is just another name of the scope of a function.

Even if the "this implicit" is possible in Javascript (what is not), the following example demonstrates why this is not the case (there is a difference between the this and the value of the scope):

<form>
  <button type="button" onclick="
    console.log({
      this_elements: typeof this.elements,   // undefined
      scoped_elements: typeof elements,      // object
      
      are_eql: this.elements === elements    // false
    })
  ">
    Clique
  </button>
</form>


    <h3>Explicação</h3>
    Mesmo que fosse possível em JavaScript, o "<code>this</code>
    implícito" não poderia ocorrer nessa situação, já que há uma
    evidente (demonstrada acima) diferença de valores.

    O escopo da função, criado a partir do documento, formulário
    pai (se presente) e, enfim elemento associado ao
    <em>attribute event</em>, é diferente do <code>this</code>
    da função, que corresponde (ao contrário do escopo)
    unicamente ao elemento associado ao <em>attribute event</em>.

This happens because when the function callback (created as explained above) is invoked, its this is defined as the element containing the attribute Event Listener. Already the scope (defined during the creation of this function callback) is created from the derivation of the object document, of form Owner (if present) and the event element itself. They are therefore different.


How does this happen?

Explained above.

This is specified?

Yes. References to the specification are in the text.

In addition to (apparently) all properties of Event.target are available, I can access some more value?

Actually (contrary to what I initially thought), it is not the event.target which is "placed" as a scope, but rather the element containing the Event attribute (the element associated with the event). O event.target will not always correspond to him.

What is the support for this in the browsers?

The browsers that follow the specification implement this and therefore support what has been discussed here. The implementation varies between host, since the specification does not bother to dictate how to do, but yes what to do.


Reference

  • Just for the record, OrdinaryFunctionCreate translated would be like "create any function/normal", So it’s in the specification, it doesn’t mean that it’s that name on javascript engines that run in browsers nor does it mean that it’s exactly how the engine works, what it needs is for it to meet that, but not to be exactly that. And it is worth mentioning that referring to this at that moment meant more to say, behaves as such, it does not mean that internally it is exactly this.

  • 2

    Take a look at in that modification of the source code. In <input type="button" value="Duplicar" onclick="botao( visor.value );" /> besides the this implicit is also possible to reference other identifiable elements in the context of <form>.

  • 2

    @Augustovasques, really. This is also described in algorithm (see this image with some of my markings). What happens is that the scope is defined, first, as the document, and then, in the case of forms, it is incorporated into the form element and then into the element that has the Event attribute. :-) As to the this be implicit, this does not happen in Javascript (as explained by the last link, in the references of my reply).

  • 2

    In this case, to thick words, it is a "triple derivation of the scope"- document → form owner → eventTarget. :D

Browser other questions tagged

You are not signed in. Login or sign up in order to post.