Tại sao sử dụng onClick () trong HTML là một thực tiễn xấu?


133

Tôi đã nghe nhiều lần rằng sử dụng các sự kiện JavaScript, chẳng hạn như onClick()trong HTML là một thực tiễn tồi, bởi vì nó không tốt cho ngữ nghĩa. Tôi muốn biết nhược điểm là gì và cách khắc phục mã sau đây?

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


4
Không có gì sai với onlickc, nhưng bằng cách tạo href #, bất cứ ai chọn tắt javascript sẽ bị kẹt và không thể làm gì. Đối với một số trang web đó là một điều tốt, nhưng chỉ đơn giản là mở một cửa sổ, thật ngu ngốc khi không cung cấp một liên kết "thực".
Marc B

2
Chắc chắn đọc liên kết đó. Nó có rất ít liên quan đến ngữ nghĩa, và nhiều thứ khác liên quan đến ... tất cả những thứ trên trang đó :-)
thêm vào

Hãy thử mở liên kết của bạn trong một tab mới và bạn sẽ thấy một ví dụ về lý do tại sao nó sai ...
Sebastien C.

2
Đó phải là một <button>, bởi vì các liên kết được cho là trỏ đến một tài nguyên thực tế. Đó là ngữ nghĩa nhiều hơn theo cách đó và rõ ràng hơn cho người dùng trình đọc màn hình.
lập trình

Câu trả lời:


171

Có lẽ bạn đang nói về Javascript không phô trương , sẽ giống như thế này:

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

với logic trong một tệp javascript trung tâm trông giống như thế này:

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

Những ưu điểm là

  • hành vi (Javascript) được tách ra khỏi bản trình bày (HTML)
  • không pha trộn ngôn ngữ
  • bạn đang sử dụng khung javascript như jQuery có thể xử lý hầu hết các sự cố trình duyệt chéo cho bạn
  • Bạn có thể thêm hành vi vào nhiều phần tử HTML cùng một lúc mà không cần sao chép mã

30
Bạn đã liệt kê khá nhiều ưu điểm, nhưng còn nhược điểm thì sao? Gỡ lỗi khói xanh?
abelito

2
@abelito: Không chắc chắn gỡ lỗi là một bất lợi. Nếu bất cứ điều gì đó là một lợi thế để gỡ lỗi vì bạn có thể bước qua JavaScript của mình trong bất kỳ công cụ gỡ lỗi trình duyệt nào. Không chắc chắn bạn có thể làm điều đó dễ dàng nếu mã nằm trong một dòng onClick. Bạn cũng có thể viết các bài kiểm tra đơn vị nếu bạn muốn chống lại các tập lệnh của mình, điều này cũng rất khó tôi giả sử nếu mã nằm trong một onClickvà không có logic nào được tách ra. Cá nhân tôi không có vấn đề gì trong việc gỡ lỗi JavaScript không phô trương và những lợi ích trong việc quản lý và kiểm tra mã JavaScript là rất tốt để không sử dụng nó.
Không có

45
@ François Wahl: nhược điểm chính là khả năng khám phá: nhìn vào HTML không cho bạn biết các cuộc gọi lại được đính kèm với nó, thậm chí không có bất kỳ điều gì khác.
Michael Borgwardt

1
@MichaelBorgwardt: Tôi hoàn toàn đồng ý với bạn rằng đó là một bất lợi. Nếu mã được cấu trúc và tổ chức tốt, tôi không thấy đó là một vấn đề lớn khi làm việc trong một dự án hoặc gỡ lỗi cơ sở mã của ai đó. Nói chung, trong khi tôi đồng ý với bạn, tôi không thấy khả năng khám phá là một lý do chính đáng để viết tập lệnh nội tuyến, các lợi ích bí mật như khả năng kiểm tra mã và khả năng tách mã chức năng / hành vi của bạn khỏi DOM. Tôi không nói mã nội tuyến là xấu và sẽ không hoạt động. Nó làm và hoàn toàn tốt, tùy thuộc vào việc bạn có quan tâm đến những thứ như khả năng kiểm tra hay không.
Không có

4
Một điểm khác cho cuộc thảo luận này, với người mới - onclickcó thể ánh xạ rõ ràng hơn mối quan hệ nhân quả giữa tương tác thành phần và hiệu ứng (gọi hàm) cũng như giảm tải nhận thức. Nhiều bài học ném người học vào addEventListenerđó gói nhiều ý tưởng hơn trong một cú pháp khó hơn. Hơn nữa, các khung dựa trên thành phần gần đây như Riot và React sử dụng onclickthuộc tính và đang thay đổi khái niệm về sự phân tách nên là gì. Chuyển từ HTML onclicksang một thành phần hỗ trợ điều này có thể là một quá trình "bước" hiệu quả hơn cho việc học.
jmk2142

41

Nếu bạn đang sử dụng jQuery thì:

HTML:

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

JS:

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

Điều này có lợi ích là vẫn hoạt động mà không có JS hoặc nếu người dùng ở giữa nhấp vào liên kết.

Điều đó cũng có nghĩa là tôi có thể xử lý các quảng cáo chung bằng cách viết lại thành:

HTML:

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

JS:

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

Điều này sẽ cho phép bạn thêm một cửa sổ bật lên vào bất kỳ liên kết nào bằng cách chỉ cho nó lớp bật lên.

Ý tưởng này có thể được mở rộng hơn nữa như vậy:

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

Bây giờ tôi có thể sử dụng cùng một đoạn mã cho nhiều cửa sổ bật lên trên toàn bộ trang web của mình mà không phải viết vô số nội dung onclick! Yay cho tái sử dụng!

Điều đó cũng có nghĩa là nếu sau này tôi quyết định rằng cửa sổ bật lên là thông lệ xấu, (đó là!) Và tôi muốn thay thế chúng bằng cửa sổ phương thức kiểu hộp đèn, tôi có thể thay đổi:

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

đến

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

và tất cả các quảng cáo của tôi trên toàn bộ trang web của tôi hiện đang hoạt động hoàn toàn khác nhau. Tôi thậm chí có thể thực hiện phát hiện tính năng để quyết định những việc cần làm trên cửa sổ bật lên hoặc lưu trữ tùy chọn người dùng để cho phép họ hay không. Với onclick nội tuyến, điều này đòi hỏi một nỗ lực sao chép và dán rất lớn.


4
Hoạt động mà không có JavaScript ?? Đó là jQuery. Nó cần Javascript kích hoạt trên trình duyệt để hoạt động, phải không?
Thomas Shields

2
Có, nhưng liên kết có thể chuyển hướng đến một trang thực hiện hành động mà không cần JavaScript trong trường hợp này.
ThiefMaster

12
Các liên kết hoạt động mà không Javascript. Xem cách liên kết có thuộc tính href. Nếu người dùng không có JavaScript, liên kết sẽ vẫn là liên kết và người dùng vẫn có thể truy cập nội dung.
thêm

3
@Thomas Shields: Không, anh ta có nghĩa là nếu bạn không có Javascript, liên kết vẫn sẽ đưa bạn đến / map / ...
Robert

2
A Giàu! Chúng tôi đều yêu bạn vì giải pháp nhanh nhất này!
Nertal

21

Với các ứng dụng JavaScript rất lớn, các lập trình viên đang sử dụng nhiều mã đóng gói hơn để tránh gây ô nhiễm phạm vi toàn cầu. Và để làm cho một chức năng có sẵn cho hành động onClick trong một phần tử HTML, nó phải nằm trong phạm vi toàn cầu.

Bạn có thể đã thấy các tệp JS trông như thế này ...

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

Đây là các biểu thức hàm được gọi ngay lập tức (IIFE) và bất kỳ hàm nào được khai báo trong chúng sẽ chỉ tồn tại trong phạm vi bên trong của chúng.

Nếu bạn khai báo function doSomething(){}trong IIFE, sau đó thực hiện doSomething()hành động onClick của một thành phần trong trang HTML của bạn, bạn sẽ gặp lỗi.

Mặt khác, nếu bạn tạo một eventListener cho phần tử đó trong IIFE đó và gọi doSomething()khi người nghe phát hiện ra một sự kiện nhấp chuột, thì bạn tốt vì người nghe và doSomething()chia sẻ phạm vi của IIFE.

Đối với các ứng dụng web nhỏ với số lượng mã tối thiểu, điều đó không thành vấn đề. Nhưng nếu bạn khao khát viết những cơ sở mã lớn, có thể duy trì, onclick=""là một thói quen mà bạn nên làm việc để tránh.


1
nếu bạn muốn viết các cơ sở mã lớn, có thể bảo trì, hãy sử dụng các khung JS như Angular, Vue, React ... khuyên bạn nên liên kết các trình xử lý sự kiện bên trong các mẫu HTML của họ
Kamil Kiełczewski

20

Nó không tốt vì nhiều lý do:

  • nó trộn mã và đánh dấu
  • mã được viết theo cách này đi qua eval
  • và chạy trong phạm vi toàn cầu

Điều đơn giản nhất là thêm một namethuộc tính cho thành <a>phần của bạn , sau đó bạn có thể làm:

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

mặc dù cách thực hành tốt nhất hiện đại sẽ là sử dụng idthay vì tên và sử dụng addEventListener()thay vì sử dụng onclickvì điều đó cho phép bạn liên kết nhiều chức năng với một sự kiện.


Thậm chí gợi ý cách này còn mở ra cho người dùng một loạt các vấn đề với các va chạm xử lý sự kiện.
thiệu

3
@preaction giống như một trình xử lý sự kiện nội tuyến, do đó, tại sao câu trả lời của tôi nói rằng nó tốt hơn để sử dụng addEventListener()và tại sao.
Alnitak

2
Ai đó có thể giải thích điều này nhiều hơn? "Mã được viết theo cách này đi qua eval" ... Tôi không tìm thấy gì trên web về điều này. Bạn có nói rằng có một số bất lợi về hiệu suất hoặc bảo mật khi sử dụng Javascript nội tuyến không?
MarsAndBack

12

Có một vài lý do:

  1. Tôi thấy nó giúp duy trì việc đánh dấu riêng biệt, tức là các tập lệnh HTML và phía máy khách. Ví dụ, jQuery giúp dễ dàng thêm các trình xử lý sự kiện theo chương trình.

  2. Ví dụ bạn đưa ra sẽ bị hỏng trong bất kỳ tác nhân người dùng nào không hỗ trợ javascript hoặc đã tắt javascript. Khái niệm tăng cường lũy ​​tiến sẽ khuyến khích một siêu liên kết đơn giản /map/cho các tác nhân người dùng không có javascript, sau đó thêm trình xử lý nhấp chuột theo chương trình cho các tác nhân người dùng hỗ trợ javascript.

Ví dụ:

Đánh dấu:

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

Javascript:

$(document).ready(function(){

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

})

2
Cảm ơn đã nhắc nhở tôi về việc tăng cường tiến bộ và vì vậy điều này vẫn phù hợp với mô hình đó:<a href="https://stackoverflow.com/map/" onclick="popup('/map/', 300, 300, 'map'); return false;">link</a>
Daniel Sokolowski

10

Sửa đổi

Cách tiếp cận JavaScript không phô trương là tốt trong PAST - đặc biệt là trình xử lý sự kiện liên kết trong HTML được coi là thực tiễn xấu (chủ yếu vì onclick events run in the global scope and may cause unexpected errornhững gì được đề cập bởi YiddishNinja )

Tuy nhiên...

Hiện tại có vẻ như cách tiếp cận này đã hơi lỗi thời và cần một số cập nhật. Nếu ai đó muốn trở thành nhà phát triển frontend chuyên nghiệp và viết các ứng dụng lớn và phức tạp thì anh ta cần sử dụng các khung như Angular, Vue.js, v.v ... Tuy nhiên, các khung đó thường sử dụng (hoặc cho phép sử dụng) các mẫu HTML nơi trình xử lý sự kiện được liên kết trong mã mẫu html trực tiếp và điều này rất tiện dụng, rõ ràng và hiệu quả - ví dụ: trong mẫu góc thường mọi người viết:

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

Trong js / html thô tương đương với điều này sẽ là

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

Sự khác biệt là trong onclicksự kiện js thô được chạy trong phạm vi toàn cầu - nhưng các khung cung cấp đóng gói.

Vậy vấn đề nằm ở đâu?

Vấn đề là khi lập trình viên mới làm quen luôn nghe rằng html-onclick rất tệ và những người luôn sử dụng btn.addEventListener("onclick", ... )muốn sử dụng một số khung công tác với các mẫu ( addEventListenercũng có nhược điểm - nếu chúng tôi cập nhật DOM theo cách động bằng cách sử dụng innerHTML=(khá nhanh ) - thì chúng tôi sẽ mất các sự kiện xử lý ràng buộc theo cách đó). Sau đó, anh ta sẽ phải đối mặt với những thứ như thói quen xấu hoặc cách tiếp cận sai đối với việc sử dụng khung - và anh ta sẽ sử dụng khung theo cách rất xấu - bởi vì anh ta sẽ tập trung chủ yếu vào phần js và không tập trung vào phần mẫu (và sản xuất không rõ ràng và khó bảo trì mã). Để thay đổi thói quen này, anh ta sẽ mất rất nhiều thời gian (và có lẽ anh ta sẽ cần một chút may mắn và giáo viên).

Vì vậy, theo tôi, dựa trên kinh nghiệm với các sinh viên của tôi, sẽ tốt hơn cho họ nếu họ sử dụng trình xử lý html-ràng buộc ngay từ đầu. Như tôi nói đúng là các trình xử lý được gọi trong phạm vi toàn cầu, nhưng giai đoạn này sinh viên thường tạo ra các ứng dụng nhỏ dễ kiểm soát. Để viết các ứng dụng lớn hơn, họ chọn một số khung.

Vậy lam gi?

Chúng tôi có thể CẬP NHẬT cách tiếp cận JavaScript không phô trương và cho phép các trình xử lý sự kiện liên kết (cuối cùng với các tham số đơn giản) trong html (nhưng chỉ xử lý liên kết - không đưa logic vào onclick như trong OP quesiton). Vì vậy, theo tôi trong js / html thô, điều này nên được cho phép

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

hoặc là

Nhưng những ví dụ dưới đây KHÔNG được phép

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

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

Thực tế thay đổi, quan điểm của chúng tôi cũng nên


xin chào Kamil, tôi là người mới bắt đầu sử dụng JavaScript và cũng gặp phải sự nhầm lẫn khi sử dụng onclick, nghĩa là, xin vui lòng giải thích những nhược điểm của onclick như sau: 1. Bạn chỉ có thể có một sự kiện nội tuyến được chỉ định. 2. Các sự kiện nội tuyến được lưu trữ như một thuộc tính của phần tử DOM và, giống như tất cả các thuộc tính đối tượng, nó có thể được ghi đè. 3. Sự kiện này sẽ tiếp tục kích hoạt ngay cả khi bạn xóa thuộc tính onclick.
mirzhal

1
Quảng cáo 1. Có chỉ một, tuy nhiên kinh nghiệm của tôi (với góc cạnh) cho thấy rằng điều này là đủ trong hầu hết các tình huống - tuy nhiên nếu không thì chỉ cần sử dụng addEventListener. Quảng cáo 2. Có, chúng có thể được ghi đè (tuy nhiên thường không ai làm điều đó) - vấn đề nằm ở đâu? Quảng cáo 3. Handler sẽ không bị sa thải sau khi xóa onclick attrib - bằng chứng ở đây
Kamil Kiełczewski

8

Đó là một mô hình mới gọi là " JavaScript không phô trương ". "Tiêu chuẩn web" hiện tại nói về chức năng và cách trình bày riêng biệt.

Nó không thực sự là một "thực hành xấu", chỉ là hầu hết các tiêu chuẩn mới muốn bạn sử dụng trình lắng nghe sự kiện thay vì JavaScript liên tục.

Ngoài ra, đây có thể chỉ là một vấn đề cá nhân, nhưng tôi nghĩ nó dễ đọc hơn nhiều khi bạn sử dụng trình lắng nghe sự kiện, đặc biệt nếu bạn có nhiều hơn 1 câu lệnh JavaScript mà bạn muốn chạy.


2
Trang Wikipedia về chủ đề này đã hơn 4 tuổi. Nó không phải là một mô hình mới nữa. :)
Quentin

@David: Nó có thể không phải là "mới", nhưng có những người vẫn không sử dụng nó, vì vậy nó "mới" đối với họ.
Tên lửa Hazmat

6

Câu hỏi của bạn sẽ kích hoạt thảo luận tôi cho rằng. Ý tưởng chung là tốt để phân tách hành vi và cấu trúc. Hơn nữa, afaik, một trình xử lý nhấp chuột nội tuyến phải được evaldẫn đến 'trở thành' một chức năng javascript thực sự. Và nó khá lỗi thời, mặc dù đó là một cuộc tranh luận khá run rẩy. À, tốt, đọc một số về nó @ quirksmode.org


Tôi không muốn tạo thêm một lần chiến tranh thần thánh nữa, tôi đang cố gắng tìm ra sự thật: \
NiLL

2
  • các sự kiện onclick chạy trong phạm vi toàn cầu và có thể gây ra lỗi không mong muốn.
  • Thêm các sự kiện onclick vào nhiều thành phần DOM sẽ làm chậm
    hiệu suất và hiệu quả.

0

Hai lý do nữa để không sử dụng trình xử lý nội tuyến:

Họ có thể yêu cầu trích dẫn tẻ nhạt thoát khỏi các vấn đề

Đưa ra một chuỗi tùy ý , nếu bạn muốn có thể xây dựng một trình xử lý nội tuyến gọi một hàm với chuỗi đó, đối với giải pháp chung, bạn sẽ phải thoát các dấu phân cách thuộc tính (với thực thể HTML được liên kết) bạn sẽ phải thoát khỏi dấu phân cách được sử dụng cho chuỗi bên trong thuộc tính, như sau:

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

Điều đó cực kỳ xấu xí. Từ ví dụ trên, nếu bạn không thay thế 's, một SyntaxError sẽ có kết quả, vì đó alert('foo'"bar')không phải là cú pháp hợp lệ. Nếu bạn không thay thế "s, thì trình duyệt sẽ hiểu nó là phần cuối của onclickthuộc tính (được phân cách bằng" s ở trên), điều này cũng không chính xác.

Nếu một người thường xuyên sử dụng các trình xử lý nội tuyến, người ta sẽ phải nhớ đảm bảo làm điều gì đó tương tự như trên (và thực hiện đúng ) mỗi lần , điều này tẻ nhạt và khó hiểu trong nháy mắt. Tốt hơn là tránh các trình xử lý nội tuyến hoàn toàn để chuỗi tùy ý có thể được sử dụng trong một bao đóng đơn giản:

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

Nó không đẹp hơn sao?


Chuỗi phạm vi của một trình xử lý nội tuyến là rất đặc biệt

Bạn nghĩ mã nào sau đây sẽ đăng nhập?

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

Hãy thử nó, chạy đoạn trích. Nó có thể không phải là những gì bạn đang mong đợi. Tại sao nó sản xuất những gì nó làm? Bởi vì xử lý nội tuyến chạy bên trong withcác khối. Đoạn mã trên nằm trong ba with khối: một cho document, một cho <form>và một cho <button>:

nhập mô tả hình ảnh ở đây

disabledlà một thuộc tính của nút, nên tham chiếu disabledbên trong trình xử lý nội tuyến đề cập đến thuộc tính của nút chứ không phải disabledbiến ngoài . Điều này khá phản trực quan. withcó nhiều vấn đề: nó có thể là nguồn gây ra lỗi khó hiểu và làm chậm đáng kể mã. Nó thậm chí không được phép ở chế độ nghiêm ngặt. Nhưng với các trình xử lý nội tuyến, bạn buộc phải chạy mã qua withs - và không chỉ qua một with, mà qua nhiều withs lồng nhau . Thật là điên rồ.

withkhông bao giờ nên được sử dụng trong mã. Bởi vì trình xử lý nội tuyến hoàn toàn yêu cầu withcùng với tất cả các hành vi khó hiểu của nó, nên xử lý nội tuyến cũng nên tránh.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.