Mengapa menggunakan onClick () dalam HTML merupakan praktik yang buruk?

133

Saya telah mendengar berkali-kali bahwa menggunakan peristiwa JavaScript, seperti onClick(), dalam HTML adalah praktik yang buruk, karena itu tidak baik untuk semantik. Saya ingin tahu apa kerugiannya dan bagaimana cara memperbaiki kode berikut?

<a href="#" onclick="popup('/map/', 300, 300, 'map'); return false;">link</a>
NiLL
sumber
4
Tidak ada yang salah dengan onlickc, tetapi dengan membuat href #, siapa pun yang memilih untuk menonaktifkan javascript akan mandek dan tidak dapat melakukan apa pun. Untuk beberapa situs itu adalah hal yang baik, tetapi hanya dengan membuka jendela, itu benar-benar bodoh untuk tidak memberikan tautan "nyata".
Marc B
2
Baca tautan itu dengan pasti. Ini hampir tidak ada hubungannya dengan semantik, dan lebih banyak lagi hubungannya dengan ... semua hal di halaman itu :-)
morewry
Cobalah untuk membuka tautan Anda di tab baru, dan Anda akan melihat contoh mengapa itu salah ...
Sebastien C.
2
Itu harus menjadi <button>, karena tautan seharusnya mengarah ke sumber daya yang sebenarnya. Itu lebih semantik seperti itu dan lebih jelas untuk pengguna pembaca layar.
programmer5000

Jawaban:

171

Anda mungkin berbicara tentang Javascript yang tidak mencolok , yang akan terlihat seperti ini:

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

dengan logika di file javascript pusat terlihat seperti ini:

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

Keuntungannya adalah

  • perilaku (Javascript) dipisahkan dari presentasi (HTML)
  • tidak ada campuran bahasa
  • Anda menggunakan kerangka kerja javascript seperti jQuery yang dapat menangani sebagian besar masalah lintas-browser untuk Anda
  • Anda dapat menambahkan perilaku ke banyak elemen HTML sekaligus tanpa duplikasi kode
Michael Borgwardt
sumber
30
Anda telah mendaftarkan beberapa keuntungan, tetapi bagaimana dengan kerugiannya? Membungkam asap biru?
abelito
2
@abelito: Tidak yakin debugging itu merugikan. Jika ada, itu merupakan keuntungan untuk debugging karena Anda dapat melangkah melalui JavaScript Anda dalam alat debugging browser apa pun. Tidak yakin Anda bisa melakukan itu semudah jika kode in-line an onClick. Anda juga dapat menulis unit test jika Anda ingin melawan skrip Anda yang juga sangat sulit saya asumsikan jika kode berada di dalam onClickdan tidak ada logika yang dipisahkan. Saya pribadi tidak memiliki masalah men-debug JavaScript yang tidak mengganggu dan manfaat dalam mengelola dan menguji kode JavaScript adalah jauh dari bagus untuk tidak menggunakannya.
Tidak
45
@ François Wahl: kerugian utama adalah dapat ditemukan: melihat HTML tidak memberi tahu Anda callback mana yang dilampirkan, bahkan tidak ada di mana pun.
Michael Borgwardt
1
@MichaelBorgwardt: Saya sepenuhnya setuju dengan Anda bahwa itu adalah kerugian. Jika kode terstruktur dan terorganisir dengan baik, saya tidak melihatnya sebagai masalah besar ketika mengerjakan proyek atau men-debug seseorang menggunakan basis kode. Secara umum, sementara saya setuju dengan Anda, saya tidak melihat kemampuan menemukan untuk menjadi alasan yang baik untuk menulis skrip in-line, mengorbankan manfaat seperti testability kode dan kemampuan untuk memisahkan fungsi / kode perilaku Anda dari DOM. Saya tidak mengatakan kode in-line itu buruk dan tidak akan berfungsi. Ini benar-benar dan benar-benar baik, tergantung apakah Anda tertarik pada hal-hal seperti testability atau tidak.
Tidak
4
Poin lain dari diskusi ini, dengan para pemula - yang onclickdapat secara lebih eksplisit memetakan hubungan sebab akibat antara interaksi elemen dan efek (pemanggilan fungsi) serta mengurangi beban kognitif. Banyak pelajaran yang melempar peserta didik ke dalam addEventListenerpaket ide yang lebih banyak di dalam sintaksis yang lebih sulit. Selanjutnya, kerangka kerja berbasis komponen baru-baru ini seperti Riot dan Bereaksi menggunakan onclickatribut dan mengubah gagasan tentang apa yang seharusnya menjadi pemisahan. Mentransfer dari HTML onclickke komponen yang mendukung ini mungkin merupakan proses "langkah" yang lebih efektif untuk belajar.
jmk2142
41

Jika Anda menggunakan jQuery maka:

HTML:

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

JS:

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

Ini memiliki manfaat tetap bekerja tanpa JS, atau jika pengguna mengklik tautan tersebut.

Ini juga berarti bahwa saya dapat menangani sembulan umum dengan menulis ulang lagi ke:

HTML:

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

JS:

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

Ini akan memungkinkan Anda menambahkan sembulan ke tautan apa pun dengan hanya memberikannya kelas sembulan.

Gagasan ini dapat diperluas lebih jauh seperti:

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;
    });
});

Sekarang saya dapat menggunakan sedikit kode yang sama untuk banyak popup di seluruh situs saya tanpa harus menulis banyak hal onclick! Yay untuk dapat digunakan kembali!

Ini juga berarti bahwa jika nanti saya memutuskan bahwa popup adalah praktik yang buruk, (yang memang demikian!) Dan saya ingin menggantinya dengan jendela modal gaya lightbox, saya dapat mengubah:

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

untuk

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

dan semua popup saya di seluruh situs saya sekarang bekerja dengan sangat berbeda. Saya bahkan bisa melakukan deteksi fitur untuk memutuskan apa yang harus dilakukan pada sembulan, atau menyimpan preferensi pengguna untuk mengizinkannya atau tidak. Dengan onclick inline, ini membutuhkan salinan dan upaya menempel yang sangat besar.

Bradshaw yang kaya
sumber
4
Bekerja tanpa JavaScript ?? Ini jQuery. Perlu Javascript diaktifkan di browser untuk berfungsi, bukan?
Thomas Shields
2
Ya, tetapi tautan dapat dialihkan ke halaman yang melakukan tindakan tanpa JavaScript dalam kasus ini.
ThiefMaster
12
The Link bekerja tanpa JavaScript. Lihat bagaimana tautan memiliki atribut href normal. Jika pengguna tidak memiliki JavaScript, tautan akan tetap berupa tautan dan pengguna masih dapat mengakses konten.
awal
3
@Thomas Shields: Tidak, maksudnya jika Anda tidak memiliki Javascript, tautan itu masih akan membawa Anda ke / map / ...
Robert
2
Ah Kaya! Kami semua mencintaimu untuk solusi terbaik ini!
Nirmal
21

Dengan aplikasi JavaScript yang sangat besar, pemrogram menggunakan lebih banyak enkapsulasi kode untuk menghindari polusi lingkup global. Dan untuk membuat fungsi tersedia untuk aksi onClick dalam elemen HTML, itu harus dalam lingkup global.

Anda mungkin telah melihat file JS yang terlihat seperti ini ...

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

Ini adalah Ekspresi Fungsi Segera Diminta (IIFE) dan fungsi apa pun yang dinyatakan di dalamnya hanya akan ada dalam lingkup internal mereka.

Jika Anda mendeklarasikan function doSomething(){}dalam IIFE, lalu membuat doSomething()aksi onClick suatu elemen di halaman HTML Anda, Anda akan mendapatkan kesalahan.

Sebaliknya, jika Anda membuat eventListener untuk elemen itu dalam IIFE itu dan menelepon doSomething()ketika pendengar mendeteksi acara klik, Anda baik karena pendengar dan doSomething()berbagi ruang lingkup IIFE.

Untuk aplikasi web kecil dengan jumlah kode minimal, tidak masalah. Tetapi jika Anda bercita-cita untuk menulis basis kode yang besar dan dapat dipelihara, onclick=""adalah kebiasaan yang harus Anda hindari.

LetMyPeopleCode
sumber
1
jika Anda bercita-cita untuk menulis basis kode yang besar dan dapat dikelola, gunakan kerangka kerja JS seperti Angular, Vue, React ... yang merekomendasikan untuk mengikat event handler di dalam templat HTML mereka
Kamil Kiełczewski
20

Ini tidak baik karena beberapa alasan:

  • itu mencampur kode dan markup
  • kode yang ditulis dengan cara ini melewati eval
  • dan berjalan dalam lingkup global

Hal paling sederhana adalah menambahkan nameatribut ke <a>elemen Anda , maka Anda bisa melakukannya:

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

meskipun praktik terbaik modern adalah menggunakan idalih - alih nama, dan menggunakan addEventListener()alih-alih menggunakan onclickkarena itu memungkinkan Anda untuk mengikat beberapa fungsi ke satu peristiwa.

Alnitak
sumber
Bahkan menyarankan cara ini membuka pengguna hingga banyak masalah dengan tabrakan pengendali event.
preaksi
3
@preaction sama seperti event handler inline, maka mengapa jawaban saya mengatakan lebih baik digunakan addEventListener(), dan mengapa.
Alnitak
2
Adakah yang bisa menjelaskan hal ini lebih lanjut? "kode yang ditulis dengan cara ini melewati eval" ... Saya tidak menemukan apa pun di web tentang ini. Apakah Anda mengatakan ada beberapa kelemahan kinerja atau keamanan untuk menggunakan Javascript inline?
MarsAndBack
12

Ada beberapa alasan:

  1. Saya merasa bantu untuk memisahkan markup, yaitu skrip HTML dan sisi klien. Misalnya, jQuery memudahkan untuk menambahkan penangan acara secara terprogram.

  2. Contoh yang Anda berikan akan rusak di agen pengguna apa pun yang tidak mendukung javascript, atau javascript dimatikan. Konsep peningkatan progresif akan mendorong hyperlink sederhana /map/untuk agen pengguna tanpa javascript, kemudian menambahkan penangan klik prgramatis untuk agen pengguna yang mendukung javascript.

Sebagai contoh:

Markup:

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

Javascript:

$(document).ready(function(){

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

})
Graham
sumber
2
Terima kasih telah mengingatkan saya pada peningkatan progresif dan jadi ini masih cocok dengan paradigma itu juga:<a href="https://stackoverflow.com/map/" onclick="popup('/map/', 300, 300, 'map'); return false;">link</a>
Daniel Sokolowski
10

Revisi

Pendekatan JavaScript yang tidak mencolok baik di MASA LALU - terutama event handler binding dalam HTML dianggap sebagai praktik buruk (terutama karena onclick events run in the global scope and may cause unexpected errorapa yang disebutkan oleh YiddishNinja )

Namun...

Saat ini tampaknya pendekatan ini agak ketinggalan jaman dan perlu beberapa pembaruan. Jika seseorang ingin menjadi pengembang frontend profesional dan menulis aplikasi yang besar dan rumit maka dia harus menggunakan kerangka kerja seperti Angular, Vue.js, dll. Namun kerangka itu biasanya menggunakan (atau memungkinkan untuk menggunakan) template-HTML di mana penangan acara terikat dalam kode html-template secara langsung dan ini sangat berguna, jelas dan efektif - misalnya dalam template sudut biasanya orang menulis:

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

Dalam js / html mentah, ini setara dengan

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

Perbedaannya adalah bahwa dalam onclickperistiwa js mentah dijalankan dalam lingkup global - tetapi kerangka kerja menyediakan enkapsulasi.

Jadi di mana masalahnya?

Masalahnya adalah ketika programmer pemula yang selalu mendengar bahwa html-onclick buruk dan yang selalu menggunakan btn.addEventListener("onclick", ... )ingin menggunakan beberapa kerangka kerja dengan template ( addEventListenerjuga memiliki kelemahan - jika kita memperbarui DOM secara dinamis menggunakan innerHTML=(yang cukup cepat ) - maka kita kehilangan acara penangan mengikat dengan cara itu). Kemudian dia akan menghadapi sesuatu seperti kebiasaan buruk atau pendekatan yang salah untuk penggunaan kerangka kerja - dan dia akan menggunakan kerangka kerja dengan cara yang sangat buruk - karena dia akan fokus terutama pada bagian js dan tidak pada bagian kerangka (dan menghasilkan tidak jelas dan sulit untuk mempertahankan kode). Untuk mengubah kebiasaan ini, ia akan kehilangan banyak waktu (dan mungkin ia akan membutuhkan keberuntungan dan guru).

Jadi menurut saya, berdasarkan pengalaman dengan siswa saya, lebih baik bagi mereka jika mereka menggunakan html-handler-bind di awal. Seperti yang saya katakan memang benar bahwa penangan dipanggil dalam lingkup global tetapi pada tahap ini siswa biasanya membuat aplikasi kecil yang mudah dikendalikan. Untuk menulis aplikasi yang lebih besar, mereka memilih beberapa kerangka kerja.

Jadi apa yang harus dilakukan?

Kita dapat MEMPERBARUI pendekatan JavaScript yang Tidak Mengganggu dan mengizinkan pengikat acara bind (akhirnya dengan parameter sederhana) dalam html (tetapi hanya pengikat ikatan - tidak memasukkan logika ke dalam klik seperti pada pertanyaan OP). Jadi menurut saya di js / html mentah ini harus diizinkan

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

atau

Tetapi contoh di bawah ini TIDAK boleh diizinkan

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

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

Kenyataannya berubah, sudut pandang kita juga harus

Kamil Kiełczewski
sumber
hai Kamil, saya pemula di JavaScript dan juga menghadapi kebingungan tentang penggunaan onclick, yaitu, bisakah Anda menjelaskan kerugian onclick sebagai berikut: 1. Anda hanya dapat memiliki satu acara inline yang ditugaskan. 2.Inline events disimpan sebagai properti dari elemen DOM dan, seperti semua properti objek, ia dapat ditimpa. 3.Peristiwa ini akan terus menyala meskipun Anda menghapus properti onclick.
mirzhal
1
Iklan 1. Ya hanya satu, namun pengalaman saya (dengan sudut) menunjukkan bahwa ini sudah cukup dalam kebanyakan situasi - namun jika tidak maka gunakan saja addEventListener. Iklan 2. Ya mereka bisa ditimpa (namun biasanya tidak ada yang melakukannya) - di mana masalahnya? Iklan 3. Handler tidak akan dipecat setelah menghapus attrib onclick - buktikan di sini
Kamil Kiełczewski
8

Ini adalah paradigma baru yang disebut " JavaScript Tidak Mengganggu ". "Standar web" saat ini mengatakan untuk memisahkan fungsi dan presentasi.

Ini sebenarnya bukan "praktik buruk", hanya saja sebagian besar standar baru ingin Anda menggunakan pendengar acara alih-alih menggunakan JavaScript.

Juga, ini mungkin hanya hal pribadi, tapi saya pikir ini jauh lebih mudah dibaca ketika Anda menggunakan pendengar acara, terutama jika Anda memiliki lebih dari 1 pernyataan JavaScript yang ingin Anda jalankan.

Roket Hazmat
sumber
2
Halaman Wikipedia pada subjek sudah lebih dari 4 tahun. Ini bukan paradigma baru lagi. :)
Quentin
@ David: Ini mungkin bukan "baru", tetapi masih ada orang yang tidak menggunakannya, jadi "baru" bagi mereka.
Rocket Hazmat
6

Pertanyaan Anda akan memicu diskusi saya kira. Gagasan umumnya adalah memisahkan perilaku dan struktur itu baik. Selanjutnya, afaik, penangan klik sebaris harus evaldiarahkan untuk 'menjadi' fungsi javascript nyata. Dan itu cukup kuno, meskipun itu argumen yang cukup goyah. Ah, baca beberapa tentangnya @ quirksmode.org

KooiInc
sumber
Saya tidak ingin membuat perang suci sekali lagi, saya mencoba untuk menemukan kebenaran: \
NiLL
2
  • onclick events berjalan dalam lingkup global dan dapat menyebabkan kesalahan tak terduga.
  • Menambahkan peristiwa onclick ke banyak elemen DOM akan memperlambat
    kinerja dan efisiensi.
Minghuan
sumber
0

Dua alasan lagi untuk tidak menggunakan penangan inline:

Mereka dapat meminta kutipan yang lolos dari masalah

Diberikan string sewenang - wenang , jika Anda ingin dapat membangun inline handler yang memanggil fungsi dengan string itu, untuk solusi umum, Anda harus melarikan diri dari pembatas atribut (dengan entitas HTML terkait), dan Anda akan harus lolos dari pembatas yang digunakan untuk string di dalam atribut, seperti berikut:

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>'
);

Itu sangat jelek. Dari contoh di atas, jika Anda tidak mengganti 's, SyntaxError akan menghasilkan, karena alert('foo'"bar')bukan sintaks yang valid. Jika Anda tidak mengganti "s, maka peramban akan menafsirkannya sebagai akhir onclickatribut (dibatasi dengan "s di atas), yang juga akan salah.

Jika seseorang terbiasa menggunakan penangan inline, ia harus memastikan untuk mengingat melakukan sesuatu yang mirip dengan yang di atas (dan melakukannya dengan benar ) setiap kali , yang membosankan dan sulit untuk dipahami dalam sekejap. Lebih baik untuk menghindari penangan inline sepenuhnya sehingga string sewenang-wenang dapat digunakan dalam penutupan sederhana:

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);

Bukankah itu jauh lebih baik?


Rantai lingkup penangan inline sangat aneh

Menurut Anda apa yang akan dicatat oleh kode berikut?

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

Cobalah, jalankan cuplikannya. Mungkin bukan itu yang Anda harapkan. Mengapa itu menghasilkan apa yang dilakukannya? Karena penangan inline dijalankan di dalam withblok. Kode di atas ada di dalam tiga with blok: satu untuk document, satu untuk <form>, dan satu untuk <button>:

masukkan deskripsi gambar di sini

Karena disabledmerupakan properti tombol, referensi disableddi dalam inline handler mengacu pada properti tombol, bukan disabledvariabel luar . Ini cukup kontra-intuitif. withmemiliki banyak masalah: ini dapat menjadi sumber bug yang membingungkan dan secara signifikan memperlambat kode. Bahkan tidak diizinkan sama sekali dalam mode ketat. Tetapi dengan penangan inline, Anda dipaksa untuk menjalankan kode melalui withs - dan bukan hanya melalui satu with, tetapi melalui beberapa withs bersarang . Ini gila.

withtidak boleh digunakan dalam kode. Karena penangan inline secara implisit memerlukan withbersama dengan semua perilakunya yang membingungkan, penangan inline harus dihindari juga.

Performa Tertentu
sumber