Как работает встроенный Javascript (в HTML)?


129

Я знаю, что это плохая практика. Не пишите такой код, если это вообще возможно.

Конечно, мы всегда оказываемся в ситуациях, когда умный фрагмент встроенного Javascript может быстро решить проблему.

Я преследую этот вопрос, чтобы полностью понять, что происходит (и потенциальные ловушки), когда написано что-то вроде этого:

<a href="#" onclick="alert('Hi')">Click Me</a>

Насколько я могу судить, функционально это то же самое, что и

<script type="text/javascript">
   $(function(){ // I use jQuery in this example
       document.getElementById('click_me').onclick = 
           function () { alert('Hi'); };
   });
</script>
<a href="#" id="click_me">Click Me</a>

Исходя из этого, кажется, что строка, назначенная атрибуту onclick, вставляется в анонимную функцию, которая назначается обработчику щелчка элемента. Так ли это на самом деле?

Потому что я начинаю делать такие вещи:

<a href="#" onclick="$(this).next().fadeIn(); return false;">Display my next sibling</a> <!-- Return false in handler so as not to scroll to top of page! --> 

Что работает. Но я не знаю, насколько это взлом. Это выглядит подозрительно, потому что нет явной функции, из которой возвращается!

Вы можете спросить, почему ты это делаешь, Стив? Встроенный JS - плохая практика!

Честно говоря, я устал редактировать три разных раздела кода только для того, чтобы изменить один раздел страницы, особенно когда я просто создаю прототип, чтобы посмотреть, будет ли это вообще работать. Это намного проще, а иногда даже имеет смысл, чтобы код, специально связанный с этим элементом HTML, был определен прямо внутри элемента: когда через 2 минуты я решаю, что это была ужасная, ужасная идея, я могу уничтожить весь div (или что-то еще ), и у меня нет кучи таинственного мусора JS и CSS, висящего на остальной части страницы, хоть сколько-нибудь замедляющего рендеринг. Это похоже на концепцию локальности ссылки, но вместо промахов кеша мы смотрим на ошибки и раздувание кода.


3
Вы правы, это анонимная функция.
bhamlin

1
Вам нужно будет #click_meпривязать запрос к событию DOMready или разместить сценарий после узла.
Берги

1
В консоли сделайте document.getElementById("click_me").onclick;. Или предупредить об этом. Вы увидите, что это функция.
Д. Строут

Ответы:


96

У вас почти все правильно, но вы не учли thisзначение, указанное во встроенном коде.

<a href="#" onclick="alert(this)">Click Me</a>

на самом деле ближе к:

<a href="#" id="click_me">Click Me</a>
<script type="text/javascript">
document.getElementById('click_me').addEventListener("click", function(event) {
    (function(event) {
        alert(this);
    }).call(document.getElementById('click_me'), event);
});
</script>

Встроенные обработчики событий устанавливаются thisравными цели события. Вы также можете использовать анонимную функцию во встроенном скрипте

<a href="#" onclick="(function(){alert(this);})()">Click Me</a>

7
Сценарий должен идти после тега, иначе тег не существует при его выполнении - я предлагаю изменить ваш ответ, чтобы отразить это.
undefined

как мне распространить eventс помощью встроенного onclick='myFunction(event)'??
oldboy

9

Что делает браузер, когда у вас есть

<a onclick="alert('Hi');" ... >

заключается в том, чтобы установить фактическое значение «onclick» примерно таким:

new Function("event", "alert('Hi');");

То есть он создает функцию, которая ожидает параметр «событие». (В IE нет; это больше похоже на простую анонимную функцию.)


2
Ах. Таким образом, я могу использовать переменную eventдля получения события (и исходного элемента от него через event.target) из моего встроенного фрагмента JS! Прохладно.
Стивен Лу

1
IE действительно не передает eventпараметр, но если вы используете eventвнутри этой анонимной функции, IE будет понимать ее как фактический объект события. Поскольку анонимная функция установлена ​​как свойство windowобъекта в IE, она будет видеть это как window.event.
rdleal

8

Кажется, существует много плохой практики , связанной с атрибутами обработчика событий. Плохая практика - это незнание и использование доступных функций там, где это наиболее целесообразно. Атрибуты событий полностью документированы W3C, и в них нет ничего плохого. Это ничем не отличается от размещения встроенных стилей, которые также документированы W3C и могут быть полезны в разы. Независимо от того, помещаете ли вы его в теги сценария или нет, он будет интерпретироваться одинаково.

https://www.w3.org/TR/html5/webappapis.html#event-handler-idl-attributes


2
Да, я склонен согласиться, и я даже привел разумные «причины» того, почему такой конкретный вид использования встроенного кода может быть оправдан в определенных ситуациях. Но реальность такова, что когда приложение (веб-приложение или другое) приобретает определенную сложность (а это даже обычно не занимает много времени или работы), небольшие фрагменты кода, разбросанные по макету HTML, скорее всего, будут быть архитектурно несостоятельным с точки зрения ремонтопригодности. Например, даже начав напрямую кодировать JS внутри HTML-файла, можно соблазнить скользкую дорожку спагеттификации.
Стивен Лу

1
И это действительно веский аргумент против, с которым я согласен. Обычно я сам не добавляю ни встроенных скриптов, ни стилей для этого материала. Но у людей разные вкусы. Многие, например, любят добавлять теги скриптов и стилей в раздел body, я этого не переношу. Но это действительно так, и некоторым это нравится. То, что bad practice/spaghettificationу одних швов , есть good practice/structureу других.
Daniel B

Я смотрел на github.com/styled-components/styled-components, и если вы объедините это, например, с response, у нас теперь есть все, что связано с компонентом вашего приложения, фактически живущим вместе (надеюсь, мирно) в одном месте. И на самом деле это тоже не выглядит ужасно. Похоже на прогресс. В этом свете вставка встроенных стилей и кода в HTML - неправильный путь (до сих пор это кажется застреванием HTML и стилей в коде JS).
Стивен Лу

5

Лучший способ ответить на ваш вопрос - увидеть его в действии.

<a id="test" onclick="alert('test')"> test </a> 

В js

var test = document.getElementById('test');
console.log( test.onclick ); 

Как вы видите console, если вы используете хром, он печатает анонимную функцию с переданным объектом события, хотя в IE это немного отличается.

function onclick(event) {
   alert('test')
}

Я согласен с некоторыми из ваших замечаний о встроенных обработчиках событий. Да, их легко написать, но я не согласен с вашей точкой зрения о необходимости изменять код в нескольких местах, если вы хорошо структурируете свой код, вам не нужно этого делать.


Думаю, что-то вроде этого <button onclick="login()"> test login </button>отлично подходит для прототипирования. Вы хотите протестировать что-то, что требует взаимодействия с пользователем прямо сейчас, и вы все равно собираетесь удалить это позже. Зачем писать лишний код повсюду?
Дагг Наббит

Это не лишний код, а просто дополнительная анонимная функция. И это не везде, на самом деле все в одном месте, в тегах скрипта.
азиз пунджани

Я не уверен, что мы разделяем ту же идею «хорошо структурировать код». Если вы привязываете пользовательский интерфейс к своим «основным функциям» в том месте, где на самом деле находятся ваши основные функции, это кажется мне худшим механизмом связывания, чем просто их привязка в пользовательском интерфейсе. Я обычно храню все привязки пользовательского интерфейса отдельно от основного, и у меня такое чувство, что OP может также ...
Дагг Наббит,

3

Это выглядит подозрительно, потому что нет явной функции, из которой возвращается!

Это анонимная функция, которая была привязана к событию щелчка объекта.

зачем ты это делаешь, Стив?

Какого черта ты такой… Да ладно, как ты сказал, это действительно широко распространенная плохая практика :)


3

Попробуйте это в консоли:

var div = document.createElement('div');

div.setAttribute('onclick', 'alert(event)');

div.onclick

В Chrome это показывает:

function onclick(event) {
  alert(event)
}

... и нестандартное nameсвойство div.onclickIS "onclick".

Итак, анонимно это или нет, зависит от вашего определения «анонимности». Сравните с чем-то вроде var foo = new Function(), где foo.nameпустая строка, и foo.toString()выдаст что-то вроде

function anonymous() {

}

Это началось как комментарий к ответу Пойнти, но на самом деле не сработало как комментарий. Думаю, это действительно лучший ответ здесь.
Дагг Наббит
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.