jQueryへの別れ：現代的な開発のための必須JavaScriptメソッド


## はじめに

私は長い間レガシーコードと共に仕事をしてきましたが、jQueryの重要性は依然として頻繁に話題に上がるトピックの一つです。ライブラリ自体は便利なままですが、それは別の時代のニーズを完璧に満たしていました。
現在、私たちは既にES2023について話していますが、過去にjQueryがカバーしていたほとんどの機能は、すでに2015年にリリースされたES6に取り込まれています。
ES6の標準は既に広範にサポートされており、96％のレベルに達しています（出典：[caniuse.com](https://caniuse.com/?search=es6)）。そのため、特に要素の選択、スタイリング、アニメーション、データの取得などの基本的なタスクについては、ライブラリの使用を見直す良いタイミングかもしれません。
以下のトピックは、いくつかの標準的なjQueryのパターンと、それに相当するバニラJavaScriptでの手法を示す参考資料として役立つと思います。


## 要素の選択

DOM要素の1つまたは複数を選択するために、CSSセレクタを活用することができます。JavaScriptでは、要素の選択に使用できるさまざまなメソッドがあります。その中でもよく使われるメソッドの1つがQuery Selectorです。Query Selectorを使用すると、CSSセレクタパターンに基づいてドキュメント内の特定の要素を対象にすることができます。

```javascript
// jQueryとネイティブJavaScript: .itemのすべてのインスタンスを選択
$(".item");
document.querySelectorAll(".item");
```

上記のコードは、jQueryとネイティブJavaScriptを使用してクラス名が`.item`のすべての要素を選択する方法を示しています。jQueryでは、CSSセレクタを引数として`$`関数を使用します。ネイティブJavaScriptでは、指定されたセレクタに一致する要素のコレクションを返す`document.querySelectorAll`メソッドを使用します。

以下に、要素の選択に異なるセレクタを使用する例を示します:

```javascript
// クラスによるクエリ
$(".item");
document.querySelectorAll(".item");
document.getElementsByClassName("item");

// IDによるクエリ
$("#item");
document.querySelector("#item");
document.getElementById("item");

// 属性によるクエリ
$('a[target=_blank]');
document.querySelectorAll('a[target=_blank]');
```

上記の例では、クラス、ID、属性の条件に基づいて要素を対象にするために、さまざまなセレクタを使用しています。jQueryとネイティブJavaScriptの両方で同じ結果を得るためのメソッドが提供されています。

これらの要素の選択テクニックを活用することで、DOM内で目的の要素を効果的に選択し、それらとさらなる操作や相互作用を行うことができます。

## 要素に対して関数を実行する方法

jQueryでは、選択した要素全体に対して簡単に関数を実行することができますが、バニラJavaScriptでは少し手順が異なります。バニラJavaScriptでは、`NodeList`内の要素を繰り返し処理するために、`NodeList.forEach()`メソッドを使用する必要があります。

以下のコードは、jQueryとバニラJavaScriptによる要素の非表示化の例です。

```javascript
// jQuery: .itemのすべてのインスタンスを非表示にする
$(".item").hide();

// JavaScript: NodeList内の要素を繰り返し処理して、.itemのすべてのインスタンスを非表示にする
document.querySelectorAll(".item").forEach(item => {
  item.style.display = "none";
});
```

上記のコードでは、jQueryでは`.item`のすべてのインスタンスを非表示にするために、`hide()`メソッドを使用しています。一方、バニラJavaScriptでは、`document.querySelectorAll(".item")`を使用して`.item`のすべてのインスタンスを取得し、`NodeList.forEach()`メソッドを使用してそれぞれの要素に対して非表示のスタイルを適用しています。

バニラJavaScriptでは、`NodeList.forEach()`メソッドを使用することで、NodeList内の要素に対して簡単に関数を実行することができます。このような手順を経て、バニラJavaScriptでも要素の操作や処理を行うことが可能です。

## 別の要素内の要素を選択する方法

要素の特定の範囲内で要素を検索する必要がある場合、jQueryでは一般的に`.find()`メソッドを使用します。しかし、JavaScriptでは、要素の子要素に対して`querySelector`または`querySelectorAll`メソッドを使用することで同じ効果を実現することができます。

上記のコードは、jQueryとJavaScriptの両方で`.container`内の最初の`.item`要素を選択する方法を示しています。jQueryでは、`.container`要素を変数に格納し、後で.find(".item")を使用して子要素を検索します。JavaScriptでは、`.container`要素を`document.querySelector(".container")`で取得し、後で`querySelector(".item")`を使用して子要素を検索します。

```javascript
// jQuery: .container内の最初の.itemを選択
var container = $(".container");
// 後で...
container.find(".item");

// JavaScript: .container内の最初の.itemを選択
var container = document.querySelector(".container");
// 後で...
container.querySelector(".item");

// JavaScript: .container内のすべての.linkを選択
var container = document.querySelector(".container");
// 後で...
container.querySelectorAll(".link");
```

## 親要素や隣接要素へのトラバース


要素の階層構造に基づいて、対象要素から親要素や隣接要素を選択する方法を紹介します。JavaScriptでは、次のようなプロパティを使用します: `nextElementSibling`、`previousElementSibling`、`parentElement`。

以下のコード例では、jQueryとJavaScriptを使用して要素の隣接要素や親要素を選択します。

```javascript
// jQuery: .itemの次の要素、前の要素、親要素を選択
$(".item").next();
$(".item").prev();
$(".item").parent();

// JavaScript: .itemの次の要素、前の要素、親要素を選択
var item = document.querySelector(".item");
item.nextElementSibling;
item.previousElementSibling;
item.parentElement;
```

この例では、`.item`というクラスを持つ要素を対象にしています。jQueryでは、`.next()`メソッドを使用して次の要素、`.prev()`メソッドを使用して前の要素、`.parent()`メソッドを使用して親要素を選択します。JavaScriptでは、`nextElementSibling`プロパティを使用して次の要素、`previousElementSibling`プロパティを使用して前の要素、`parentElement`プロパティを使用して親要素を選択します。

これにより、要素間のトラバースが可能となり、柔軟なDOM操作が実現できます。

## イベントの取り扱い

jQueryでは、`.on()`、`.bind()`、`.live()`、`.click()`などさまざまな方法でイベントを監視できますが、同等の機能を持つJavaScriptの`.addEventListener()`メソッドを使用します。

```javascript
// jQuery: クリック、マウスエンター、キーアップイベントを処理
$(".button").click(function(e) { /* クリックイベントの処理 */ });
$(".button").mouseenter(function(e) {  /* マウスエンターイベントの処理 */ });
$(document).keyup(function(e) {  /* キーアップイベントの処理 */  });

// JavaScript: クリック、マウスエンター、キーアップイベントを処理
document.querySelector(".button").addEventListener("click", (e) => { /* ... */ });
document.querySelector(".button").addEventListener("mouseenter", (e) => { /* ... */ });
document.addEventListener("keyup", (e) => { /* ... */ });
```

## クリックイベントのデリゲーション

Queryの.on()メソッドを使用して、動的に追加された要素に対してイベントハンドラを登録できます。JavaScriptでは、イベントのデリゲーションを実現するために、イベントバブリングを利用して親要素でイベントをキャプチャし、対象の要素を判別する必要があります。

```javascript
// jQuery: .container内の.active要素のクリックイベントを処理
$(".container").on("click", ".active", handleClick);

// JavaScript:
document.querySelector(".container").addEventListener("click", function (event) {
  if (event.target.matches(".active")) {
    handleClick(event);
  }
});
```

## 動的に追加された要素のイベント監視

jQueryの`.on()`メソッドを使用すると、動的にDOMに追加されたオブジェクトのイベントハンドラを処理できます。jQueryを使用しない場合でも、要素をDOMに追加する際にイベントハンドラを要素に追加することで同様の機能を実現できます。

```javascript
// jQuery: ダイナミックに追加された.active要素のクリックイベントを処理
$(".search-container").on("click", ".active", handleClick);

// JavaScript: 要素を作成しDOMに追加し、イベントリスナーを追加
var searchElement = document.createElement("div");
document.querySelector(".search-container").appendChild(searchElement);
searchElement.addEventListener("click", handleClick);
```

## イベントのトリガーと作成

イベントのトリガーと作成には、JavaScriptの`dispatchEvent()`メソッドを使用します。`dispatchEvent()`メソッドは任意の要素で呼び出すことができ、最初の引数として`Event`を取ります。

```javascript
// jQuery: documentおよび.itemでmyEventをトリガー
$(document).trigger("myEvent");
$(".item").trigger("myEvent");

// JavaScript: myEventを作成してディスパッチ
document.dispatchEvent(new Event("myEvent"));
document
.querySelector(".item").dispatchEvent(new Event("myEvent"));
```

## 要素のスタイリング

jQueryでは、要素のインラインCSSを変更するために`.css()`を呼び出しますが、JavaScriptでは`.style`を使用して異なるプロパティに値を割り当てることで同じ効果を実現できます。

```javascript
// jQuery: .itemのテキストカラーを#000に変更
$(".item").css("color", "#000");

// JavaScript: 最初の.itemのテキストカラーを#000に変更
document.querySelector(".item").style.color = "#000";
```

複数のプロパティをまとめて設定する場合：

```javascript
// jQuery: 複数のスタイルを設定
$(".item").css({
  "color": "#000",
  "background-color": "red"
});

// JavaScript: colorを#000に設定し、backgroundをredに設定
var item = document.querySelector(".item");
item.style.color = "#000";
item.style.backgroundColor = "red";

// すべてのスタイルを一度に設定（既存のスタイルを上書き）
item.style.cssText = "color: #000; background-color: red";
```

## fadeIn()とfadeOut()

同じ効果をJavaScriptで再現するために、`fadeIn()`という関数を作成しました。この関数では、引数として要素を受け取り、要素のスタイルを変更することでフェードイン効果を作り出しています。
要素の不透明度をアニメーションするために`transition`プロパティを設定し、`opacity`プロパティを`1`に変更して要素を可視化しています。

```javascript
// jQuery: .itemをフェードイン
$(".item").fadeIn();
```

このコードは、`.item`というクラスを持つ要素をフェードインさせます。jQueryの`.fadeIn()`メソッドを使用して、要素の不透明度をアニメーションさせることでフェードイン効果を実現しています。

```javascript
// JavaScript:
function fadeIn(element) {
  element.style.transition = "opacity 1s";
  element.style.opacity = "1";
}

fadeIn(document.querySelector(".item"));
```

## hide() と show()

`.hide()` と `.show()` は要素の `display` プロパティを `none` と `block` に設定するための便利なメソッドです。

```javascript
// jQuery: 要素を非表示にする
$(".item").hide();
$(".item").show();

// JavaScript: 要素を非表示にする（displayプロパティを変更）
document.querySelector(".item").style.display = "none";
document.querySelector(".item").style.display = "block";
```

## DOMの構築が完了

DOMが完全に読み込まれるのを待つ必要がある場合（たとえば、DOM上のオブジェクトにイベントをアタッチする場合）、通常はjQueryの`$(document).ready()`または短縮形の`$()`を使用します。代わりに、JavaScriptでは `DOMContentLoaded` イベントを聴くことで同様の機能を実現できます。

```javascript
// jQuery: DOMが完全に読み込まれた後に何かを実行
$(document).ready(function() {
  /* DOMが完全に読み込まれた後に何かを実行 */
});

// JavaScript: DOMが完全に読み込まれた後に何かを実行
document.addEventListener("DOMContentLoaded", (event) => {
  /* DOMが完全に読み込まれた後に何かを実行 */
});
```

## クラスの操作

jQueryでは、`.addClass()`、`.removeClass()`、`.toggleClass()`などを使用してクラスを操作できます。JavaScriptでは、`classList`プロパティを使用して同様の機能を実現できます。

```javascript
// jQuery: "active" クラスを追加、削除、切り替える
$(".item").addClass("active");
$(".item").removeClass("active");
$(".item").toggleClass("active");

// JavaScript: "active" クラスを追加、削除、切り替える
var item = document.querySelector(".item");
item.classList.add("active");
item.classList.remove("active");
item.classList.toggle("active");
```

複数のクラスを追加または削除する場合：

```javascript
// "active" と "highlighted" クラスを追加し、削除する
var item = document.querySelector(".item");
item.classList.add("active", "highlighted");
item.classList.remove("active", "highlighted");
```

相互に排他的な2つのクラスを切り替える場合、`classList`プロパティにアクセスして`.replace()`を呼び出すことで、1つのクラスを別のクラスに置き換えることができます。

```javascript
// "active" クラスを削除し、"active" クラスに置き換える
document.querySelector(".item").classList.replace("active", "active");
```

## クラスの有無を確認する

要素が特定のクラスを持つかどうかを確認する場合、JavaScriptの`.classList.contains()`メソッドを使用できます。

```javascript
// jQuery: .itemが "active" クラスを持っているか確認し、何かを実行する
if ($(".item").hasClass("active")) {
  // 何かを実行...
}

// JavaScript: .itemが "active" クラスを持っているか確認し、何かを実行する
if (document.querySelector(".item").classList.contains("active")) {
  // 何かを実行...
}
```

## .get() や .ajax() によるネットワークリクエスト

jQueryでは、`.get()`や`.ajax()`を使用してネットワークリクエストを行うことができます。JavaScriptでは、同様の機能を持つ`fetch()`を使用し、レスポンスを処理するためのPromiseを返します。

```javascript
// jQuery: AJAXリクエストを行う
$.ajax({
    url: "data.json"
  }).done(function(data) {
    // ...
  }).fail(function() {
    // エラー処理
  });

// JavaScript: fetchを使用してネットワークリクエストを行う
fetch("data.json")
  .then(data => {
    // データの処理
  }).catch(error => {
    // エラー処理
  });
```

## 要素の作成

JavaScriptで動的に要素を作成する場合、`document`オブジェクトの`createElement()`メソッドを使用します。

```javascript
// jQuery: div要素とspan要素を作成
$("<div/>");
$("<span/>");

// JavaScript: div要素とspan要素を作成
document.createElement("div");
document.createElement("span");
```

## DOMの更新

要素のテキストを変更したり、新しい要素をDOMに追加したりする場合、`.innerHTML`プロパティがよく使用されますが、これはクロスサイトスクリプティング（XSS）攻撃にさらされる可能性があります。代わりに、より安全な代替手段があります。

要素のテキストを読み取ったり更新したりする場合、`textContent`プロパティを使用します。

```javascript
// jQuery: .itemのテキストを更新
$(".item").text("新しいテキスト");
// .itemのテキストを読み取る
$(".item").text(); // "新しいテキスト" を返す

// JavaScript: .itemのテキストを更新
document.querySelector(".item").textContent = "新しいテキスト";
// .itemのテキストを読み取る
document.querySelector(".item").textContent; // "新しいテキスト" を返す
```

要素をDOMに追加する場合：

```javascript
// jQuery: div要素を作成し.containerに追加
$(".container").append($("<div/>"));

// JavaScript: div要素を作成し.containerに追加
var element = document.createElement("div");
document.querySelector(".container").appendChild(element);
```

## まとめ

これらはカバーされたメソッドの概要であり、jQueryのネガティブな側面やネイティブJavaScriptへの移行の利点については言及していません。
この記事は、ネイティブJavaScriptに移行する際の参考として活用してください。なお、これは包括的なガイドではなく、公式ドキュメントを参照し、ここで紹介したネイティブJavaScriptのメソッドをさらに探求することをお勧めします。

## 参考

Mozilla Web Documentationを読むことで、JavaScriptのさらなる深層に入ることができます。[https://developer.mozilla.org/ja/docs/Web/JavaScript](https://developer.mozilla.org/ja/docs/Web/JavaScript) 
 
jQueryからバニラJavaScriptへの移行に関する特別な情報は、以下のリンクから見つけることができます。
- [https://youmightnotneedjquery.com/](https://youmightnotneedjquery.com/)  
- [https://tobiasahlin.com/blog/move-from-jquery-to-vanilla-javascript/](https://tobiasahlin.com/blog/move-from-jquery-to-vanilla-javascript/)
- [https://dev.to/rfornal/-replacing-jquery-with-vanilla-javascript-1k2g](https://dev.to/rfornal/-replacing-jquery-with-vanilla-javascript-1k2g)