After renaming my function the code stopped working. That new name scrollIntoView
seems to be clashing with the element.scrollIntoView()
method.
<div onmousedown="scrollIntoView('id001')"/>
function scrollIntoView(id) {
alert(id);
}
I've created a simple test case https://jsfiddle.net/mgtn215y/ which has shown my function is simply ignored in favor of element.scrollIntoView()
even
- it is not called on element
- attributes do not match
The solution is obvious - to use a different function name. As this behavior is consistent across major browsers, I expect this is specified somewhere. However, I couldn't find anything related in e.g. JavaScript lexical grammar. Is there any background for this behavior?
The issue is the scope chain of functions compiled from HTML element attribute values declared in HTML source. Attributes of the form on_eventName=functionBodyCode
exhibit this behavior.
For historical reasons (the DOM did not exist in its current form, document.getElementByID
had yet to be invented...) such functions are compiled with a scope chain comprising the object on which the event handler attribute is provided, any form
element the element is within, and the document
object. However different browsers took different approaches when emulating Netscape behavior. Some browsers included any and every parent object of an element providing an event handler attribute, while others omitted some pertinent objects such as document
.
Technical detail may be found in "Javascript the definitive guide" O'Reilly, section "19.1.6. Scope of Event Handlers".
The main recommendation is to avoid supplying event handlers in HTML - add them in JavaScript using addEventListener
instead. If for some reason function calls must be coded in HTML attribute values, avoid using function names that are methods of DOM objects.
Note the custom scope chain for event handlers only applies to functions generated from HTML attributes. It does not apply to functions created in JavaScript and added to an element using addEventListener
or element.onEventName=aFunctionObject
.
The following snippet demonstrates locating property names of outer elements in the scope chain of event handlers defined in HTML: :
<form name="formName1" action="">
<p> Try to access the elements collection of the form:
<button type="button"
onclick="console.log( elements);">elements
</button>
</p>
</form>
<form name="formName2" action="" onsubmit="return false">
<p> Try to access form name as form.name:
<button type="button"
onclick="console.log( 'form.name: %s', form.name);">form.name
</button>
</p>
</form>
<form name="formName3" action="" onsubmit="return false">
<p>Access document.write as "write"
<button type="button"
onclick="console.log( 'write: %s', write);">write
</button>
</p>
</form>
In the first form,
elements
is a property of the surrounding form element.In the second form,
form
is a property of the HTMLButtonElement.In the third form,
write
is a method ofdocument
.
For some strange reason, it looks like inline handlers implicitly use with(this)
when they run, where this
references the element that triggered the event. Whenever you try to reference something, if that something exists as a property of this
, then that property will end up being referenced, rather than an outer variable with the same name:
console.log(typeof className); // Undefined in standard global scope
console.log(typeof scrollIntoView); // Undefined in standard global scope
.mydiv {
width: 100px;
height: 100px;
background-color: red;
cursor: pointer;
}
<div class="mydiv" onclick="
console.log(typeof className); // Defined inside inline handler
console.log(typeof scrollIntoView); // Defined inside inline handler
">
To the interpreter, the onclick
actually looks something more like:
onclick="with(this) {
console.log(typeof className);
console.log(typeof scrollIntoView);
}"
It's pretty strange. But inline event handlers are terrible practice anyway, and probably shouldn't be used; assign the handler properly with addEventListener
or onkeydown
instead:
document.querySelector('.mydiv').onmousedown = () => scrollIntoView("id001");
function scrollIntoView(id) {
console.log(id);
}
.mydiv {
width: 100px;
height: 100px;
background-color: red;
cursor: pointer;
}
<div class="mydiv"></div>
To override, simply try:
Element.prototype.scrollIntoView = function(id) {
alert(id);
}
But do note that it works with addEventListener
:
var element = document.getElementById("element");
function scrollIntoView(id) {
alert(id);
}
element.addEventListener("mousedown", function() {
scrollIntoView("id001");
});
div {
width: 100px;
height: 100px;
background-color: red;
cursor: pointer;
}
<div id="element"></div>
来源:https://stackoverflow.com/questions/52599614/reserved-function-names-in-javascript