ブックマークレットを書こうとしたらShadow DOMでちょっと躓いた話

あけましておめでとうございます

まず

僕はあまりブックマークを使用せず、履歴だけで生きるようにしています。
特に強い理由はないんですが、Chromeのアドレスバーが優秀なのでマウスでブックマークを探すより cmd + L でアドレスを打ったほうが早いから、ぐらいです。

困ったこと

そういう生活をしていると、履歴を整理する必要が出てきます。
例えば、同じ系列のツールを使っていて乗り換えたりしてもう滅多に使わなくなったサイトの履歴が残り続けていると、常にそれがアドレスバーの優先度高で補完に出てきてしまい、わりとフラストレーションが溜まります。
かといって、履歴を全消ししてしまうとその日のパフォーマンスが1/3ぐらいになってしまうので、特定のドメイン配下の履歴だけいい感じに消したくなります。
なので、ブックマークレット(JavaScript snippet)を書きました。

できたもの

実行ページはここ chrome://history/

infiniteList = document.querySelector("#history-app::shadow > #main-container #content #history::shadow > #infinite-list");
historyItems = infiniteList.getElementsByTagName("history-item");
for(let ele of historyItems){
    if (!ele.hidden) ele.querySelector("::shadow > #main-container #checkbox").click()
}

これで基本的に40件ぐらいチェックされるので、あとは一回消してもう一回実行して、みたいなことをしないといけない。 scrollを含めてやれば全部自動でできそうなんだけど、ShadowDOM内部の更新タイミングみたいなのが順当になっていなくて綺麗にできそうになかったので諦めた。

解説

基本的にChromeの履歴ページはShadowDomのネストになっており、ReactのComponent単位でShadowRootオブジェクトが挟まっているような形になっています。 そして、ShadowRootを挟む際にはselectorに ::shadow > をつけて掘っていきます。

<body>
  <div id="main-container">
    #shadow-root
      <div id="shadow-item"></div>
  </div>
</body>

に対して

// これは何もhitせず
document.querySelector( "#shadow-item" );
// shadow-rootを掘る必要がある
document.querySelector( "#main-container::shadow > #shadow-item" );

です。 ページ内にあるチェックボックスを全部つけるぐらい楽だろうと思って軽い気持ちで書き始めたら意外に詰まってしょげでした。
あとは、とりあえずガッとComponentを出して、Listの中身が2件でも他のListをhiddenにしているだけのようで?表示されているかどうかを見ないと無を選択してしまいこのようになってしまうので注意が必要でした。

f:id:miyachik:20180206203114p:plain

こんなことするならちまちまボタン押したほうが早くない?

はい