Чому використання onClick () у HTML є поганою практикою?


133

Я не раз чув, що використання подій JavaScript, таких як onClick()HTML, є поганою практикою, оскільки це не добре для семантики. Мені хотілося б знати, які недоліки є, і як виправити наступний код?

<a href="#" onclick="popup('/map/', 300, 300, 'map'); return false;">link</a>


4
З onlickc немає нічого поганого, але, зробивши href #, кожен, хто вирішить вимкнути javascript, буде застряг і нічого не зможе зробити. Для деяких сайтів це добре, але для простого відкриття вікна абсолютно нерозумно не надавати "справжнє" посилання.
Марк Б

2
Виразно прочитайте це посилання. Це має дуже мало спільного з семантикою, і більше спільного з ... всі речі на цій сторінці :-)
morewry

Спробуйте відкрити своє посилання на новій вкладці, і ви побачите приклад того, чому це неправильно ...
Себастьян C.

2
Це має бути так <button>, оскільки посилання повинні вказувати на фактичний ресурс. Цей спосіб більш семантичний і зрозуміліший для користувачів читачів екрану.
програміст5000

Відповіді:


171

Ви, мабуть, говорите про ненав’язливий Javascript , який виглядав би так:

<a href="#" id="someLink">link</a>

з логікою в центральному файлі JavaScript виглядає приблизно так:

$('#someLink').click(function(){
    popup('/map/', 300, 300, 'map'); 
    return false;
});

Переваги є

  • поведінка (Javascript) відокремлено від презентації (HTML)
  • відсутність змішування мов
  • ви використовуєте рамку javascript, наприклад jQuery, яка може вирішувати більшість проблем між браузером
  • Ви можете додати поведінку до багатьох елементів HTML одночасно без дублювання коду

30
Ви перерахували чимало переваг, а як щодо недоліків? Налагодження синього диму?
абеліто

2
@abelito: Не впевнений, що налагодження є недоліком. Якщо що-небудь, це перевага для налагодження, оскільки ви можете перейти через JavaScript у будь-якому інструменті налагодження браузера. Не впевнений, що ви можете зробити це так просто, якщо код є рядковим onClick. Ви також можете писати одиничні тести, якщо хочете, щоб проти ваших сценаріїв, що також дуже важко, я вважаю, що код знаходиться всередині, onClickа логіка не відокремлена. Я особисто не маю жодних проблем з налагодженням ненав'язливого JavaScript, і переваги в управлінні та тестуванні JavaScript-коду далеко не великі, щоб не використовувати його.
Ніп

45
@ François Wahl: головний недолік - відкритість: перегляд HTML-коду не говорить про те, які зворотні виклики додаються до нього, навіть не існують.
Майкл Боргвардт

1
@MichaelBorgwardt: Я повністю згоден з вами, що це недолік. Якщо код добре структурований і організований, я не вважаю це величезною проблемою під час роботи над проектом або налагодженням кому-небудь іншої бази коду. Взагалі, хоча я погоджуюсь з вами, я не вважаю, що виявлення є вагомим приводом для написання вбудованого сценарію, приносячи переваги, такі як перевірка коду та можливість відокремити свій код функції та поведінки від DOM. Я не кажу, що рядковий код поганий, і він не працює. Це є і абсолютно чудово, залежно від того, цікавляться ви такими речами, як перевірка чи ні.
Nope

4
Ще один момент цього обговорення з новачками - це onclickможе більш чітко відобразити причинно-наслідкові зв’язки між взаємодією елемента та ефектом (викликом функції), а також зменшити когнітивне навантаження. Багато уроків кидають учнів у те, addEventListenerщо пакує ще багато ідей всередині більш складного синтаксису. Крім того, останні структури на основі компонентів, такі як Riot і React, використовують onclickатрибут і змінюють уявлення про те, яким має бути поділ. Перехід від HTML onclickдо компонента, який це підтримує, може бути більш ефективним «кроковим» процесом для навчання.
jmk2142

41

Якщо ви використовуєте jQuery, тоді:

HTML:

 <a id="openMap" href="/map/">link</a>

JS:

$(document).ready(function() {
    $("#openMap").click(function(){
        popup('/map/', 300, 300, 'map');
        return false;
    });
});

Це має перевагу продовжувати працювати без JS або якщо середній користувач натискає посилання.

Це також означає, що я міг обробляти загальні спливаючі вікна, переписуючи знову на:

HTML:

 <a class="popup" href="/map/">link</a>

JS:

$(document).ready(function() {
    $(".popup").click(function(){
        popup($(this).attr("href"), 300, 300, 'map');
        return false;
    });
});

Це дозволить вам додати спливаюче вікно до будь-якого посилання, просто надавши йому клас спливаючого вікна.

Цю ідею можна поширити ще далі:

HTML:

 <a class="popup" data-width="300" data-height="300" href="/map/">link</a>

JS:

$(document).ready(function() {
    $(".popup").click(function(){
        popup($(this).attr("href"), $(this).data('width'), $(this).data('height'), 'map');
        return false;
    });
});

Тепер я можу використовувати один і той же біт коду для багатьох спливаючих вікон на всьому сайті, без необхідності писати набір матеріалів onclick! Так, для повторного використання!

Це також означає, що якщо згодом я вирішу, що спливаючі вікна є поганою практикою (якою вони є!) І що я хочу замінити їх модальним вікном стилю лайтбокс, я можу змінити:

popup($(this).attr("href"), $(this).data('width'), $(this).data('height'), 'map');

до

myAmazingModalWindow($(this).attr("href"), $(this).data('width'), $(this).data('height'), 'map');

і всі мої спливаючі вікна на моєму веб-сайті зараз працюють зовсім інакше. Я навіть міг зробити функцію виявлення функцій, щоб вирішити, що робити у спливаючому вікні, або зберегти перевагу користувачів, щоб дозволити їм чи ні. Завдяки вбудованому onclick це вимагає величезної копії та вставки.


4
Працює без JavaScript ?? Це jQuery. Для роботи йому потрібен Javascript у веб-переглядачі, правда?
Томас Шилдс

2
Так, але посилання може в цьому випадку переспрямувати на сторінку, яка виконує дію без JavaScript.
ThiefMaster

12
Посилання працює без JavaScript. Подивіться, як посилання має нормальний атрибут href. Якщо у користувача немає JavaScript, посилання все одно буде посиланням, і користувач все ще може отримати доступ до вмісту.
morewry

3
@Thomas Shields: Ні, він означає, що якщо у вас немає Javascript, посилання все одно переведе вас на / map / ...
Роберт

2
Ах Річ! Ми всі любимо вас за це найтонше рішення!
Nirmal

21

Завдяки дуже великим програмам JavaScript, програмісти використовують більше інкапсуляції коду, щоб уникнути забруднення глобальної сфери. А щоб зробити функцію доступною для дії onClick в елементі HTML, вона повинна бути в глобальному масштабі.

Можливо, ви бачили файли JS, які виглядають приблизно так ...

(function(){
    ...[some code]
}());

Це вирази функцій негайно викликаних функцій (IIFE), і будь-яка функція, заявлена ​​в них, буде існувати лише у їхньому внутрішньому обсязі.

Якщо ви function doSomething(){}заявите в IIFE, то зробіть doSomething()елемент onClick елемента на своїй HTML-сторінці, ви отримаєте помилку.

Якщо, з іншого боку, ви створюєте eventListener для цього елемента в межах цього IIFE і дзвоните, doSomething()коли слухач виявить подію клацання, ви хороші, тому що слухач і doSomething()ділиться областю IIFE.

Для маленьких веб-додатків з мінімальною кількістю коду це не має значення. Але якщо ви прагнете писати великі, що підтримуються коди, onclick=""це звичка, якої слід уникати.


1
якщо ви прагнете писати великі,
доступні для кодування

20

Це не добре з кількох причин:

  • він змішує код і розмітку
  • код, написаний таким чином, проходить через eval
  • і працює в глобальному масштабі

Найпростіше було б додати nameатрибут до свого <a>елемента, тоді ви могли б зробити:

document.myelement.onclick = function() {
    window.popup('/map/', 300, 300, 'map');
    return false;
};

хоча сучасна найкраща практика полягає у використанні idзамість імені, а addEventListener()замість використання, onclickоскільки це дозволяє прив’язати кілька функцій до однієї події.


Навіть запропонований таким чином відкриває користувачеві масу проблем із зіткненнями обробника подій.
попередження

3
@ реакція, як це робить обробник подій вбудованого походження, отже, чому моя відповідь говорить, що краще використовувати addEventListener()та чому.
Альнітак

2
Хтось може пояснити це більше? "код, написаний таким чином, проходить через eval" ... Я не знаходжу нічого в Інтернеті про це. Ви хочете сказати, що для використання вбудованого Javascript є якийсь недолік у продуктивності чи безпеці?
MarsAndBack

12

Є кілька причин:

  1. Я вважаю, що це допомагає підтримувати окрему розмітку, тобто HTML та сценарії на стороні клієнта. Наприклад, jQuery дозволяє легко додавати обробників подій програмно.

  2. Приклад, який ви подаєте, буде порушений у будь-якому користувальницькому агенті, який не підтримує JavaScript або вимкнено JavaScript. Концепція прогресивного вдосконалення заохочуватиме просте гіперпосилання на /map/користувацьких агентів без javascript, а потім додавати обробник кліків prgramatic для користувацьких агентів, які підтримують javascript.

Наприклад:

Розмітка:

<a id="example" href="/map/">link</a>

Javascript:

$(document).ready(function(){

    $("#example").click(function(){
        popup('/map/', 300, 300, 'map');
        return false;
    });

})

2
Дякую, що нагадали мені про прогресивне вдосконалення, і це все ще відповідає цій парадигмі:<a href="https://stackoverflow.com/map/" onclick="popup('/map/', 300, 300, 'map'); return false;">link</a>
Даніель Соколовський,

10

Перегляд

Ненав'язливий підхід JavaScript був хорошим у минулому - особливо посилання обробника подій у HTML вважалося поганою практикою (головним чином через onclick events run in the global scope and may cause unexpected errorте, що згадували YiddishNinja )

Однак ...

Наразі здається, що такий підхід трохи застарів і потребує певного оновлення. Якщо хтось хоче бути професійним розробником frontend та писати великі та складні додатки, тоді йому потрібно використовувати рамки, такі як Angular, Vue.js, тощо. Однак ці рамки зазвичай використовують (або дозволяють використовувати) HTML-шаблони, де пов'язують обробники подій в коді HTML-шаблону безпосередньо, і це дуже зручно, зрозуміло та ефективно - наприклад, у кутовому шаблоні люди зазвичай пишуть:

<button (click)="someAction()">Click Me</button> 

У raw js / html еквівалент цього буде

<button onclick="someAction()">Click Me</button>

Різниця полягає в тому, що в сирому js onclick подія запускається в глобальному масштабі - але рамки забезпечують інкапсуляцію.

То де проблема?

Проблема полягає в тому, коли початківець програміст, який завжди чув, що html-onclick поганий, і який завжди використовує, btn.addEventListener("onclick", ... )хоче використовувати якусь рамку з шаблонами ( addEventListenerтакож є недоліки - якщо ми оновимо DOM динамічним способом за допомогою innerHTML=(що досить швидко ) - тоді ми втрачаємо події обробники пов'язуються таким чином). Тоді він зіткнеться з чимось на зразок шкідливих звичок або неправильного підходу до використання фреймворку - і він буде використовувати рамки дуже погано - тому що він зосередиться в основному на js-частині, а не на частині шаблону (а також створює незрозумілі та важкі в обслуговуванні код). Щоб змінити ці звички, він втратить багато часу (і, ймовірно, йому знадобиться трохи удачі та вчителя).

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

То що робити?

Ми можемо ОБНОВЛИТИ ненав’язливий підхід JavaScript і дозволити зв'язувати обробники подій (зрештою, з простими параметрами) в html (але тільки прив'язувати обробник - не вводити логіку в onclick, як в ОП питання). Тож, на мою думку, у raw js / html це має бути дозволено

<button onclick="someAction(3)">Click Me</button>

або

Але нижче прикладів НЕ слід допускати

<button onclick="console.log('xx'); someAction(); return true">Click Me</button>

<a href="#" onclick="popup('/map/', 300, 300, 'map'); return false;">link</a>

Реальність змінюється, і наша точка зору повинна


привіт Kamil, я початківець у JavaScript, а також зіткнувся з плутаниною щодо використання onclick, тобто, будь ласка, поясніть недоліки onclick наступним чином: 1. Ви можете призначити лише одну вбудовану подію. 2. Вбудовані події зберігаються як властивість елемента DOM і, як і всі властивості об'єкта, вони можуть бути перезаписані. 3.Ця подія продовжується, навіть якщо ви видалите властивість onclick.
міржаль

1
Оголошення 1. Так, тільки одне, однак мій досвід (з кутовим) показує, що цього достатньо в більшості ситуацій - але якщо не, то просто використовувати addEventListener. Оголошення 2. Так, їх можна перезаписати (однак зазвичай це ніхто не робить) - де проблема? Оголошення 3. Обробник не буде звільнений після видалення onclick attrib - підтвердження тут
Kamil Kiełczewski

8

Це нова парадигма під назвою " Ненав'язливий JavaScript ". Нинішній "веб-стандарт" говорить про окремі функціональні можливості та презентацію.

Це насправді не "погана практика", це просто те, що більшість нових стандартів хочуть, щоб ви використовували слухачів подій замість вкладеного JavaScript.

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


2
Сторінка Вікіпедії на цю тему старше 4 років. Це вже не нова парадигма. :)
Квентін

@David: Це може бути не "новим", але люди все ще не користуються ним, тому це "нове" для них.
Ракета Хазмат

6

Я думаю, ваше запитання спровокує дискусію. Загальна думка полягає в тому, що добре розділяти поведінку та структуру. Крім того, afaik, обробник вбудованих клацань, повинен бути evalприведений до того, щоб він став справжньою функцією JavaScript. І це досить старомодно, але це доволі хиткий аргумент. Ну, добре, прочитайте про це @ quirksmode.org


Я не хочу створювати ще
одну

2
  • події onclick працюють у глобальному масштабі і можуть спричинити несподівані помилки.
  • Додавання подій onclick до багатьох елементів DOM уповільнить
    продуктивність та ефективність.

0

Ще дві причини не використовувати вбудовані обробники:

Вони можуть вимагати нудних проблем, що уникають цитати

З огляду на довільну рядок, якщо ви хочете мати можливість побудувати оброблювач вбудованого рядка, який викликає функцію за допомогою цього рядка, для загального рішення вам доведеться уникнути роздільників атрибутів (із пов’язаною суттю HTML), і ви повинні вийти з роздільника, який використовується для рядка всередині атрибута, наприклад:

const str = prompt('What string to display on click?', 'foo\'"bar');
const escapedStr = str
  // since the attribute value is going to be using " delimiters,
  // replace "s with their corresponding HTML entity:
  .replace(/"/g, '&quot;')
  // since the string literal inside the attribute is going to delimited with 's,
  // escape 's:
  .replace(/'/g, "\\'");
  
document.body.insertAdjacentHTML(
  'beforeend',
  '<button onclick="alert(\'' + escapedStr + '\')">click</button>'
);

Це неймовірно некрасиво. З наведеного вище прикладу, якщо ви не замінили 's, призведе SyntaxError, оскільки alert('foo'"bar')це невірний синтаксис. Якщо ви не замінили "s, браузер інтерпретував би його як закінчення onclickатрибута (з розмежуванням "s вище), що також було б невірно.

Якщо хтось звично використовує вбудовані обробники, потрібно буде обов'язково кожен раз робити щось подібне до вищезазначеного (і робити це правильно ) , що нудно і важко зрозуміти на перший погляд. Краще уникати вбудованих обробників повністю, щоб довільну рядок можна було використовувати в простому закритті:

const str = prompt('What string to display on click?', 'foo\'"bar');
const button = document.body.appendChild(document.createElement('button'));
button.textContent = 'click';
button.onclick = () => alert(str);

Хіба це не так приємніше?


Ланцюг застосування вбудованого обробника надзвичайно своєрідний

Як ви думаєте, що увійде наступний код?

let disabled = true;
<form>
  <button onclick="console.log(disabled);">click</button>
</form>

Спробуйте, запустіть фрагмент. Це, мабуть, не те, чого ви очікували. Чому він виробляє те, що робить? Тому що вбудовані обробники працюють всередині withблоків. Наведений вище код знаходиться у трьох with блоках: один для document, один для <form>, і один для <button>:

введіть тут опис зображення

Оскільки disabledє властивістю кнопки, посилання disabledвсередині вбудованого обробника відноситься до властивості кнопки, а не до зовнішньої disabledзмінної. Це досить контрінтуїтивно. withмає багато проблем: він може стати джерелом плутаних помилок і значно уповільнює код. У суворому режимі це взагалі не дозволено. Але з вбудованими обробниками ви змушені запускати код через withs - і не просто через один with, а через кілька вкладених withs. Це божевілля.

withніколи не слід використовувати в коді. Оскільки вбудовані обробники неявно вимагають withразом з усією його заплутаною поведінкою, слід уникати і вбудованих обробників.

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