「querySelector()って書き方は知ってるけど、その先どうすればいいの?」
「クラスの追加・削除はaddClass/removeClassが便利だったのに…」
「イベント処理って素のJavaScriptだと面倒なんじゃ…?」
前回は配列操作のモダンな書き方を学びました。今回は、jQueryの真骨頂とも言えるDOM操作について、モダンなJavaScriptでの書き方を見ていきましょう。
要素の取得
まずは要素の取得方法を比較してみましょう:
JS
// jQuery
const $button = $('#submitButton');
const $items = $('.item');
const $inputs = $('input[type="text"]');
const $firstItem = $('.item:first');
const $lastItem = $('.item:last');
const $form = $button.closest('form');
// Modern JavaScript
const button = document.querySelector('#submitButton');
const items = document.querySelectorAll('.item');
const inputs = document.querySelectorAll('input[type="text"]');
const firstItem = document.querySelector('.item');
const lastItem = document.querySelector('.item:last-child');
const form = button.closest('form');
主な違いと注意点:
querySelector
は最初の1つの要素、querySelectorAll
は該当する全ての要素を返す- jQueryオブジェクトと異なり、直接DOM要素が取得できる
querySelectorAll
の返り値は配列ではなくNodeList
なので、必要に応じてArray.from()
を使用する
要素の走査(要素の探索)
JS
// jQuery
const $parent = $element.parent();
const $children = $element.children();
const $next = $element.next();
const $prev = $element.prev();
const $siblings = $element.siblings();
// Modern JavaScript
const parent = element.parentElement;
const children = element.children;
const next = element.nextElementSibling;
const prev = element.previousElementSibling;
const siblings = [...parent.children].filter(child => child !== element);
ポイント:
- ほとんどのDOM走査メソッドは直感的な名前で用意されている
- 兄弟要素の取得など、一部の操作は自前で実装が必要
children
はHTMLCollection
を返すので、配列操作が必要な場合は変換が必要
クラスの操作
JS
// jQuery
$element.addClass('active');
$element.removeClass('disabled');
$element.toggleClass('selected');
$element.hasClass('visible');
// Modern JavaScript
element.classList.add('active');
element.classList.remove('disabled');
element.classList.toggle('selected');
element.classList.contains('visible');
// 複数のクラスを一度に操作
element.classList.add('active', 'visible');
classList APIのメリット:
- メソッド名がjQueryと似ているため移行が容易
- 複数クラスの一括操作が可能
- パフォーマンスが優れている(DOMネイティブのAPI)
スタイルの操作
JS
// jQuery
$element.css('backgroundColor', '#f00');
$element.css({
width: '100px',
height: '100px',
margin: '10px'
});
// Modern JavaScript
element.style.backgroundColor = '#f00';
Object.assign(element.style, {
width: '100px',
height: '100px',
margin: '10px'
});
モダンJavaScriptでのスタイル操作のポイント:
- キャメルケースでプロパティ名を指定(background-color → backgroundColor)
Object.assign
を使うことで複数のスタイルを一括設定可能- CSSカスタムプロパティ(変数)も操作可能
要素の作成と追加
JS
// jQuery
const $newDiv = $('<div>')
.addClass('container')
.attr('id', 'newContent')
.text('Hello World');
$('#parent').append($newDiv);
// Modern JavaScript: createElement方式
const newDiv = document.createElement('div');
newDiv.classList.add('container');
newDiv.id = 'newContent';
newDiv.textContent = 'Hello World';
document.querySelector('#parent').appendChild(newDiv);
// Modern JavaScript: テンプレートリテラル方式
const template = `
<div class="container" id="newContent">
Hello World
</div>
`;
document.querySelector('#parent').insertAdjacentHTML('beforeend', template);
二つのアプローチの使い分け:
createElement
: 要素を動的に操作する場合に適しているinsertAdjacentHTML
: 静的なHTML構造を追加する場合に便利- テンプレートリテラルを使うと、HTMLの構造が視覚的に分かりやすい
イベントの処理
JS
// jQuery
$button.on('click', function(e) {
e.preventDefault();
$(this).addClass('clicked');
});
// イベントの削除
$button.off('click', handleClick);
// Modern JavaScript
button.addEventListener('click', function(e) {
e.preventDefault();
this.classList.add('clicked');
});
// イベントの削除
button.removeEventListener('click', handleClick);
// イベント委譲
document.querySelector('.list').addEventListener('click', e => {
if (e.target.matches('.item')) {
handleItemClick(e);
}
});
モダンなイベント処理のポイント:
addEventListener
は複数のイベントリスナーを登録可能- イベント委譲には
matches
メソッドが便利 removeEventListener
を使う場合は、同じ関数参照が必要
実践的なリファクタリング例:タブUI
JS
// Before: jQuery
$('.tab').on('click', function(e) {
e.preventDefault();
var $tab = $(this);
var target = $tab.data('target');
$('.tab').removeClass('active');
$tab.addClass('active');
$('.tab-content').hide();
$('#' + target).show();
});
// After: Modern JavaScript
document.querySelectorAll('.tab').forEach(tab => {
tab.addEventListener('click', e => {
e.preventDefault();
const target = tab.dataset.target;
// タブの切り替え
document.querySelectorAll('.tab')
.forEach(t => t.classList.remove('active'));
tab.classList.add('active');
// コンテンツの切り替え
document.querySelectorAll('.tab-content')
.forEach(content => content.style.display = 'none');
document.getElementById(target).style.display = 'block';
});
});
リファクタリングのポイント:
querySelectorAll
とforEach
で要素の一括処理dataset
でdata属性にアクセス- クラスの切り替えは
classList
を使用 - 表示/非表示の切り替えは直接styleプロパティを操作
まとめ
DOM操作において、jQueryは確かに便利なAPIを提供してきました。しかし、モダンなJavaScriptを使うことで:
- より軽量なコード
- ネイティブAPIによる高速な処理
- 明確な意図の表現
- 余分な抽象化の排除
が実現できます。次回は「非同期処理の進化 – コールバック地獄からの解放」について解説し、Promise/async/awaitを使った効率的な非同期処理の書き方を学んでいきましょう。