注意とお願い
この記事の内容はもはや古いです。ここに書いている方法では動かないものをいくつか見つけました。参考にする際は動作をよく確認してから使ってください。
ひとつお願いがあります。「あれ、動かないぞ」というコードを見つけたら是非コメントか編集リクエストで教えてください。解決方法までなくても結構です。「これはもう動かないよ」という印をつけたいのです。
この記事はYou Don't Need jQueryの日本語訳と同じ内容です。
先日ひょんなことからYou Don't Need jQueryの日本語訳をさせていただきました。著者のCam Songさんからも快諾をいただけたので1、Qiitaでも公開させていただきます。
なお、本家の英語の説明は継続的にメンテされているので、この記事の情報は古くなっている可能性があります。
追記
この記事は当初は「もうjQueryは必要ない」というタイトルで公開していました。このタイトルに関して不快に思われた方が多いようなので、タイトルを変更させていただきました。
不快な気分になられた方は申し訳ありません。一点、意訳で「もう」を加えて必要以上に煽りっぽくなってしまったのは翻訳の問題ですのでその点に関して原著者には非はない、ということだけ補足させてください。
jQueryは必要ない(You Don't Need jQuery)
フロントエンドの開発環境はめまぐるしく進化していて、最近のブラウザでは十分な質、量のDOM/BOM APIが実装されています。もうDOM操作やイベント処理のためにjQueryを覚える必要はありません。また、ReactやAngularそしてVueなどのフロントエンドライブラリの流行により、DOMを直接操作することはアンチパターンとなりました。jQueryはそれほど重要ではなくなったのです。このプロジェクトは、jQueryでの書き方の代わりとなるネイティブでの書き方(IE10以上)をまとめます。
目次
Translations
- 한국어
- 简体中文
- Bahasa Melayu
- Bahasa Indonesia
- Português(PT-BR)
- Tiếng Việt Nam
- Español
- Русский
- Türkçe
- Italiano
- Français
セレクタ
classセレクタ、idセレクタ、属性セレクタのような主要セレクタはdocument.querySelector
もしくはdocument.querySelectorAll
で代替できます。
jQueryのセレクタと比べて以下の違いがあります。
-
document.querySelector
はセレクタにマッチする最初のエレメントを返す -
document.querySelectorAll
はセレクタにマッチする全てのエレメントのNodeListを返す。Array.prototype.slice.call(document.querySelectorAll(selector));
で配列に変換できる。 - セレクタにマッチする要素がなかった場合、jQueryとdocument.querySelectorAllは
[]
を返すが、document.querySelectorはnull
を返す。したがってNull Pointer Exceptionに注意する必要がある。もしくはdocument.querySelector(selector) ||[]
のように||
を使ってデフォルト値を指定しておく。
注意:
document.querySelector
とdocument.querySelectorAll
はかなり遅いです。もし、パフォーマンスが必要ならdocument.getElementById
やdocument.getElementsByClassName
、document.getElementsByTagName
を使ってください。
- 1.0 セレクタによる選択
// jQuery
$('selector');
// Native
document.querySelectorAll('selector');
- 1.1 クラス名による選択
// jQuery
$('.class');
// Native
document.querySelectorAll('.class');
// or
document.getElementsByClassName('class');
- 1.2 idによる選択
// jQuery
$('#id');
// Native
document.querySelector('#id');
// or
document.getElementById('id');
- 1.3 属性による選択
// jQuery
$('a[target=_blank]');
// Native
document.querySelectorAll('a[target=_blank]');
- 1.4 子孫要素の選択
// jQuery
$el.find('li');
// Native
el.querySelectorAll('li');
-
1.5 兄弟要素の選択
- 兄弟要素
// jQuery $el.siblings(); // Native Array.prototype.filter.call(el.parentNode.children, function(child) { return child !== el; });
- 直前の兄弟要素
// jQuery $el.prev(); // Native el.previousElementSibling;
- 直後の兄弟要素
// jQuery $el.next(); // Native el.nextElementSibling;
1.6 祖先要素の選択
指定要素からdocument方向に遡って走査し、セレクタにマッチする最初の祖先要素を返します。
// jQuery
$el.closest(selector);
// Native - 最近のブラウザのみ。IEでは動かない。
el.closest(selector);
// Native - IE10+
function closest(el, selector) {
const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector;
while (el) {
if (matchesSelector.call(el, selector)) {
return el;
} else {
el = el.parentElement;
}
}
return null;
}
- 1.7 Parents Until
指定要素からセレクタにマッチする祖先要素までdocument方向に遡って走査し、フィルタにマッチする祖先要素を全て取得します。ただし、セレクタで指定された要素は含みません。
// jQuery
$el.parentsUntil(selector, filter);
// Native
function parentsUntil(el, selector, filter) {
const result = [];
const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector;
// parentから走査を開始する
el = el.parentElement;
while (el && !matchesSelector.call(el, selector)) {
if (!filter) {
result.push(el);
} else {
if (matchesSelector.call(el, filter)) {
result.push(el);
}
}
el = el.parentElement;
}
return result;
}
-
1.8 フォーム
- input/textarea
// jQuery $('#my-input').val(); // Native document.querySelector('#my-input').value;
-
.radio
内でのe.currentTarget
のインデックスを返す
// jQuery $(e.currentTarget).index('.radio'); // Native Array.prototype.indexOf.call(document.querySelectorAll('.radio'), e.currentTarget);
1.9 iframeのコンテンツ
$('iframe').contents()
はiframeのcontentDocument
を返します。
-
Iframe contents
// jQuery $iframe.contents(); // Native iframe.contentDocument;
-
Iframe Query
// jQuery $iframe.contents().find('.css'); // Native iframe.contentDocument.querySelectorAll('.css');
- 1.10 bodyを取得する
// jQuery
$('body');
// Native
document.body;
-
1.11 属性の設定、取得
- 属性値を取得する
// jQuery $el.attr('foo'); // Native el.getAttribute('foo');
- 属性値を設定する
// jQuery, DOMを変化させずメモリ上で動作することに注意 $el.attr('foo', 'bar'); // Native el.setAttribute('foo', 'bar');
-
data-
属性を取得する
// jQuery $el.data('foo'); // Native (`getAttribute`を使う) el.getAttribute('data-foo'); // Native (IE11以上のサポートなら`dataset`を使ってもよい) el.dataset['foo'];
CSSとスタイル
-
2.1 CSS
- スタイルを取得する
// jQuery $el.css("color"); // Native // NOTE: 既知のバグ デフォルト値が'auto'の場合、値が指定されていなくても'auto'が返る const win = el.ownerDocument.defaultView; // nullは疑似要素でないことを示している win.getComputedStyle(el, null).color;
- スタイルを設定する
// jQuery $el.css({ color: "#ff0011" }); // Native el.style.color = '#ff0011';
- スタイルを一括取得、一括設定する
複数のスタイルを一括で設定したいなら、oui-dom-utilsのsetStyles関数を参考にすると良いでしょう。
+ クラスを追加する// jQuery $el.addClass(className); // Native el.classList.add(className);
- クラスを削除する
// jQuery $el.removeClass(className); // Native el.classList.remove(className);
- クラスの有無をチェックする
// jQuery $el.hasClass(className); // Native el.classList.contains(className);
- クラスの有無を切り替える
// jQuery $el.toggleClass(className); // Native el.classList.toggle(className);
2.2 横幅と高さ
横幅(width)と高さ(height)の書き方はほぼ同じなので、高さ(height)の例のみを示します。
-
ウィンドウの高さ
// window height $(window).height(); // jQueryのようにスクロールバーを除いた高さ window.document.documentElement.clientHeight; // スクロールバーを含めるなら window.innerHeight;
-
ドキュメントの高さ
// jQuery $(document).height(); // Native document.documentElement.scrollHeight;
-
エレメントの高さ
// jQuery $el.height(); // Native function getHeight(el) { const styles = window.getComputedStyle(el); const height = el.offsetHeight; const borderTopWidth = parseFloat(styles.borderTopWidth); const borderBottomWidth = parseFloat(styles.borderBottomWidth); const paddingTop = parseFloat(styles.paddingTop); const paddingBottom = parseFloat(styles.paddingBottom); return height - borderBottomWidth - borderTopWidth - paddingTop - paddingBottom; } // integerで取得(`border-box`の時は`height`が、`content-box`の時は`height + padding + border`が返る) el.clientHeight; // decimalで取得(`border-box`の時は`height`が、`content-box`の時は`height + padding + border`が返る) el.getBoundingClientRect().height;
- 2.3 PositionとOffset
-
Position
offset parentを起点として、エレメントの座標を取得する。
// jQuery $el.position(); // Native { left: el.offsetLeft, top: el.offsetTop }
-
Offset
documentを起点として、エレメントの座標を取得する。
// jQuery $el.offset(); // Native function getOffset (el) { const box = el.getBoundingClientRect(); return { top: box.top + window.pageYOffset - document.documentElement.clientTop, left: box.left + window.pageXOffset - document.documentElement.clientLeft } }
- 2.4 スクロール位置
縦スクロールバーの位置を取得する。
// jQuery
$(window).scrollTop();
// Native
(document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop;
DOM操作
- 3.1 Remove
DOMからエレメントを削除する。
// jQuery
$el.remove();
// Native
el.parentNode.removeChild(el);
-
3.2 Text
- テキストを取得する
子孫エレメントも含めた全テキスト内容を取得する。
// jQuery $el.text(); // Native el.textContent;
- テキストを設定する
エレメントのコンテントを指定されたテキストに設定する。
// jQuery $el.text(string); // Native el.textContent = string;
-
3.3 HTML
- HTMLを取得する
// jQuery $el.html(); // Native el.innerHTML;
- HTMLを設定する
// jQuery $el.html(htmlString); // Native el.innerHTML = htmlString;
3.4 Append
最後の子要素としてエレメントを追加する。
// jQuery
$el.append("<div id='container'>hello</div>");
// Native
el.insertAdjacentHTML("beforeend","<div id='container'>hello</div>");
- 3.5 Prepend
最初の子要素としてエレメントを追加する。
// jQuery
$el.prepend("<div id='container'>hello</div>");
// Native
el.insertAdjacentHTML("afterbegin","<div id='container'>hello</div>");
- 3.6 insertBefore
指定要素の後ろに新しいノードを追加する。
// jQuery
$newEl.insertBefore(queryString);
// Native
const target = document.querySelector(queryString);
target.parentNode.insertBefore(newEl, target);
- 3.7 insertAfter
指定要素の前に新しいノードを追加する。
// jQuery
$newEl.insertAfter(queryString);
// Native
const target = document.querySelector(queryString);
target.parentNode.insertBefore(newEl, target.nextSibling);
- 3.8 is
セレクタにマッチするならtrue
を返す。
// is関数は複数エレメントや関数にも対応するが、matches関数は単一エレメントのみに使える
$el.is(selector);
// Native
el.matches(selector);
- 3.9 clone
エレメントのディープコピーを生成する。
// jQuery
$el.clone();
// Native
el.cloneNode();
// パラメータには`true`が渡され、深い複製を生成します。
// 浅い複製を生成するには、`false`を渡します。
- 3.10 empty
全ての子ノードを削除する。
// jQuery
$el.empty();
// Native
el.innerHTML = '';
- 3.11 wrap
エレメントを指定のHTMLで囲む。
// jQuery
$('.inner').wrap('<div class="wrapper"></div>');
// Native
Array.prototype.slice.call(document.querySelectorAll('.inner')).forEach(function(el){
var wrapper = document.createElement('div');
wrapper.className = 'wrapper';
el.parentNode.insertBefore(wrapper, el);
el.parentNode.removeChild(el);
wrapper.appendChild(el);
});
- 3.12 unwrap
セレクタにマッチしたエレメントの親要素をDOMから削除する。マッチしたエレメント自体は残す。
// jQuery
$('.inner').unwrap();
// Native
Array.prototype.slice.call(document.querySelectorAll('.inner')).forEach(function(el){
Array.prototype.slice.call(el.childNodes).forEach(function(child){
el.parentNode.insertBefore(child, el);
});
el.parentNode.removeChild(el);
});
- 3.13 replaceWith
セレクタにマッチしたエレメントの内容を与えられた内容に置き換える。
// jQuery
$('.inner').replaceWith('<div class="outer"></div>');
// Native
Array.prototype.slice.call(document.querySelectorAll('.inner')).forEach(function(el){
var outer = document.createElement('div');
outer.className = 'outer';
el.parentNode.insertBefore(outer, el);
el.parentNode.removeChild(el);
});
Ajax
Fetch APIはXMLHttpRequestを置き換える新たな規格です。ChromeとFirefoxで動きます。レガシーなブラウザでもpolyfillを使えます。
IE9以上ならgithub/fetch、IE8以上ならfetch-ie8、jsonpを利用したいならfetch-jsonpを試してみてください。
- 4.1 マッチしたエレメントをサーバから取得したHTMLに置き換える。
// jQuery
$(selector).load(url, completeCallback)
// Native
fetch(url).then(data => data.text()).then(data => {
document.querySelector(selector).innerHTML = data
}).then(completeCallback)
イベント
名前空間(namespace)と委譲(delegation)を利用した完全な代替手段が必要なら、 https://github.com/oneuijs/oui-dom-events を参照してください。
-
5.0 ドキュメントが読み込まれたときの動作(
DOMContentLoaded
)
// jQuery
$(document).ready(eventHandler);
// Native
// DOMContentLoadedがすでに完了していないか確認する
if (document.readyState !== 'loading') {
eventHandler();
} else {
document.addEventListener('DOMContentLoaded', eventHandler);
}
-
5.1 イベントをバインドする(
on
)
// jQuery
$el.on(eventName, eventHandler);
// Native
el.addEventListener(eventName, eventHandler);
-
5.2 イベントをアンバインドする(
off
)
// jQuery
$el.off(eventName, eventHandler);
// Native
el.removeEventListener(eventName, eventHandler);
-
5.3 イベントを発火させる(
trigger
)
// jQuery
$(el).trigger('custom-event', {key1: 'data'});
// Native
if (window.CustomEvent) {
const event = new CustomEvent('custom-event', {detail: {key1: 'data'}});
} else {
const event = document.createEvent('CustomEvent');
event.initCustomEvent('custom-event', true, true, {key1: 'data'});
}
el.dispatchEvent(event);
ユーティリティ関数
殆どのユーティリティ関数はネイティブのAPIで置き換えることができます。表記の一貫性やパフォーマンスを重視した他のライブラリを使う選択肢もあります。lodashがおすすめです。
-
6.1 基本的なユーティリティ関数
- isArray
配列かどうか判定する。
// jQuery
$.isArray(array);
// Native
Array.isArray(array);
- isWindow
windowかどうか判定する。
// jQuery
$.isWindow(obj);
// Native
function isWindow(obj) {
return obj != null && obj === obj.window;
}
- inArray
配列の中で、指定された値が最初に現れたインデックスを返す。(見つからなければ-1を返す)。
// jQuery
$.inArray(item, array);
// Native
Array.indexOf(item);
- isNumeric
数値かどうか判定する。
typeof
を使ってください。ライブラリを使う場合、typeof
は正確でない場合があります。
// jQuery
$.isNumeric(item);
// Native
function isNumeric(item) {
return typeof item === 'number';
}
- isFunction
JavaScript関数オブジェクトかどうか判定する。
// jQuery
$.isFunction(item);
// Native
function isFunction(item) {
return typeof item === 'function';
}
- isEmptyObject
空のオブジェクトである(列挙できる要素がない)か判定する。
// jQuery
$.isEmptyObject(obj);
// Native
function isEmptyObject(obj) {
for (let key in obj) {
return false;
}
return true;
}
- isPlainObject
{}
もしくはnew Object
で生成されたオブジェクトであるか判定する。
// jQuery
$.isPlainObject(obj);
// Native
function isPlainObject(obj) {
if (typeof (obj) !== 'object' || obj.nodeType || obj != null && obj === obj.window) {
return false;
}
if (obj.constructor &&
!{}.hasOwnProperty.call(obj.constructor.prototype, 'isPrototypeOf')) {
return false;
}
return true;
}
- extend
二つ以上のオブジェクトをマージする。
object.assign
はECMAScript6のAPIですが、polyfillも利用できます。
// jQuery
$.extend({}, defaultOpts, opts);
// Native
Object.assign({}, defaultOpts, opts);
- trim
前後の空白を除去する。
// jQuery
$.trim(string);
// Native
string.trim();
- map
配列やオブジェクトを新しい配列に変換する。
// jQuery
$.map(array, function(value, index) {
});
// Native
array.map(function(value, index) {
});
- each
配列やオブジェクトに対して繰り返し処理を行う。
// jQuery
$.each(array, function(value, index) {
});
// Native
array.forEach(function(value, index) {
});
- grep
フィルター関数に合致したエレメントだけを返す。
// jQuery
$.grep(array, function(value, index) {
});
// Native
array.filter(function(value, index) {
});
- type
JavaScript「クラス」名を判定します。
// jQuery
$.type(obj);
// Native
Object.prototype.toString.call(obj).replace(/^\[object (.+)\]$/, '$1').toLowerCase();
- merge
二つの配列をマージする。
// jQuery
$.merge(array1, array2);
// Native
// 重複した要素は削除されない
function merge() {
return Array.prototype.concat.apply([], arguments)
}
- now
現在の時刻を返す。
// jQuery
$.now();
// Native
Date.now();
- proxy
関数内で実行されるthisを任意のオブジェクトに変更する。
// jQuery
$.proxy(fn, context);
// Native
fn.bind(context);
- makeArray
配列形式のオブジェクトを配列に変換する。
// jQuery
$.makeArray(arrayLike);
// Native
Array.prototype.slice.call(arrayLike);
// ES6なら
Array.from(arrayLike);
- 6.2 contains
ある要素が他の要素の子孫であるか判定する。
// jQuery
$.contains(el, child);
// Native
el !== child && el.contains(child);
- 6.3 globaleval
JavaScriptコードをグローバル空間で実行する。
// jQuery
$.globaleval(code);
// Native
function Globaleval(code) {
let script = document.createElement('script');
script.text = code;
document.head.appendChild(script).parentNode.removeChild(script);
}
// evalはcurrentコンテキストで実行される。$.globalevalのコンテキストはグローバルである。
eval(code);
-
6.4 parse
- parseHTML
文字列をDOM nodeの配列として返します。
// jQuery
$.parseHTML(htmlString);
// Native
function parseHTML(string) {
const tmp = document.implementation.createHTMLDocument();
tmp.body.innerHTML = string;
return tmp.body.children;
}
- parseJSON
JSON文字列をJavaScriptに変換します。
// jQuery
$.parseJSON(str);
// Native
JSON.parse(str);
Promise
promiseは非同期処理の最終的な処理結果を表します。jQueryにはpromiseを扱うための独自の方法があります。ネイティブのJavaScriptではPromises/A+規格に則り、薄く、最小限のAPIを実装しています。
- 7.1 done, fail, always
done
はpromiseが成功(resolved)したとき、fall
は失敗(rejected)したとき、always
はどちらの場合も呼び出されます。
// jQuery
$promise.done(doneCallback).fail(failCallback).always(alwaysCallback)
// Native
promise.then(doneCallback, failCallback).then(alwaysCallback, alwaysCallback)
- 7.2 when
when
は複数のpromiseを扱うときに使います。すべてのpromiseの結果が返ったときに成功となります(失敗が含まれてても成功となります)。
// jQuery
$.when($promise1, $promise2).done((promise1Result, promise2Result) => {})
// Native
Promise.all([$promise1, $promise2]).then([promise1Result, promise2Result] => {});
- 7.3 Deferred
Deferred
はpromiseを作成する方法の一つです。
// jQuery
function asyncFunc() {
var d = new $.Deferred();
setTimeout(function() {
if(true) {
d.resolve('some_value_compute_asynchronously');
} else {
d.reject('failed');
}
}, 1000);
return d.promise();
}
// Native
function asyncFunc() {
return new Promise((resolve, reject) => {
setTimeout(function() {
if (true) {
resolve('some_value_compute_asynchronously');
} else {
reject('failed');
}
}, 1000);
});
}
// Deferred way
function defer() {
let resolve, reject;
let promise = new Promise(function() {
resolve = arguments[0];
reject = arguments[1];
});
return { resolve, reject, promise };
}
function asyncFunc() {
var d = defer();
setTimeout(function() {
if(true) {
d.resolve('some_value_compute_asynchronously');
} else {
d.reject('failed');
}
}, 1000);
return d.promise;
}
アニメーション
- 8.1 show、hide
// jQuery
$el.show();
$el.hide();
// Native
// show関数の詳細を見たければ次のURLを参照してください
// https://github.com/oneuijs/oui-dom-utils/blob/master/src/index.js#L363
el.style.display = ''|'inline'|'inline-block'|'inline-table'|'block';
el.style.display = 'none';
- 8.2 toggle
エレメントが表示されていないなら表示し、表示されているなら非表示にします。
!!2018-12-25 Chrome バージョン: 71.0.3578.98で動作しないことを確認
// jQuery
$el.toggle();
// Native
if (el.ownerDocument.defaultView.getComputedStyle(el, null).display === 'none') {
el.style.display = ''|'inline'|'inline-block'|'inline-table'|'block';
}
else {
el.style.display = 'none';
}
- 8.3 fadeIn、fadeOut
// jQuery
$el.fadeIn(3000);
$el.fadeOut(3000);
// Native
el.style.transition = 'opacity 3s';
// fadeIn
el.style.opacity = '1';
// fadeOut
el.style.opacity = '0';
- 8.4 fadeTo
エレメントのopacityを調整してください。
// jQuery
$el.fadeTo('slow',0.15);
// Native
el.style.transition = 'opacity 3s'; // 'slow'は3秒だということにしている
el.style.opacity = '0.15';
- 8.5 fadeToggle
フェードイン・フェードアウトを伴ってエレメントの表示・非表示を切り替えます。
// jQuery
$el.fadeToggle();
// Native
el.style.transition = 'opacity 3s';
let { opacity } = el.ownerDocument.defaultView.getComputedStyle(el, null);
if (opacity === '1') {
el.style.opacity = '0';
}
else {
el.style.opacity = '1';
}
- 8.6 スライドアップ、スライドダウン
// jQuery
$el.slideUp();
$el.slideDown();
// Native
let originHeight = '100px';
el.style.transition = 'height 3s';
// slideUp
el.style.height = '0px';
// slideDown
el.style.height = originHeight;
- 8.7 slideToggle
スライドを伴って、エレメントの表示・非表示を切り替えます。
// jQuery
$el.slideToggle();
// Native
let originHeight = '100px';
el.style.transition = 'height 3s';
let { height } = el.ownerDocument.defaultView.getComputedStyle(el, null);
if (parseInt(height, 10) === 0) {
el.style.height = originHeight;
}
else {
el.style.height = '0px';
}
- 8.8 animate
CSSプロパティで定義されたアニメーションを表示します。
// jQuery
$el.animate({params}, speed);
// Native
el.style.transition = 'all' + speed;
Object.keys(params).forEach(function(key) {
el.style[key] = params[key];
})
選択肢
- You Might Not Need jQuery - ネイティブのJavaScriptでイベント、エレメント、Ajaxを扱うサンプル集(英語)
- npm-dom and webmodules - npmで利用できるDOMモジュールを集めたOrganizationです
対応ブラウザ
Latest ✔ | Latest ✔ | 10+ ✔ | Latest ✔ | 6.1+ ✔ |
ライセンス
MIT
-
「もちろん。MITライセンスなんだから好きにしていいよ」とのこと。かっこいい! ↩