Зарезервированные имена функций в JavaScript

После переименования моей функции код перестал работать. Это новое имя scrollIntoView кажется конфликтующим с element.scrollIntoView() метод.

<div onmousedown="scrollIntoView('id001')"/>

function scrollIntoView(id) {
    alert(id);
}

Я создал простой тестовый пример https://jsfiddle.net/mgtn215y/, который показал мою функцию просто игнорируется в пользу element.scrollIntoView() даже

  • он не вызывается для элемента
  • атрибуты не совпадают

Решение очевидно — использовать другое имя функции. Поскольку такое поведение одинаково во всех основных браузерах, я ожидаю, что это где-то указано. Однако я не мог найти ничего связанного, например. Лексическая грамматика JavaScript. Есть ли какая-то подоплека для такого поведения?


person Jan Tosovsky    schedule 01.10.2018    source источник
comment
В этом контексте element — это объект Element, относящийся к элементу, для которого запускается функция. Изучение элемента div путем вызова getElementById показывает, что у него есть функция scrollIntoView(), как и у любого другого элемента, и, поскольку областью действия события onmousedown является сам элемент, он предпочитает эту функцию созданной вами глобальной функции. Возможным решением было бы то, что Juniorized предложил ниже.   -  person Miki Mints    schedule 02.10.2018


Ответы (3)


Проблема заключается в цепочке областей действия функций, скомпилированных из значений атрибутов элемента HTML, объявленных в исходном коде HTML. Атрибуты формы on_eventName=functionBodyCode демонстрируют такое поведение.

По историческим причинам (DOM не существовал в его нынешнем виде, document.getElementByID еще предстояло изобрести...) такие функции компилируются с цепочкой областей видимости, включающей объект, для которого предоставляется атрибут обработчика событий, любой form элемент, которым является элемент внутри и объект document. Однако разные браузеры использовали разные подходы к эмуляции поведения Netscape. Некоторые браузеры включали любой и каждый родительский объект элемента, предоставляющий атрибут обработчика событий, в то время как другие опускали некоторые соответствующие объекты, такие как document.

Технические подробности можно найти в Javascript в полном руководстве O'Reilly, раздел 19.1.6. Область действия обработчиков событий.

Основная рекомендация — избегать предоставления обработчиков событий в HTML — вместо этого добавьте их в JavaScript, используя addEventListener. Если по какой-либо причине вызовы функций должны быть закодированы в значениях атрибутов HTML, избегайте использования имен функций, которые являются методами объектов DOM.

Обратите внимание, что настраиваемая цепочка областей действия для обработчиков событий применяется только к функциям, созданным из атрибутов HTML. Он не применяется к функциям, созданным в JavaScript и добавленным к элементу с помощью addEventListener или element.onEventName=aFunctionObject.


Следующий фрагмент демонстрирует поиск имен свойств внешних элементов в цепочке областей видимости обработчиков событий, определенных в 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>

  • В первой форме elements является свойством окружающего элемента формы.

  • Во второй форме form является свойством HTMLButtonElement.

  • В третьей форме write является методом document.

  • Ссылаясь на 2020 год, HTML5 определяет цепочку областей действия для обработчиков атрибутов HTML как элемент › внешний элемент формы › документ в разделе Scope в элементе нумерованного списка 3.9 внутренний необработанный нескомпилированный обработчик.

person traktor    schedule 01.10.2018
comment
Другой обходной путь: onmousedown="window.scrollIntoView('id1001')" - person Barmar; 02.10.2018
comment
Так это зависит от браузера, а не является частью спецификации JS? - person Barmar; 02.10.2018
comment
@Barmar Как и ожидалось, это не часть ECMAScript, что, в частности, не касается HTML-приложений. Цепочка областей действия восходит к HTML3 или более ранней версии, и хотя я помню ее обсуждение на comp_lang_javasscript, это было давно. Я не смог найти упоминания об этом в опубликованных стандартах HTML. Дальнейшее исследование (см. фрагмент кода) показывает, что элемент с функцией атрибута HTML и объект документа находятся в области действия, но текущие браузеры не включают элемент формы. - person traktor; 02.10.2018
comment
Backtrack — элемент формы находится в области действия, я просто использовал имя имени свойства, которое было доступно в элементе кнопки в виде пустой строки. Ответ обновлен. ( @Бармар ) - person traktor; 02.10.2018
comment
Обновлено со ссылкой на стандарт HTML. - person traktor; 11.08.2020

По какой-то странной причине кажется, что встроенные обработчики неявно используют with(this) при запуске, где this ссылается на элемент, вызвавший событие. Всякий раз, когда вы пытаетесь сослаться на что-то, если это что-то существует как свойство this, тогда в конечном итоге будет ссылка на это свойство, а не на внешнюю переменную с тем же именем:

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
">

Для интерпретатора onclick фактически выглядит примерно так:

onclick="with(this) {
  console.log(typeof className);
  console.log(typeof scrollIntoView);
}"

Это довольно странно. Но встроенные обработчики событий в любом случае являются ужасной практикой, и, вероятно, их не следует использовать; вместо этого назначьте обработчик правильно с помощью addEventListener или onkeydown:

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>

person CertainPerformance    schedule 01.10.2018

Чтобы переопределить, просто попробуйте:

Element.prototype.scrollIntoView = function(id) {
    alert(id);
}

Но обратите внимание, что это работает с 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>

person Community    schedule 01.10.2018