Як працює вбудований 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! --> 

Який працює. Але я не знаю, скільки це хак. Це виглядає підозріло, оскільки немає видимої функції, з якої повертаються!

Ви можете запитати, чому ви це робите, Стів? Inline JS - це погана практика!

Ну, якщо бути чесним, мені набридло редагувати три різні розділи коду, щоб змінити один розділ сторінки, особливо коли я просто щось прототипую, щоб побачити, чи буде він взагалі працювати. Це набагато простіше, а іноді навіть має сенс, щоб код, специфічно пов'язаний з цим HTML-елементом, був визначений прямо в рамках елемента: Коли через 2 хвилини я вирішу, що це була жахлива, жахлива ідея, я можу загнати весь div (або що завгодно) ) і в мене немає кулі таємничих JS і CSS сутких, що розвішуються на решті сторінки, сповільнюючи візуалізацію все так незначно. Це схоже на концепцію місцевості відліку, але замість кешу не вистачає, ми дивимося на помилки та розширення коду.


3
Ви праві, це анонімна функція.
Бемлін

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

1
У консолі зробіть document.getElementById("click_me").onclick;. Або попередити про це. Ви побачите, що це у функції.
D. Strout

Відповіді:


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
Сценарій повинен надходити після тегу, інакше тег не існує при його виконанні - я пропоную змінити свою відповідь, щоб це відобразити.
визначено

як я розповсюджую 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
І це правильний вагомий аргумент проти того, на який я згоден. Я зазвичай не додаю сценарії вбудованого сценарію, а також стилі для цього матер. Але люди мають різні смаки. Багато хто, наприклад, любить додавати теги скриптів та стилів у розділі тіла, я не можу терпіти. Але це дійсно і деяким людям це подобається. Що шви подобаються bad practice/spaghettificationдля одних, так good practice/structureдля інших.
Даніель Б

Я дивився на github.com/styled-components/styled-components, і якщо, наприклад, поєднувати це з реагуванням, у нас зараз є все, що пов’язано з компонентом вашого додатка, який насправді живе разом (сподіваємось, мирно) в одному місці. І насправді це теж не виглядає жахливо. Відчуває прогрес. У цьому світлі заклинення вбудованих стилів та коду в 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')
}

Я погоджуюся з деякими вашими пунктами щодо обробників inline подій. Так, їх легко написати, але я не згоден з вашою думкою про необхідність зміни коду в декількох місцях, якщо ви добре структуруєте свій код, вам це не потрібно робити.


Я думаю, що щось подібне <button onclick="login()"> test login </button>ідеально підходить для складання прототипів. Ви хочете перевірити те, що вимагає взаємодії з користувачами зараз, і ви просто збираєтесь його видалити пізніше. Навіщо писати додатковий код всюди?
Dagg Nabbit

Це не зайвий код, а лише додаткова анонімна функція. І це не скрізь, це насправді все в одному місці, в тегах сценарію.
aziz punjani

Я не впевнений, що ми поділяємо ту саму ідею "добре структурувати ваш код". Якщо ви прив’язуєте інтерфейс користувача до своїх "основних функцій" в тому місці, де фактично живуть ваші основні функції, мені здається, це гірше розташування сполук, ніж просто прив'язування їх до інтерфейсу. Зазвичай я тримаю всі прив'язки інтерфейсу окремо від основних, і я відчуваю, що ОП також може ...
Дагг Наббіт

3

Це виглядає підозріло, оскільки немає видимої функції, з якої повертаються!

Це анонімна функція, яка була прикріплена до події клацання об’єкта.

чому ти це робиш, Стів?

Чому на землі ти це робиш? ... Ну, ніколи, як ви вже згадували, це дійсно широко прийнята погана практика :)


3

Спробуйте це в консолі:

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

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

div.onclick

У Chrome це показує:

function onclick(event) {
  alert(event)
}

... і нестандартною nameвластивістю div.onclickє "onclick".

Отже, чи це анонімність чи ні, залежить ваше визначення "анонімного". Порівняйте з чимось подібним var foo = new Function(), де foo.nameпорожній рядок, і foo.toString()вийде щось подібне

function anonymous() {

}

Це почалося як коментар до відповіді Pointy, але насправді не працювало як коментар. Думаю, його справді найкраща відповідь.
Dagg Nabbit
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.