HTML'de onClick () kullanmak neden kötü bir uygulama?


133

onClick()HTML gibi JavaScript etkinliklerini kullanmanın kötü bir uygulama olduğunu birçok kez duydum , çünkü anlambilim için iyi değil. Olumsuz yönlerin ne olduğunu ve aşağıdaki kodu nasıl düzelteceğimi bilmek isterim?

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


4
Onlickc ile ilgili yanlış bir şey yok, ancak href oluşturarak #, javascript'i devre dışı bırakmayı seçen herkes takılıp kalacak ve hiçbir şey yapamayacak. Bazı siteler için bu iyi bir şey, ancak sadece bir pencere açmak için, "gerçek" bir bağlantı sağlamamak düpedüz aptalca.
Marc B

2
Bu bağlantıyı kesinlikle okuyun. Bunun anlambilimle çok az ilgisi var ve daha çok ... o sayfadaki tüm şeyler :-)
morewry

Bağlantınızı yeni bir sekmede açmaya çalıştığınızda neden yanlış olduğuna dair bir örnek göreceksiniz ...
Sebastien C.

2
Bu bir olmalıdır <button>, çünkü bağlantıların gerçek bir kaynağı göstermesi gerekir. Bu şekilde daha anlamsal ve ekran okuyucu kullanıcıları için daha anlaşılır.
programmer5000

Yanıtlar:


171

Muhtemelen şuna benzeyen göze batmayan Javascript'ten bahsediyorsunuz :

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

aşağıdaki gibi görünen merkezi bir javascript dosyasındaki mantıkla:

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

Avantajlar

  • davranış (Javascript) sunumdan (HTML) ayrılmıştır
  • dillerin karıştırılması yok
  • sizin için tarayıcılar arası sorunların çoğunu çözebilecek jQuery gibi bir javascript çerçevesi kullanıyorsunuz
  • Aynı anda birçok HTML öğesine, kod kopyası olmadan davranış ekleyebilirsiniz

30
Birkaç avantajı sıraladınız, peki ya dezavantajlar? Mavi duman hata ayıklama mı?
abelito

2
@abelito: Hata ayıklamanın bir dezavantaj olduğundan emin değilim. Herhangi bir şey varsa, herhangi bir tarayıcı hata ayıklama aracında JavaScript'inizde ilerleyebileceğiniz için hata ayıklamanın bir avantajı vardır. Kod satır içi ise bunu bu kadar kolay yapabileceğinizden emin değilim onClick. Ayrıca, komut dosyalarınıza karşı isterseniz birim testleri de yazabilirsiniz ki bu da çok zordur, kodun bir içinde onClickolup olmadığını ve mantığın ayrılmadığını varsayarım . Şahsen göze batmayan JavaScript'te hata ayıklama konusunda bir sorunum yok ve JavaScript kodunu yönetme ve test etmenin avantajları onu kullanmamak için çok büyük.
Hayır

45
@ François Wahl: ana dezavantaj keşfedilebilirliktir: HTML'ye bakmak size hangi geri aramaların eklendiğini, nerede olduğunu bile söylemez.
Michael Borgwardt

1
@MichaelBorgwardt: Bunun bir dezavantaj olduğuna tamamen katılıyorum. Kod iyi yapılandırılmış ve organize edilmişse, bir proje üzerinde çalışırken veya başka birinin kod tabanında hata ayıklarken bunu büyük bir sorun olarak görmüyorum. Genel olarak, sizinle aynı fikirde olsam da, keşfedilebilirliği satır içi komut dosyası yazmak için iyi bir neden olarak görmüyorum, kodun test edilebilirliği ve işlev / davranış kodunuzu DOM'dan ayırma yeteneği gibi faydalardan ödün veriyorum. Satır içi kodun kötü olduğunu ve işe yaramayacağını söylemiyorum. Test edilebilirlik gibi şeylerle ilgilenip ilgilenmediğinize bağlı olarak, kesinlikle iyidir ve kesinlikle iyidir.
Hayır

4
Bu tartışmanın bir başka noktası, acemilerle - onclicköğe etkileşimi ve etki (işlev çağrısı) arasındaki nedensel ilişkileri daha açık bir şekilde haritalayabilir ve bilişsel yükü azaltabilir. Pek çok ders, öğrencileri addEventListenerdaha zor bir sözdizimi içinde çok daha fazla fikir barındıran içine atar . Dahası, Riot ve React gibi son bileşen tabanlı çerçeveler bu onclicközelliği kullanıyor ve ayırmanın ne olması gerektiği fikrini değiştiriyor. HTML'den onclickbunu destekleyen bir bileşene aktarma, öğrenme için daha etkili bir "adım" süreci olabilir.
jmk2142

41

JQuery kullanıyorsanız:

HTML:

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

JS:

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

Bu, hala JS olmadan çalışma avantajına sahiptir veya kullanıcı bağlantıya orta tıklarsa.

Aynı zamanda, genel pop-up'ları şuna yeniden yazarak işleyebileceğim anlamına da geliyor:

HTML:

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

JS:

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

Bu, herhangi bir bağlantıya sadece pop-up sınıfı vererek bir pop-up eklemenize izin verir.

Bu fikir şu şekilde daha da genişletilebilir:

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

Artık sitemin tamamında birçok pop-up için aynı kod parçasını onclick ile bir sürü şey yazmak zorunda kalmadan kullanabilirim! Yeniden kullanılabilirlik için Yaş!

Ayrıca, daha sonra pop-up'ların kötü bir uygulama olduğuna (ki bunlar!) Ve onları bir ışık kutusu tarzı kalıcı pencereyle değiştirmek istediğime karar verirsem, değiştirebileceğim anlamına gelir:

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

için

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

ve tüm sitemdeki tüm açılır pencerelerim artık tamamen farklı çalışıyor. Bir pop-up'ta ne yapılacağına karar vermek için özellik algılaması yapabilir veya onlara izin verip vermeme tercihlerini saklayabilirim. Satır içi onclick ile bu, büyük bir kopyalama ve yapıştırma çabası gerektirir.


4
JavaScript olmadan çalışır ?? Bu jQuery. Çalışması için tarayıcıda Javascript'in etkinleştirilmesi gerekiyor, değil mi?
Thomas Shields

2
Evet, ancak bu durumda bağlantı, eylemi JavaScript olmadan gerçekleştiren bir sayfaya yönlendirebilir.
ThiefMaster

12
Bağlantı JavaScript olmadan çalışır. Bağlantının nasıl normal bir href özelliğine sahip olduğuna bakın. Kullanıcının JavaScript'i yoksa, bağlantı yine de bir bağlantı olur ve kullanıcı içeriğe yine de erişebilir.
morewry

3
@Thomas Shields: Hayır, Javascript'iniz yoksa, bağlantının sizi yine de / map / ... adresine götüreceği anlamına geliyor.
Robert

2
Ah Rich! Bu en güzel çözüm için hepimiz sizi seviyoruz!
Nirmal

21

Çok büyük JavaScript uygulamalarında, programcılar küresel kapsamı kirletmekten kaçınmak için daha fazla kod kapsülleme kullanıyor. Ve bir HTML öğesindeki onClick eylemine bir işlev sunmak için, genel kapsamda olması gerekir.

Buna benzeyen JS dosyaları görmüş olabilirsiniz ...

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

Bunlar Anında Çağrılan İşlev İfadeleridir (IIFE'ler) ve bunların içinde bildirilen herhangi bir işlev yalnızca kendi iç kapsamları içinde var olacaktır.

Bir function doSomething(){}IIFE içinde beyan ederseniz, ardından doSomething()HTML sayfanızda bir öğenin onClick eylemini yaparsanız, bir hata alırsınız.

Öte yandan, o IIFE içinde bu öğe için bir olay listesi oluşturur doSomething()ve dinleyici bir tıklama olayı algıladığında çağırırsanız , dinleyici ve doSomething()IIFE'nin kapsamını paylaştığı için iyi olursunuz.

Minimum miktarda kod içeren küçük web uygulamaları için önemli değil. Ancak büyük, sürdürülebilir kod tabanları yazmak onclick=""istiyorsanız, kaçınmak için çalışmanız gereken bir alışkanlıktır.


1
Büyük, bakımı yapılabilir kod tabanları yazmak istiyorsanız Angular, Vue, React gibi JS çerçeveleri kullanın ... bu da olay işleyicileri HTML şablonlarının içine bağlamanızı tavsiye eder
Kamil Kiełczewski

20

Birkaç nedenden dolayı iyi değil:

  • kodu ve işaretlemeyi karıştırır
  • bu şekilde yazılan kod geçer eval
  • ve küresel kapsamda çalışır

En basit şey, öğenize bir nameöznitelik eklemektir <a>, sonra şunları yapabilirsiniz:

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

modern en iyi uygulama id, bir ad yerine bir kullanmak ve kullanmak addEventListener()yerine kullanmak olabilir, onclickçünkü bu, birden çok işlevi tek bir olaya bağlamanıza olanak tanır.


Hatta bu şekilde önermek bile kullanıcıyı olay işleyici çarpışmalarıyla ilgili bir dizi soruna açar.
ön tepkili

3
@preaction tıpkı bir satır içi olay işleyicisinin yaptığı gibi, bu yüzden cevabım neden daha iyi olduğunu söylüyor addEventListener()ve neden.
Alnitak

2
Birisi bunu daha fazla açıklayabilir mi? "Bu şekilde yazılan kod eval'dan geçer" ... Web'de bununla ilgili hiçbir şey bulamadım. Satır içi Javascript kullanmanın bazı performans veya güvenlik dezavantajları olduğunu mu söylüyorsunuz?
MarsAndBack

12

Bir kaç neden var:

  1. Bunun, işaretlemeyi, yani HTML ve istemci tarafı komut dosyalarını ayırmaya yardımcı olduğunu düşünüyorum. Örneğin, jQuery , programlı bir şekilde olay işleyicileri eklemeyi kolaylaştırır.

  2. Verdiğiniz örnek, javascript'i desteklemeyen veya javascript kapalı olan herhangi bir kullanıcı aracısında çalışmayacaktır. Aşamalı geliştirme kavramı, /map/javascript içermeyen kullanıcı aracıları için basit bir köprüyü teşvik eder , ardından javascript'i destekleyen kullanıcı aracıları için prgramatik olarak bir tıklama işleyici ekler.

Örneğin:

İşaretleme:

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

JavaScript:

$(document).ready(function(){

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

})

2
Bana ilerici geliştirmeyi hatırlattığınız için teşekkürler ve bu nedenle bu paradigmaya hala uyuyor:<a href="https://stackoverflow.com/map/" onclick="popup('/map/', 300, 300, 'map'); return false;">link</a>
Daniel Sokolowski

10

Revizyon

Göze çarpmayan JavaScript yaklaşımı GEÇMİŞ'te iyiydi - özellikle HTML'deki olay işleyici bağlama kötü uygulama olarak görülüyordu (esas onclick events run in the global scope and may cause unexpected errorolarak YiddishNinja tarafından bahsedildiği için )

Ancak...

Şu anda görünen o ki bu yaklaşım biraz modası geçmiş ve biraz güncelleme gerektiriyor. Birisi profesyonel önyüzü developper olmak istiyorum ve o çerçeveler genellikle kullanılması (veya kullanılmaması izin verin) Ancak bu ... vb açısal, Vue.js gibi çerçeveler kullanmak gerekir sonra büyük ve komplike uygulamalar yazarsan HTML şablonları olay işleyicileri bağlamak nerede doğrudan html şablon kodunda ve bu çok kullanışlı, açık ve etkilidir - örneğin açısal şablonda genellikle insanlar şunları yazar:

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

Ham js / html'de bunun eşdeğeri olacaktır

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

Aradaki fark, ham js'de onclick olayının genel kapsamda çalıştırılmasıdır - ancak çerçeveler kapsülleme sağlar.

Peki sorun nedir?

Sorun şu ki, html-onclick'in kötü olduğunu her zaman duyan ve her zaman kullanan acemi programcı btn.addEventListener("onclick", ... ), şablonlarla bazı frameworkler kullanmak istediğinde ( addEventListeneraynı zamanda dezavantajları da vardır - DOM'u dinamik bir şekilde güncellersek innerHTML=(ki bu oldukça hızlıdır ) - o zaman olayları kaybediyoruz işleyiciler bu şekilde bağlanır). Sonra kötü alışkanlıklar veya çerçeve kullanımına yanlış yaklaşım gibi bir şeyle karşı karşıya kalacak - ve çerçeveyi çok kötü bir şekilde kullanacak - çünkü esas olarak js kısmına odaklanacak ve şablon kısmına odaklanmayacak (ve net olmayan ve sürdürmesi zor olan kodu). Bu alışkanlıkları değiştirmek için çok zaman kaybedecek (ve muhtemelen biraz şansa ve öğretmene ihtiyacı olacak).

Bu yüzden, bence öğrencilerimle edindiğim deneyime dayanarak, başlangıçta html-handlers-bind kullansalar onlar için daha iyi olurdu. Dediğim gibi, işleyicilerin küresel kapsamda çağrıldığı doğrudur, ancak bu aşamada öğrenciler genellikle kontrolü kolay küçük uygulamalar oluştururlar. Daha büyük uygulamalar yazmak için bazı çerçeveler seçerler.

Peki ne yapmalı?

Unobtrusive JavaScript yaklaşımını GÜNCELLEME ve html'de (ancak yalnızca bağlama işleyicisi - mantığı OP quesiton'daki gibi onclick'e koymayın) bağlama olay işleyicilerine (sonunda basit parametrelerle) izin verebiliriz. Yani bence ham js / html'de buna izin verilmeli

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

veya

Ancak aşağıdaki örneklere izin VERİLMEMELİDİR

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

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

Gerçek değişir, bakış açımız da değişmeli


merhaba Kamil, JavaScript'te başlangıç ​​seviyesindeyim ve onclick kullanımıyla ilgili kafa karışıklığıyla da karşılaştım, yani, lütfen onclick'in dezavantajlarını aşağıdaki gibi açıklayabilirsiniz: 1. Atanmış yalnızca bir satır içi etkinliğiniz olabilir. 2. Satır içi olaylar, DOM öğesinin bir özelliği olarak depolanır ve tüm nesne özellikleri gibi üzerine yazılabilir. 3. Onclick özelliğini silseniz bile bu olay tetiklenmeye devam edecektir.
mirzhal

1
Ad 1. Evet sadece bir tane, ancak benim deneyimim (açısal ile) bunun çoğu durumda yeterli olduğunu gösteriyor - ancak değilse sadece kullanın addEventListener. Ad 2. Evet üzerine yazılabilir (ancak genellikle kimse yapmaz) - sorun nerede? Reklam 3. İşleyici, onclick özelliğini sildikten sonra kovulmayacak - kanıt burada
Kamil Kiełczewski

8

Bu , " Unobtrusive JavaScript " adlı yeni bir paradigma . Mevcut "web standardı", işlevselliği ve sunumu ayırmayı söylüyor.

Bu gerçekten "kötü bir uygulama" değil, sadece çoğu yeni standart, sıralı JavaScript yerine olay dinleyicileri kullanmanızı istiyor.

Ayrıca, bu sadece kişisel bir şey olabilir, ancak özellikle çalıştırmak istediğiniz birden fazla JavaScript ifadeniz varsa, olay dinleyicilerini kullandığınızda okumanın çok daha kolay olduğunu düşünüyorum.


2
Konuyla ilgili Wikipedia sayfası 4 yaşın üzerinde. Artık yeni bir paradigma değil . :)
Quentin

@David: "Yeni" olmayabilir, ancak hala kullanmayan insanlar var, bu yüzden onlar için "yeni".
Rocket Hazmat

6

Sorunuz tartışmayı tetikleyecektir sanırım. Genel fikir, davranış ve yapıyı ayırmanın iyi olduğudur. Ayrıca, afaik, bir satır içi tıklama işleyicisinin evalgerçek bir javascript işlevi "haline" gelmesi gerekir. Ve oldukça eski moda, her ne kadar bu oldukça titrek bir argüman olsa da. Ah, bunun hakkında biraz okuyun @ quirksmode.org


Bir kez daha kutsal savaş yaratmak istemiyorum, doğruyu bulmaya çalışıyorum: \
NiLL

2
  • onclick olayları genel kapsamda çalışır ve beklenmeyen hatalara neden olabilir.
  • Birçok DOM öğesine onclick olayları eklemek,
    performansı ve verimliliği yavaşlatacaktır .

0

Satır içi işleyicileri kullanmamak için iki neden daha:

Sıkıcı alıntılardan kaçan sorunlara ihtiyaç duyabilirler

Bir göz önüne alındığında keyfi genel çözüm için, bu dize ile bir işlevini çağıran bir satır içi işleyicisi inşa edebilmek istiyorsanız, dize, kaçmak gerekecek niteliği (ilişkili HTML varlığı ile) ayraçları, ve sen olacak aşağıdaki gibi, öznitelik içindeki dize için kullanılan sınırlayıcıdan kaçış gerekir:

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

Bu inanılmaz derecede çirkin. Yukarıdaki örnekten, 's'yi değiştirmediyseniz, alert('foo'"bar')sözdizimi geçerli olmadığı için bir SyntaxError oluşacaktır . URL'leri değiştirmediyseniz, "tarayıcı bunu onclicközniteliğin sonu olarak yorumlar (ile sınırlandırılmış" yukarıdaki s ) ve bu da yanlış olur.

Bir alışkanlıkla satır içi işleyicileri kullanıyorsa, bir yukarıda benzer bir şey yapmak hatırlamak emin olun (ve bunu yapmak zorunda kalacak sağ ) her zaman bir bakışta anlamak sıkıcı ve zor. Satır içi işleyicilerden tamamen kaçınmak daha iyidir, böylece rasgele dizge basit bir kapanışta kullanılabilir:

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

Bu çok daha hoş değil mi?


Bir satır içi işleyicinin kapsam zinciri son derece tuhaftır

Aşağıdaki kodun günlüğe kaydedeceğini düşünüyorsunuz?

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

Deneyin, parçacığı çalıştırın. Muhtemelen beklediğiniz gibi değil. Yaptığı şeyi neden üretir? Çünkü satır içi işleyiciler withbloklar içinde çalışır . Yukarıdaki kod üç with blok içindedir : biri için document, biri için <form>ve biri için <button>:

görüntü açıklamasını buraya girin

Yana disableddüğmeye özelliktir, başvuru disabledsatır içi işleyicisi içinde düğmenin mülkiyet değil, dış atıfta disableddeğişken. Bu oldukça mantıksız. withbirçok sorunu vardır: kafa karıştırıcı hataların kaynağı olabilir ve kodu önemli ölçüde yavaşlatır. Katı modda bile buna izin verilmez. Ancak satır içi işleyicilerle, kodu yalnızca bir tane değil, birden çok iç içe geçmiş URL aracılığıyla çalıştırmak zorunda kalırsınız . Bu delilik.withwithwith

withasla kodda kullanılmamalıdır. Satır içi işleyiciler withtüm kafa karıştırıcı davranışlarının yanı sıra örtük olarak gerektirdiğinden , satır içi işleyicilerden de kaçınılmalıdır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.