HTMLでonClick()を使用することが悪い習慣であるのはなぜですか?


133

onClick()HTML でなどのJavaScriptイベントを使用することは、セマンティクスに適さないため、悪い習慣であると何度も聞いています。欠点は何ですか、また次のコードをどのように修正するのですか?

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


4
onlickcには何の問題もありませんが、hrefを作成することにより、#JavaScriptを無効にすることを選択した人はすべてスタックし、何もできなくなります。一部のサイトにとっては良いことですが、単にウィンドウを開くだけの場合、「実際の」リンクを提供しないことはまったく愚かです。
マークB

2
間違いなくそのリンクを読んでください。セマンティクスとはほとんど関係がなく、そのページのすべてのものと関係があります:-)
morewry

リンクを新しいタブで開こうとすると、それが間違っている理由の例が表示されます...
Sebastien C.

2
<button>リンクは実際のリソースを指すことになっているため、これはである必要があります。それはその意味でより意味があり、スクリーンリーダーのユーザーにとってはより明確です。
programmer5000

回答:


171

おそらく、次のような控えめなJavascriptについて話しているでしょう。

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

中央のJavaScriptファイルのロジックは次のようになります。

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

利点は

  • 動作(JavaScript)がプレゼンテーション(HTML)から分離されている
  • 言語の混在なし
  • あなたはあなたのためにほとんどのクロスブラウザの問題を処理できるjQueryのようなJavaScriptフレームワークを使用しています
  • コードを複製せずに、一度に多くのHTML要素に動作を追加できます。

30
かなりの数の利点を挙げましたが、欠点はどうですか?青い煙をデバッグしていますか?
アベリト

2
@abelito:確かにデバッグは不利です。どちらかと言えば、ブラウザのデバッグツール内でJavaScriptをステップ実行できるため、デバッグの利点です。コードがインラインである場合、それが簡単にできるかどうかはわかりませんonClick。また、スクリプトに対してユニットテストを作成することもできます。これは、コードが内部にonClickあり、ロジックが分離されていない場合にも想定します。個人的には控えめなJavaScriptのデバッグに問題はなく、JavaScriptコードを管理およびテストすることの利点は、JavaScriptを使用しないことには非常に大きなものです。
いいえ、

45
@FrançoisWahl:主な欠点は発見可能性です。HTMLを見ても、どのコールバックがそれに接続されているかはわかりません。
Michael Borgwardt

1
@MichaelBorgwardt:私はそれが不利益であることに完全に同意します。コードが適切に構造化および整理されている場合、プロジェクトで作業したり、他の人のコードベースをデバッグしたりするときに、それを大きな問題とは見なしません。一般に、私はあなたに同意しますが、インラインスクリプトを記述する正当な理由である発見可能性、コードのテスト容易性、およびDOMから関数/動作コードを分離する機能などの犠牲になる利点はないと思います。インラインコードが不適切で機能しないと言っているのではありません。テスト可能性などに興味があるかどうかに応じて、実際には問題ありません。
いいえ、

4
初心者を対象としたこのディスカッションのもう1つのポイント-は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を有効にする必要があります。
Thomas Shields、

2
はい。ただし、この場合、JavaScriptなしでアクションを実行するページにリンクをリダイレクトできます。
ThiefMaster、2011年

12
リンクは JavaScriptをせずに動作します。リンクが通常のhref属性を持っている方法を確認してください。ユーザーがJavaScriptを持っていない場合でも、リンクはリンクであり、ユーザーは引き続きコンテンツにアクセスできます。
morewry

3
@Thomas Shields:いいえ、彼はあなたがJavascriptを持っていない場合でも、/ map /へのリンクが表示されることを意味します...
Robert

2
ああリッチ!私たちは皆、この気の利いた解決策であなたを愛しています!
通常の

21

非常に大規模なJavaScriptアプリケーションの場合、プログラマーはコードのカプセル化を強化して、グローバルスコープの汚染を回避しています。また、HTML要素のonClickアクションで関数を使用できるようにするには、その関数をグローバルスコープに含める必要があります。

あなたはこのようなJSファイルを見たことがあるかもしれません...

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

これらは即時に呼び出される関数式(IIFE)であり、それらの中で宣言された関数はすべて内部スコープ内にのみ存在します。

function doSomething(){}IIFE内で宣言doSomething()し、HTMLページで要素のonClickアクションを作成すると、エラーが発生します。

一方、そのIIFE内でその要素のeventListenerを作成doSomething()し、リスナーがクリックイベントを検出したときに呼び出す場合、リスナーとdoSomething()IIFEのスコープを共有しているため、問題ありません。

最小限のコードを持つ小さなWebアプリの場合、それは問題ではありません。しかし、大きくてメンテナンス可能なコードベースを書くことを熱望しているのであればonclick=""、避けるべき習慣です。


1
大規模で保守可能なコードベースを作成したい場合は、 Angular、Vue、ReactなどのJSフレームワークを使用してください。HTMLテンプレート内でイベントハンドラーをバインドすることをお勧めします
KamilKiełczewski19年

20

それはいくつかの理由で良くありません:

  • コードとマークアップが混在しています
  • この方法で書かれたコードは通過します eval
  • そしてグローバルスコープで実行されます

最も簡単なことはname<a>要素に属性を追加することです。

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

最近のベストプラクティスはid、名前のaddEventListener()代わりにを使用onclickすることですが、複数の関数を1つのイベントにバインドできるため、使用する代わりに使用します。


この方法を提案しても、ユーザーはイベントハンドラーの衝突に関する一連の問題に直面します。
プレアクション2015年

3
@preactionは、インラインイベントハンドラーと同じように、したがって、なぜ私の答えはを使用する方が良いと言っているのaddEventListener()か、そしてその理由は。
Alnitak 2015年

2
誰かがこれをもっと説明できますか?「このように書かれたコードはevalを通過します」...これについてはWeb上で何も見つかりません。インラインJavascriptを使用すると、パフォーマンスやセキュリティの面で不利になると言っていませんか?
MarsAndBack 2018年

12

いくつかの理由があります。

  1. HTMLとクライアント側のスクリプトなど、マークアップを分離することでメンテナンスの手助けになると思います。たとえば、jQueryを使用すると、プログラムでイベントハンドラーを簡単に追加できます。

  2. 指定した例は、JavaScriptをサポートしていないか、JavaScriptがオフになっているユーザーエージェントでは機能しません。プログレッシブエンハンスメントのコンセプトは/map/、JavaScriptなしのユーザーエージェントへの単純なハイパーリンクを奨励し、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>
ダニエルSokolowski

10

リビジョン

PASTでは控えめなJavaScriptアプローチが優れていました-特にHTMLのイベントハンドラーバインドは悪い習慣と見なされていました(主にYiddishNinjaonclick events run in the global scope and may cause unexpected errorによる言及があったため)

しかしながら...

現在、このアプローチは少し時代遅れで、更新が必要なようです。プロのフロントエンド開発者になり、大きくて複雑なアプリを書きたい場合は、Angular、Vue.jsなどのフレームワークを使用する必要があります。ただし、そのフレームワークは通常、イベントハンドラーがバインドされているHTMLテンプレートを使用(または使用を許可)htmlテンプレートコードで直接、これは非常に便利、明確、効果的です。たとえば、角度のあるテンプレートでは、通常、次のように記述します。

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

未加工のjs / htmlでは、これと同等になります

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

違いは、生のjs onclickイベントではグローバルスコープで実行されることですが、フレームワークはカプセル化を提供します。

では、問題はどこにあるのでしょうか?

問題は、常にhtml-onclickが悪いことを耳にし、常に使用する初心者プログラマーがbtn.addEventListener("onclick", ... )テンプレートを使用していくつかのフレームワークを使用したい場合です(欠点addEventListenerもあります-を使用して動的にDOMを更新すると(かなり高速です)、イベントが失われます。ハンドラはそのようにバインドします)。それから彼はフレームワークの使用法に悪い習慣や間違ったアプローチのような何かに直面します-そして彼は非常に悪い方法でフレームワークを使用します-彼は主にjs-partに焦点を当て、テンプレートパートには焦点を当てないからですコード)。この習慣を変えるために、彼は多くの時間を失うでしょう(そして、おそらく彼はいくつかの運と教師を必要とするでしょう)。innerHTML=

だから私の意見では、私の生徒たちの経験に基づいて、彼らが最初にhtml-handlers-bindを使用するのが良いでしょう。ハンドラーがグローバルスコープで呼び出されることは事実ですが、この段階では通常、制御しやすい小さなアプリケーションを作成します。より大きなアプリケーションを作成するには、いくつかのフレームワークを選択します。

じゃあ何をすればいいの?

控えめなJavaScriptアプローチを更新し、HTMLでバインドイベントハンドラー(最終的には単純なパラメーターを使用)を許可できます(ただし、バインドハンドラーのみ-OPクエリのようにonclickにロジックを配置しないでください)。だから私の意見では生の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>

現実は変化し、私たちの視点も変化するはずです


こんにちはカミル、私はJavaScriptの初心者であり、onclickの使用についても混乱に直面しています。つまり、onclickの欠点を次のように説明できます。1.インラインイベントは1つしか割り当てられない場合があります。2.インラインイベントはDOM要素のプロパティとして保存され、すべてのオブジェクトプロパティと同様に上書きできます。3. onclickプロパティを削除しても、このイベントは引き続き発生します。
mirzhal

1
広告1.はい、1つだけですが、私の経験(角度のある)は、ほとんどの状況でこれで十分であることを示していますaddEventListener。広告2.はい、上書きできます(ただし、通常は誰も上書きしません)-問題はどこにありますか?広告3. onclick attribを削除した後、ハンドラーは
呼び出さ

8

これは、「邪魔にならないJavaScript」と呼ばれる新しいパラダイムです。現在の「ウェブ標準」は、機能とプレゼンテーションを分離するように言っています。

これは実際には「悪い習慣」ではなく、JavaScriptをインライン化する代わりに、ほとんどの新しい標準でイベントリスナーを使用することを求めているだけです。

また、これは個人的なことかもしれませんが、特に実行するJavaScriptステートメントが複数ある場合は、イベントリスナーを使用すると読みやすくなります。


2
この件に関するウィキペディアのページは4年以上前のものです。それはもはや新しいパラダイムではありません。:)
クエンティン、

@David:「新品」ではないかもしれませんが、まだ使っていない人もいるので「新品」です。
ロケットハズマット

6

あなたの質問は私が推測する議論を引き起こすでしょう。一般的な考え方は、動作と構造を分離することは良いことです。さらに、インラインクリックハンドラーであるafaikをeval実際のJavaScript関数に「変換」する必要があります。そして、それはかなり古いものですが、それはかなり不安定な議論です。ああ、まあ、それについていくつか読んでください@ quirksmode.org


私はもう一度聖なる戦争を作りたくありません、私は本当のことを見つけようとしています:\
NiLL

2
  • onclickイベントはグローバルスコープで実行され、予期しないエラーを引き起こす可能性があります。
  • 多くのDOM要素にonclickイベントを追加する
    と、パフォーマンスと効率が低下します。

0

インラインハンドラを使用しない2つの理由:

彼らは面倒な引用エスケープ問題を要求することができます

任意の文字列が与えられ、その文字列を使用して関数を呼び出すインラインハンドラーを構築できるようにしたい場合、一般的なソリューションでは、(関連付けられている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を置き換えなかった場合、alert('foo'"bar')は有効な構文ではないため、SyntaxErrorが発生します。を置き換えなかった場合"、ブラウザはそれを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ブロック内で実行されるためです。上記のコードは3つの withブロック内にあります。1つはdocument、1つは、1つ<form><button>

ここに画像の説明を入力してください

disabledはボタンのプロパティなのでdisabled、インラインハンドラー内での参照は、外部disabled変数ではなくボタンのプロパティを参照します。これはかなり直観に反しています。withには多くの問題があります。それは混乱を招くバグの原因となる可能性があり、コードを大幅に遅くします。ストリクトモードではまったく許可されていません。しかし、インラインハンドラで、あなたがしている強制によるコードを実行するwithと、1つだけでなくて- S withが、複数のネストされたてwithの。それはクレイジーです。

withコードでは使用しないでください。withインラインハンドラーは、そのすべての混乱する動作と共に暗黙的に必要とするため、インラインハンドラーも回避する必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.