The special scope of an inline event handler for an element has a different _javascript technique in each browser

Source: Internet
Author: User
Tags event listener wrapper

Standard reference

No.

Problem description

Binding an event in the attribute of an element, in effect creating an inline event handler (such as

alert"); ...>...inline event handlers have a special scope chain, and there are differences in the implementation details of each browser.

The impact

Improper use of variables or calls in an inline event-handling function of an element causes the script to run an error.

Affected browsers

All browsers

Problem analysis

1. Scope chain of inline event handler functions

Unlike other functions, the scope chain of an inline event handler starts from the head: The calling object, the DOM object for the element, the DOM object (if any) of the FORM that the element belongs to, the Document object, the Window object (Global object).

such as the following code:

<form action= "." Method= "get" >
	<input type= "button" value= "Compatmode" onclick= "alert (compatmode);" >
</form>

Equivalent to 1:

<form action= "." Method= "get" >
	<input type= "button" value= "Compatmode" >
</form>
< script>
document.getelementsbytagname ("input") [0].onclick=function () {with [
	document] {with (this
		2.form) 3{with (    This

The code in both of these ways will pop up the Document.compatmode value in all browsers.

Replacing ' Compatmode ' in the above code with ' method ' will pop up ' get ' in each browser, that is, the value of the method property of the Form object that the INPUT element is in.

Note:
1. This code simulates the behavior of browsers only to illustrate the problem, and does not mean that all browsers are so implemented.
2. Whether to use this keyword or use this DOM object directly, there are differences in each browser, please see the contents of this article 2.1.
3. Whether to add a FORM object to the scope chain, the browser in the implementation of the differences, please see the details of the contents of this article 2.2.

2. The difference between the scope chain of the inline event handler function and the browser

Refer to WebKit Source code:

void V8lazyeventlistener::p reparelistenerobject (scriptexecutioncontext* context) {if (Hasexistinglistenerobject ())

  Return

  V8::handlescope Handlescope;
  v8proxy* Proxy = V8proxy::retrieve (context);

  if (!proxy) return;
  Use the outer scope to hold context.
  v8::local<v8::context> V8context = Worldcontext (). Adjustedcontext (proxy);
  Bail out if we cannot get to the context.

  if (V8context.isempty ()) return;

  V8::context::scope Scope (V8context);

  Fixme:cache the wrapper function. //Nodes other than the document object, when executing inline event handlers push document, form, and the target node on The scope chain.We are using ' with ' statement. chrome/fast/forms/form-action.html//chrome/fast/forms/selected-index-value.html//Base/fast/overflow/ons croll-layer-self-destruct.html////Don ' t use new lines so, lines in the modified handler//have the same numb
  ERs as in the original code. String code = "(function (evt) {" \ "with ( This. ownerdocument? This. Ownerdocument: {}) {"\" with (This  . Form? This. Form: {}) {"\" with ( This) {"\" return (function (evt) {);
  Code.append (M_code);
  Insert ' \ n ' Otherwise//-style comments could break the handler.
  Code.append ("\ n}). Call (this, evt);}}}");
  v8::handle<v8::string> codeexternalstring = v8externalstring (code);
  v8::handle<v8::script> Script = V8proxy::compilescript (codeexternalstring, M_sourceurl, M_lineNumber); if (!script.
    IsEmpty ()) {v8::local<v8::value> Value = proxy->runscript (script, false); if (!value.

      IsEmpty ()) {ASSERT (value->isfunction ());

      v8::local<v8::function> wrappedfunction = v8::local<v8::function>::cast (value); Change the toString function on the wrapper function to avoid it//returning the source for the actual wrapper F Unction.  Instead IT//returns source for a clean wrapper function with the event//argument wrapping the event source Code.
    The reason for this is//, some Web sites use toString on event functions and eval the  Source returned (sometimes a RegExp is applied as OK) for some//other use.
      That fails miserably if the actual wrapper source is//returned.
      Define_static_local (V8::P ersistent<v8::functiontemplate>, Tostringtemplate, ()); if (Tostringtemplate.isempty ()) tostringtemplate = V8::P ersistent<v8::functiontemplate>::new (V8::FunctionTem
      Plate::new (v8lazyeventlistenertostring));
      V8::local<v8::function> tostringfunction;
      if (!tostringtemplate.isempty ()) tostringfunction = Tostringtemplate->getfunction ();
        if (!tostringfunction.isempty ()) {String Tostringresult = ' function ';
        Tostringresult.append (M_functionname);
        Tostringresult.append ("("); Tostringresult.append (m_issvgevent?)
        "EVT": "Event");
        Tostringresult.append (") {\ n");
        Tostringresult.append (M_code);
        Tostringresult.append ("\ n}"); Wrappedfunction->sethiddenvalue (v8hiddenpropertyname::tostRingstring (), v8externalstring (Tostringresult));
      Wrappedfunction->set (V8::string::new ("toString"), tostringfunction);

      } wrappedfunction->setname (V8::string::new (fromwebcorestring (M_functionname), M_functionname.length ()));
    Setlistenerobject (wrappedfunction);
 }
  }
}

As you can see from the code above, WebKit uses the ' this ' keyword when adding objects to the scope chain, and determines whether to add a Form object to the scope chain by determining whether ' this.form ' exists.

There are similar implementations in other browsers, however, in each browser, there are differences in how the target object (that is, the object that binds this inline event handler ) is added to the scope chain, and the method of determining whether to add a FORM object in the scope chain is different.

2.1. Browsers use different methods to add target objects when generating this particular scope chain

Each browser adds the DOM object of the element that the inline event-handler function belongs to to the scope chain, but the way it is joined is different.

such as the following code:

<input type= "button" value= "Hello" onclick= "alert (value);" >

In all browsers, ' hello ' will pop up.

Then modify the code to change the execution context of the inline event handler function for the INPUT element:

<input type= "button" value= "Hello" onclick= "alert (value);" >
<script>
var $target =document.getelementsbytagname ("input") [0];
var o={
	onclick: $target. onclick,
	value: "Hi, I ' m here!"
};
O.onclick ();
</script>

The results of running in each browser are as follows:

IE Chrome Hi, I ' m here!
Firefox Safari Opera Hello

As you can see, browsers have different ways to add the DOM object of the element that the inline event handler functions to the scope chain.

The way you add in IE Chrome is similar to the following code:

<input type= "button" value= "Hello" >
<script>
var $target =document.getelementsbytagname ("input ") [0];
$target. Onclick=function () {with (
	document) {
		
			alert (value);
}}} </script>

The addition of Firefox Safari Opera is similar to the following code:

<input type= "button" value= "Hello" >
<script>
var $target =document.getelementsbytagname (" Input ") [0];
$target. Onclick=function () {with (
	document) {with
		($target) {
			alert (value);
}}} </script>

The effect of this difference is rare because there is very little need to change the execution context of inline event handlers .

2.2. Browsers have a different understanding of how to add a FORM object when creating this particular scope chain

Each browser adds the form object that the inline event handler functions to the scope chain, but how to determine whether the element is "part of" a form object or not is handled differently by browsers.

such as the following code:

<form action= "." Method= "get" >
	<div>
		<span onclick= "alert (method);" >click</span>
	</div>
</form>
<script>
document.method= " Document.method ";
</script>

In each browser, the information that pops up after you click on the SPAN element is as follows:

IE Safari Opera Get
Chrome Firefox Document.method

Visible:

    • IE Safari Opera Adds a Form object to the scope chain of an inline event handler , whether adding a Form object appears to be determined by whether the element is a descendant of a form. So in these browsers, the variable ' method ' in the function ultimately gets the value of the FORM's ' method '.
    • Chrome Firefox does not add a Form object to the scope chain of an inline event handler to determine whether the join form object depends on whether the ' form ' property of the target object to which the function is bound exists. From the WebKit source above, you can see that Chrome is using ' this.form ' to judge that the ' form ' attribute exists only if the target element is a descendant of a form and the target element is a FORM element. The SPAN element in this example is not a form element, so the variable ' method ' eventually gets the value of ' Document.method '.

If you replace the SPAN element in the above code with an INPUT element or another form element, the performance will be consistent across all browsers.

3. An instance of the problem caused by this particular scope chain of the inline event handler function

3.1. The problem that a variable accessed in an inline event-handling function of an element unexpectedly has the same name as the other object of the global object in the scope chain of that function

When a variable accessed in an inline event-handler function unexpectedly has the same name as the property of another object in the scope chain of the function that is not a global object (window), the actual value of the variable is not the expected value.

Suppose you have the following code:

<button onclick= "Onsearch ()" > click here </button>
<script>
function Onsearch () {
	Alert ("click!");
}
</script>

The author's intention is to click the button that pops up "click!" Information, but the HtmlElement object for the WebKit engine browser has an event listener named Onsearch, which will cause the code above to be executed in Chrome Safari as expected. In this case, because the listener is undefined (null), an error of "uncaught Typeerror:object is not a function" will be reported.

Attached: In the above code, append the following code to confirm the location of ' Onsearch ':

<script>
var o=document.getelementsbytagname ("button") [0];
if ("Onsearch" in O) alert ("The current object has a Onsearch property.") ");
if (O.hasownproperty ("Onsearch")) Alert ("Onsearch property is private to the current object. ");
</script>

3.2. The problem that occurs when an attempt is made to invoke a property or method of a form in an inline event-handling function that is not a descendant of a form element in the form

Suppose you have the following code:

<form action= "xxx" method= "get" > ...
	<a href= "#" onclick= "submit ();" >click</a>
</form>

The author intended to call form's ' Submit ' method after clicking A element, but Chrome Firefox did not add the form object to the scope chain of the inline event handler , so the above code does not work in Chrome Firefox 。

Solution

1. Do not use the inline event handler function , the DOM standard event registration method for the element registration event handler functions, such as:

<button> Click here </button>
<script>
function Onsearch () {
	alert ("click!");
}
function bind ($target, eventname,onevent) {
	$target. AddEventListener $target. AddEventListener (EventName, Onevent,false): $target. attachevent ("on" +eventname,onevent);
Bind (document.getElementsByTagName ("button") [0], "click", Onsearch);
</script>

2. When you must use an inline event handler, make sure that the variable you are trying to access within the function is in the global scope, and that it is not referenced to unexpected objects because of the function's unique scope chain. The easiest way to do this is to use prefixes such as ' my_onsearch '.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.