現在、インターネット上では、少し複雑な Web サイトやアプリケーションには、大量の HTML、CSS、JavaScript が含まれています。インターネットの使用が進化し、インターネットへの依存度が高まるにつれて、フロントエンド コードを整理して保守するための計画を立てることが絶対に必要になります。
今日の大規模なインターネット企業の一部は、ますます多くの人々がフロントエンド コードにさらされることが増えるため、コードのモジュール性を遵守しようとします。この方法でプログラムのコードの一部を変更しても、後続の無関係な部分の実行に誤って大きな影響を与えることはありません。
特に HTML、CSS、JavaScript は本質的に相互依存しているため、予期せぬ結果を防ぐのは簡単な問題ではありません。さらに悪いことに、フロントエンド コードに関しては、サーバーサイド開発で長年使用されてきた関心の分離など、伝統的なコンピューター サイエンスの原則がほとんど議論されません。
この記事では、HTML、CSS、JavaScript コードを分離する方法について私が学んだことについて話します。個人や他の人々の経験からすると、これを行うための最良の方法は明白ではなく、多くの場合直感的ではなく、多くのいわゆるベスト プラクティスに反する場合があります。
目標
HTML、CSS、JavaScript の間には常に連携が存在します。いずれにせよ、これらのテクノロジーは本質的に相互作用するように設計されています。たとえば、フラッシュ トランジション エフェクトは、クラス セレクターを使用してスタイルシートで定義できますが、多くの場合、HTML によって初期化され、JavaScript の作成などのユーザー操作によってトリガーされます。フロントエンド コードの一部の結合は避けられないため、目標は単に結合を排除することではなく、コード間の不必要な依存関係の結合を減らすことです。バックエンド開発者は、CSS ルールや一部の JavaScript 機能を誤って破ってしまうことを心配することなく、HTML テンプレートのマークアップを変更できる必要があります。今日の Web チームが大規模かつ専門化するにつれて、この目標はこれまで以上に大きくなっています。
アンチパターン
フロントエンド コードの密結合は必ずしも明らかではありません。複雑なのは、一方では疎結合に見えるかもしれないが、他方では密結合である可能性があることです。以下は、私が何度も行ったり見たりして、失敗から学んだすべてのアンチパターンです。パターンごとに、なぜカップリングが悪いのかを説明し、カップリングを回避する方法を指摘します。
過度に複雑なセレクター
CSS Zen Garden は、HTML タグを 1 つも変更せずに Web サイト全体の外観を完全に変更できることを世界に示しました。これはセマンティック Web 運動の典型であり、主要原則の 1 つはプレゼンテーション クラスの使用を避けることです。一見すると、CSS Zen Garden は分離の良い例のように見えるかもしれませんが、結局のところ、スタイルをマークアップ言語から分離することが重要なのです。ただし、これを行うと、次のようなセレクターをスタイルシートに含める必要があることがよくあります。
#sidebar section:first-child h3 + p { }
CSS Zen Garden では、HTML は CSS からほぼ完全に分離されていますが、CSS の結合が強化されます。これを HTML に変換するには、マークアップ言語の構造を深く理解する必要があります。これは、特に誰かが CSS を保守し、同時に HTML を保守する必要がある場合には、悪いことではないように思えるかもしれませんが、一度多くの人を追加すると、状況は管理できなくなります。開発者が特定の状況下で最初の ウェブサイトのマークアップがほとんど変更されない限り、CSS Zen Garden は非常に良いアイデアです。しかし、これは今日の Web アプリケーションには必ずしも当てはまりません。長くて複雑な CSS セレクターと比較して、最良の方法は、ビジュアル コンポーネント自体のルート要素に 1 つ以上のクラス セレクターを追加することです。たとえば、サイドバーにサブメニューがある場合、次の形式を使用する代わりに、各サブメニュー要素にサブメニュー クラス セレクターを追加するだけで済みます。 このアプローチの結果、HTML ではさらに多くのクラス セレクターが必要になります。これにより結合が軽減され、コードの再利用性と保守性が向上し、マークアップが自己文書化されます。 HTML にクラス セレクターがなければ、CSS に慣れていない開発者は、HTML への変更が他のコードにどのような影響を与えるかを理解できません。一方、HTML でクラス セレクターを使用すると、どのスタイルや機能が使用されているかが非常に明確になります。 複数のクラス セレクターの役割 クラス セレクターは、多くの場合、スタイル フックと JavaScript フックの両方として使用されます。これは (少なくとも 1 つのクラス タグが削減されるため) 経済的であるように見えるかもしれませんが、実際には、要素のパフォーマンスと機能性が結合されます。 上記の例では、add-item クラス スタイルを使用した「カートに追加」ボタンを説明しています。 如果开发者想为此元素添加一个单击事件监听器,用已经存在的类选择器作为钩子非常的容易。我的意思是,既然已经存在了一个,为何要添加另一个呢? 但是想想看,有很多像这样的按钮,遍布了整个网站,都调用了相同的JavaScript功能。再想想看,如果市场团队想要其中一个和其它看起来完全不同但功能相同的按钮呢。也许这样就需要更多显著的色彩了。 问题就来了,因为监听单击事件的JavaScript代码希望add-item类选择器被使用到,但是你新的按钮又无法使用这个样式(或者它必须清除所有声明的,然后再重置新的样式)。还有,如果你测试的代码同时也希望使用add-item类选择器,那么你不得不要去更新那么代码用到的地方。更糟糕的是,如果这个”添加到购物车”功能不仅仅是当前应用用到的话,也就是说,把这份代码抽象出来作为一个独立的模块,那么即使一个简单的样式修改,可能会在完全不同的应用中引发问题。 使用javaScript钩子最好的(事实上也是比较鼓励的)做法是,如果你需要这么做,使用一种方式来避免样式和行为类选择器之间的耦合。 我的个人建议是让JavaScript钩子使用前缀,比如:js-*。这样的话,当开发者在HTML源代码中看到这样的类选择器,他就完全明白个中原因了。所以,上述的”添加到购物车”的例子可以重写成这样: 现在,如果需要一个看起来不同的按钮,你可以很简单地修改下样式类选择器,而不管行为的类选择器。 JavaScript更多的样式操作 JavaScript能用类选择器去DOM中查找元素,同样,它也能通过增加或移除类选择器来改变元素的样式。但如果这些类选择器和当初加载页面时不同的话也会有问题。当JavaScript代码使用太多的组成样式操作时,那些CSS开发者就会轻易去改变样式表,却不知道破坏了关键功能。也并不是说,JavaScript不应该在用户交互之后改变可视化组件的外观,而是如果这么做,就应该使用一种一致的接口,应该使用和默认样式不一致的类选择器。 和js-*前缀的类选择器类似,我推荐使用is-*前缀的类选择器来定义那些要改变可视化组件的状态,这样的CSS规则可以像这样: 注意到状态类选择器(is-visible)是连接在组件类选择器(pop-up)后,这很重要。因为状态规则是描述一个的状态,不应该单独列出。如此不同就可以用来区分更多和默认组件样式不同的状态样式。 另外,可以让我们可以编写测试场景来保证像is-*这样的前缀约定是否遵从。一种测试这些规则的方式是使用CSSLint和HTML Inspector。 更多关于特定状态类选择可以查阅Jonathan Snnok编写的非常优秀的SMACSS书籍。 JavaScript”选择器” jQuery和新的API,像document.querySelectorAll,让用户非常简单地通过一种他们已经非常熟悉的语言–CSS选择器来查找DOM中的元素。虽然如此强大,但同样有CSS选择器已经存在的相同的问题。JavaScript选择器不应过度依赖于DOM结构。这样的选择器非常慢,并且需要更深入认识HTML知识。 就第一个例子来讲,负责HTML模板的开发者应该能在标记上做基本的改动,而不需担心破坏基本的功能。如果有个功能会被破坏,那么它就应该在标记上显而易见。 我已经提及到应该用js-*前缀的类选择器来表示JavaScript钩子。另外针对消除样式和功能类选择器之间的二义性,需要在标记中表达出来。当某人编写HTML看到js-*前缀的类选择器时,他就会明白这是别有用途的。但如果JavaScript代码使用特定的标记结构查找元素时,正在触发的功能在标记上就不那么明显了。 为了避免使用冗长而又复杂的选择器遍历DOM,坚持使用单一的类或者ID选择器。 考虑以下代码: 这么长的选择器是可以节省你在HTML中添加一个类选择器,但同样让你的代码对于标记更改非常容易受到影响。如果设计者突然决定要把保持按钮放在左边,而让取消按钮放在右边,这样的选择器就不再匹配了。 一个更好的方式(使用上述的前缀方法)是仅仅使用类选择器。 现在标记可以更改它想改的,并且只要类选择还是在正确的元素上,一切都会很正常。 类选择器就是你的契约 適切なクラス セレクターと予測可能なクラス名規則を使用すると、ほぼすべての種類の HTML、CSS、JavaScript 間の結合を減らすことができます。 HTML をレンダリングするには多くのクラス セレクターの名前を知る必要があるため、マークアップで多くのクラス セレクターを使用すると、最初は強い結合の兆候のように見えるかもしれません。しかし、クラス セレクターの使用は、従来のプログラミング設計におけるイベントまたはオブザーバー パターンに非常に似ていることがわかりました。イベント駆動型プログラミングでは、オブジェクト A 上でオブジェクト B を直接呼び出すのではなく、オブジェクト A が指定された環境で特定のイベントを発行するだけで、オブジェクト B はそのイベントをサブスクライブできます。このように、オブジェクト B はオブジェクト A のインターフェイスについて何も知る必要はなく、どのイベントをリッスンするかを知る必要があるだけです。オブジェクト B はサブスクライブするイベント名を知る必要があるため、イベント システムが何らかの形式の結合を必要とするのは当然ですが、オブジェクト A がオブジェクト B のパブリック メソッドを知る必要があるのと比較すると、これはすでに疎結合です。 HTML クラス セレクターはすべて非常に似ています。 CSS ファイルで複雑なセレクターを定義するのとは異なり (HTML の内部インターフェイスと同様)、単一のクラス セレクターを通じてビジュアル コンポーネントの外観を簡単に定義できます。 CSS ファイルでは、HTML でクラス セレクターが使用されているかどうかを気にする必要はありません。同様に、JavaScript は HTML 構造をより深く理解する必要がある複雑な DOM トラバーサル関数を使用せず、同じクラス名の要素に対するユーザー操作のみをリッスンします。クラス セレクターは、HTML、CSS、JavaScript をまとめる接着剤のようなものでなければなりません。私は個人的な経験から、これらが 3 つのテクノロジーを過度に混合せずに接続する最も簡単で最良の方法であることを知っています。 将来 Web Hypertext Technology Working Group (WHATWG) は、開発者が HTML、CSS、JavaScript を単一のコンポーネントまたはモジュールとしてバインドし、他のページ要素と統合できるようにする Web コンポーネントの仕様に取り組んでいます。インタラクティブにカプセル化されます。この仕様が大多数のブラウザーに実装されていれば、この記事で提供した提案の多くはそれほど重要ではなくなります (コードが誰と対話するかが明らかになるため)。しかし、いずれにしても、これらの原則とその理由を理解することが重要です。それらは必要であり、重要であり続けます。 Web コンポーネントの時代では、これらの実践の重要性は低くなりますが、理論は依然として当てはまります。大規模なチームや大規模なアプリケーションで使用されているプラクティスは、小さなモジュールの作成には依然として適用されますが、その逆は当てはまりません。 結論 保守可能な HTML、CSS、および JavaScript の特徴は、すべての開発者が、これらの変更がコード ベースの他の関連部分に誤って影響を与えることを心配することなく、簡単かつ自信を持ってコード ベースのすべての部分を作成できることです。このような意図しない結果を防ぐための最良の方法の 1 つは、その意味を伝え、これに遭遇したすべての開発者が使用できる、予測可能な人間らしいクラス セレクター名のセットを与えることです。これら 3 つのテクノロジーが組み合わされています。 上記のアンチパターンを回避するには、次の原則に留意してください: 1. CSS と JavaScript では、複雑な CSS セレクターではなく、明示的なコンポーネントと動作セレクターを優先します。 2. コンポーネントがどこにあるかに基づいて名前を付けます 3. スタイルと動作に同じクラス セレクターを使用しないでください 4. HTML でのクラス セレクターの使用この方法では多くの場合、表示する必要のあるクラス セレクターが必要になりますが、得られるのは予測可能性と保守性であり、評価に値します。結局のところ、クラス セレクターを HTML に追加するのは非常に簡単で、開発者側にほとんどスキルは必要ありません。 Nicolas Gallagher の言葉からの抜粋: HTML、CSS、JS の分離に関連するその他の記事については、PHP 中国語 Web サイトに注目してください。 ul.sidebar > li > ul {
/* submenu styles */
}
<button class="add-item">Add to Cart</button>
<button class="js-add-to-cart add-item">Add to Cart</button>
<button class="js-add-to-cart add-item-special">Add to Cart</button>
.pop-up.is-visible { }
var saveBtn = document.querySelector("#modal div:last-child > button:last-child")
var saveBtn = document.querySelector(".js-save-btn")
HTML と CSS を作成するために CSS を作成したり修正したりするのに費やす時間を削減する方法を探しているとき、そのためには、スタイルの場合、HTML 要素のクラス セレクターの変更にこれ以上時間をかけたくありません。これはフロントエンド開発者とバックエンド開発者の両方にとって有用であり、誰でも事前に構築された LEGO ブロックを再配置できます。そうなると、誰も CSS の魔法を披露できなくなります。