jQueryに頼らない! DOM操作を伴うブックマークレットの作り方


概要

  • jQuery使わないよ
  • 基本的なDOM情報の取得・操作はJavaScriptのquerySelector querySelectorAllを使うよ
  • ネイティブのメソッド(querySelectorなど)を別の変数に入れて使おうとしたら怒られたよ
  • querySelectorで取得できるNodeListは配列ではないので、配列のメソッドで処理する時は配列化しておくと便利だよ

ブックマークレットとは

  1. ブックマークに追加
  2. 適当な名前をつけ、URLとして「javascript:」から始まるコードを保存。
  3. 任意のページで実行する(そのブックマークをクリックする)と、そのスクリプトが実行できる。

ES2015

自分が使えればいいので、ブラウザ対応は考えなくて良い。
思う存分最新のJS文法で書きましょう。

即時実行関数(一番外側のfunction)の内側に処理を書いていきます。

(function() {
  "use strict";
  r();
  // 実行本体
  function r() {
    // ここに色々書いていく
  }
})();

取得→配列化

配列のメソッドを使って抽出・操作したいので、任意のDOMを取得したら即配列化します。
domArrは配列なので、.map().filter().reduce() など自由に使えます。
※callメソッドを使えばNodeListのまま関数適用は可能ですが、繰り返しメソッドを適用する場合には面倒です。

// 取得
const $dom = document.querySelectorAll('.className');

// 配列化
const domArr = [].slice.call($dom);

// スプレッド演算子を使っても良い
const domArr = [...$dom];

抽出

クラスで大まかに絞った後、「要素内のタイトルが「にゃーん」の要素だけに絞りたい」なんていうことをしないといけなかったりします。
配列のfilter、findメソッドの出番です。メソッド内で、任意の要素を取得し、そのtextContentを文字列比較して true を返すようにすれば抽出できます。
そもそも要素自体が取得できないケースもあるため、その場合はfalseを返すようにします。

filter

複数の要素を抽出する場合に使います。

const $target = domArr.filter(el => {
  const $title = el.querySelector('.title');
  if(!$title) { // そもそも要素が取得できないケースもあるので falseを返すようにする
    return false;
  }
  return $title.textContent === 'にゃーん';
});

// $targetは配列なので、抽出した1つの要素を操作する場合は $target[0] でアクセスする 

find

最初に見つかった1つの要素のみで良い場合は、filterよりもfindの方が向いています。
挙動・返り値が変わるだけで、条件の書き方はfilterと同じです。
つまりリファクタリングしやすい、ということ。

const $target = domArr.find(el => {
  const $title = el.querySelector('.title');
  if(!$title) { // そもそも要素が取得できないケースもあるので falseを返すようにする
    return false;
  }
  return $title.textContent === 'にゃーん';
});

// $targetはDOM要素

合計する

今回はターゲットとなるカラムのアイテム1つ1つに含まれるポイント数を合計したかったので、
さくっとポイントの文字列を抽出して合計します。

// ポイントが含まれている要素を抽出
const $pointDom = $target.querySelectorAll('.point');

// 配列化
const pointDomArr = [...$pointDom];

// ポイント文字列を数値に変換しつつ抽出 → さらにundefinedなど、合計すると NaN になる要素を除外
// [1, 3, 5...] のような配列が生成される
const pointArr = pointDomArr.map(v => v.textContent * 1).filter(v => v);

// 合計
const points = pointArr.reduce((a, b) => a + b, 0);

セミコロンを忘れないこと

minifyして1行にするため、各処理の最後にセミコロンがないと意味が変わってエラーが生じてしまいます。省略しないこと。

minify

素晴らしいWebサービスが存在するので使わせていただきましょう。
エディタ上で出来るけど? という方はそれで構いません。
下記のサービスの場合、ブックマークレット化に必要な「javascript:」も補ってくれます。

備考

怒られました。


// TypeError: Illegal invocation
const qAll = document.querySelectorAll;

// これならOK
const qAll = function(selector){
  return document.querySelectorAll(selector);
};

作ったもの

今回はAsanaの「DONE」とタイトルの付いたリストのポイント(スクリーンショット「2」の部分)を合計するためのブックマークレットを作りました。
日頃のタスクをちょっと便利にするブクマ、気軽に作っていきましょう〜。