JavaScript のスニペット集

このページは、JavaScript のスニペットなどをまとめる予定のページです。

目次

注意

  • コードのライセンスは CC0 (クレジット表示不要、改変可、商用可) です。
  • 主にブラウザ上で使用する JavaScript のスニペットです。Node.js で使用するスニペットは Node.js のスニペット集 を参照してください。
  • 少なくとも IE11 以上で使用できるように調整しています。(他の想定ブラウザは Edge, Chrome, Firefox)

スニペット

strict モード

(function(){
    "use strict";

    // 処理
})();

matches()

// polyfill (https://developer.mozilla.org/ja/docs/Web/API/Element/matches)
if (!Element.prototype.matches) Element.prototype.matches = Element.prototype.msMatchesSelector;
document.addEventListener("click", function(e){
    if (e.target.matches(".btn, .btn *")) {
        // .btn (.btnそのものか、.btn内の要素) がクリックされたときの処理
    }
});
  • matches() は 対象要素が指定したCSSセレクタに合致するか確認するためのメソッドです。 (jQuery.is() に近い)
  • 上記のような形でイベントリスナ追加をしていると、対象要素が動的に追加されるケースにも対応できて便利です。

closest()

// polyfill (https://developer.mozilla.org/en-US/docs/Web/API/Element/closest)
if (!Element.prototype.closest) {
    Element.prototype.closest = function(s) {
        var el = this;
        do { if (el.matches(s)) return el; el = el.parentElement || el.parentNode; } while (el !== null && el.nodeType === 1);
        return null;
    };
}
  • closest() は、対象要素の親要素を順々に辿って、セレクタにマッチする最初の要素を取得するメソッドです。(jQuery.closest() に近い)

querySelectorAll() + forEach()

Array.prototype.forEach.call(document.querySelectorAll("セレクタ"), function(el, i){
    // 処理
});
  • jQuery.each() のような、セレクタで選択した要素すべてになにかする処理をライブラリ無し (+ for of が使えない) の環境で行うときに使います。
if (!NodeList.prototype.forEach) NodeList.prototype.forEach = Array.prototype.forEach;

resize イベント + 間引き (requestAnimationFrame)

(function(){
    let requestId;
    // リサイズ時
    window.addEventListener("resize", function(){
        cancelAnimationFrame(requestId);
        requestId = requestAnimationFrame(function(){
            // 処理
        });
    });
})();
  • ブラウザウィンドウをリサイズすると resize イベントが大量に発生するため、requestAnimationFrame() を使用して処理を間引きます。

wheel イベント

// ホイールスクロール時
document.addEventListener("wheel", function(e){
    // 下スクロール
    if (e.deltaY > 0) {
    }
    // 上スクロール
    else if (e.deltaY < 0) {
    }
});

タッチでのスクロール無効

document.addEventListener("touchmove", function(e){ e.preventDefault() }, { passive: false });

スワイプ (上下)

let prevY = null;
document.addEventListener("touchstart", function(e){
    prevY = e.touches[0].clientY;
});
document.addEventListener("touchmove", function(e){
    if (prevY == null) return;

    // 下から上へスワイプ
    if (e.touches[0].clientY < prevY) {
        // 何か処理
        prevY = null;
    }
    // 上から下へスワイプ
    else if (e.touches[0].clientY > prevY) {
        // 何か処理
        prevY = null;
    }

    e.preventDefault();
}, { passive: false });

要素が画面上 (ビューポート上) に入っているか確認する

const viewportWidth = document.documentElement.clientWidth; // ビューポート幅 (スクロールバー除く)
const viewportHeight = document.documentElement.clientHeight; // ビューポート高さ (スクロールバー除く)
const rect = el.getBoundingClientRect(); // 要素の位置 (ビューポート上)
const yOK = (0 <= rect.top && rect.top <= viewportHeight) || (0 <= rect.bottom && rect.bottom <= viewportHeight); // 上辺または下辺がビューポート内
const xOK = (0 <= rect.left && rect.left <= viewportWidth) || (0 <= rect.right && rect.right <= viewportWidth); // 左辺または右辺がビューポート内

matchMedia()

幅のチェック (1回)

if (window.matchMedia("(min-width: 680px)").matches) {
    // 幅680px以上
}
else {

}

幅のチェック (イベント)

const handle = function(list) {
    if (list.matches) {
        // 幅680px以上
    }
    else {
        // 幅680px未満
    }
};
let list = window.matchMedia("(min-width: 680px)");
list.addListener(handle); // 幅変更時確認
handle(list); // 初回確認

外部リンクを新しいタブで開く

if (!Element.prototype.matches) Element.prototype.matches = Element.prototype.msMatchesSelector;
document.addEventListener('click', function(e){
    if (e.target.matches('a[href^="http"]:not([target])')) {
        e.target.relList.add('noopener');
        e.target.target = '_blank';
    }
});

SVGElement で classList を使用可能にする (IE11)

// SVGElement で classList を使用可能にする (IE11)
if (!("classList" in SVGElement)) {
    const classList = Object.getOwnPropertyDescriptor(HTMLElement.prototype, "classList");
    if (classList) Object.defineProperty(SVGElement.prototype, "classList", classList);
}
  • IE11より古いブラウザは想定していません。

URL のパース (URLSearchParams を使わない簡易的なもの)

var search = location.search.substring(1);
JSON.parse('{"' + decodeURI(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g,'":"') + '"}');

数値のフォーマット

const formatted = new Intl.NumberFormat("ja").format(1000);

日時のフォーマット

// yyyy/mm/dd hh:mm:ss
const d = new Intl.DateTimeFormat("ja", { year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit" }).format(new Date()).replace(/[年月]/g, "/").replace(/日/g, "");
// yyyymmddhhmmss
const d = new Intl.DateTimeFormat("ja", { year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit" }).format(new Date()).replace(/[\/ :年月日]/g, "");
// yyyy/mm/dd
const d = new Intl.DateTimeFormat("ja", { year: "numeric", month: "2-digit", day: "2-digit" }).format(new Date()).replace(/[年月]/g, "/").replace(/日/g, "");
// yyyymmdd
const d = new Intl.DateTimeFormat("ja", { year: "numeric", month: "2-digit", day: "2-digit" }).format(new Date()).replace(/[\/年月日]/g, "");
// hh:mm:ss
const d = new Intl.DateTimeFormat("ja", { hour: "2-digit", minute: "2-digit", second: "2-digit" }).format(new Date());
// hhmmss
const d = new Intl.DateTimeFormat("ja", { hour: "2-digit", minute: "2-digit", second: "2-digit" }).format(new Date()).replace(/:/g, "");
  • Intl.DateTimeFormat のオプションに year: "numeric", month: "2-digit", day: "2-digit" を指定した結果が yyyy年mm月dd日 (IE11) になるか yyyy/mm/dd (その他) になるかブラウザごとに異なるため、変換結果として出てくる可能性のあるすべての区切り文字を replace() で置換しています。
  • Date.prototype.toJSON() を使うとタイムゾーンが UTC になるため日本時間と9時間差異が生まれます。

合計

const arr = [1, 2, 3, 4];
const sum = arr.reduce((acc, curr) => acc + curr);

ユーティリティ関数

HTML エスケープ

/**
 * 指定文字列を HTML エスケープします。
 * @param {string} s 指定文字列 
 * @returns {string}
 */
const h = function(s) {
    const map = { "<" : "&lt;", ">" : "&gt;", "&" : "&amp;", "\"": "&#quot;", "'" : "&#39;" };
    return String(s).replace(/[<>&"']/g, function(c){ return map[c]; });
};

値の範囲制限

/**
 * 指定値が最小、最大の範囲外の場合に最小、最大値を、範囲内の場合に指定値をそのままを返します。(Math.max(min, Math.min(x, max)) と同等)
 * @param {Number} x   指定値
 * @param {Number} min 最小値
 * @param {Number} max 最大値
 * @returns {Number} 補正された値
 */
const minmax = function(x, min, max) { return x < min ? min : max < x ? max : x; }

度 → ラジアン

/**
 * 度 → ラジアンに変換します。
 * @param {Number} deg 度数 (45 等) 
 * @returns {Number}
 */
const rad = function(deg){ return deg * (Math.PI / 180); }

querySelectorAll() + forEach()

/**
 * 指定セレクタにマッチした要素を forEach します。
 * @param {string}    selector 指定セレクタ
 * @param {Function}  fn       コールバック
 * @param {Node}      root     ルート要素 (未指定の場合 Document)
 */
const eachElements = function(selector, fn, root){
    Array.prototype.forEach.call((root || document).querySelectorAll(selector), fn);
};

XPath 検索 ($x)

/**
 * 指定した XPath にマッチした要素を配列で取得します。(例: $x('descendant::comment()'))
 * @param {string} path XPath
 * @param {Node} startNode ルート要素 (未指定の場合 Document)
 * @return {Node[]} 結果ノード配列
 */
function $x(path, startNode) {
    const nodes = [];
    const result = document.evaluate(path, startNode || document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    for (let i = 0, len = result.snapshotLength; i < len; i++) nodes.push(result.snapshotItem(i));
    return nodes;
}

Chrome のコンソールで使用できる $x のエミュレートです。

DOM 要素の生成

/**
 * DOM 要素を簡易的に生成します。( 例: newElement("a", { href: "#" }, "リンクです") )
 * @param {string}          tagName  タグ名
 * @param {Object}          props    プロパティ・属性
 * @param {(Node|string)[]} contents 内容
 * @returns {Element} 生成要素
 */
const newElement = function(tagName, props, contents){
    const el = document.createElement(tagName);
    if (props) { // プロパティ・属性 (基本はプロパティ、一部は属性などで扱います)
        Object.keys(props).forEach(function(k){
            if (/^data-/.test(k)) el.setAttribute(k, props[k]); // data-*
            else if (k == "class") el.className = props[k]; // class → className
            else el[k] = props[k];
        });
    }
    if (contents) { // 内容 (Node、テキスト、Nodeやテキストが含まれた配列)
        const contentsArray = Array.isArray(contents) ? contents : [contents];
        contentsArray.forEach(function(content){
            const node = content instanceof Node ? content : document.createTextNode(content);
            el.appendChild(node);
        });
    }
    return el;
};

DOM 要素の生成 (HTML文字列から)

/**
 * HTML文字列から新しい要素を生成します。(例: newElementFromString('<div class="box">test</div>'))
 * @param {string} htmlString HTML文字列
 * @returns Element
 */
function newElementFromString(htmlString) {
    const tmpl = document.createElement('div');
    tmpl.innerHTML = htmlString;
    return tmpl.firstElementChild;
}

次の要素

/**
 * 対象要素の兄弟要素の中で指定セレクタにマッチする次の要素を取得します。(jQuery の next(selector) に近いものです)
 * @param {Element}   el       対象要素
 * @param {string}    selector 指定セレクタ
 * @returns {Element}          見つかった要素 (存在しない場合 null)
 */
const nextElementSibling = function(el, selector) {
    let next = el.nextElementSibling;
    while (next) { if (next.matches(selector)) return next; next = next.nextElementSibling; };
    return null;
};

前の要素

/**
 * 対象要素の兄弟要素の中で指定セレクタにマッチする前の要素を取得します。(jQuery の prev(selector) に近いものです)
 * @param {Element}   el       対象要素
 * @param {string}    selector 指定セレクタ
 * @returns {Element}          見つかった要素 (存在しない場合 null)
 */
const previousElementSibling = function(el, selector) {
    let prev = el.previousElementSibling;
    while (prev) { if (prev.matches(selector)) return prev; prev = prev.previousElementSibling; };
    return null;
};

イベント発火 (簡易)

/**
 * 指定イベントを発火させる簡易的な関数です。(例: trigger(document.body, 'click'))
 * 
 * @param {Object}  target       イベントターゲット
 * @param {string}  name         イベント名 (例: 'click')
 * @param {boolean} [bubbles]    イベントがバブルするか
 * @param {boolean} [cancelable] イベントがキャンセル可能か
 * @param {Object}  [detail]     カスタムイベント時の追加データ
 */
const trigger = function(target, name /*, bubbles = true, cancelable = true, detial = null */) {
    const bubbles = 2 in arguments ? arguments[2] : true; // IE11 ではデフォルト引数が使用できない
    const cancelable = 3 in arguments ? arguments[3] : true;
    const detail = 4 in arguments ? arguments[4] : true;
    const intarface = detail ? "CustomEvent" : "Event";
    const event = document.createEvent(intarface); // IE11用にイベントコンストラクタを使用せずにインスタンスを生成します。

    if (intarface == "CustomEvent") event.initCustomEvent(name, bubbles, cancelable, detail);
    else event.initEvent(name, bubbles, cancelable);
    target.dispatchEvent(event);
};