要約すると、クラス名ではなく、CSSセレクタの:nth-child
を使って階層の位置で表現すれば、削除したいElementを特定できるんじゃないかな、と思ってやってみたが、しんどくて諦めてます、という感じです。
最近仕事していても楽しくなくて愚痴が出がちです。 でも、愚痴っても誰も助けてくれないし、見てる方も「こいつずっと愚痴ってんな、さっさと仕事辞めるか死ねばいいのに」って思うに違いないんです。
それでも、助けを求めるように呟いてしまうわけです。ならいっそのことTwitterを呟けなくすればよいのではないか、と考えました。
ということで、Twitterで呟けないようにするために、DOMからTweetする場所(テキストボックス、リプライアイコン等)を削除するWebExtensionを書き始めた。ついでにプロモートツイートも消してる。
ただ画面表示時とbody以下のelementの変化を拾って特定のCSSセレクタにマッチするElementを削除しているだけです。
とりあえずデバッグモードで動いているけども、早くも問題がにぶち当たった。
Twitterが新しいデザインになるらしく、アカウントによってはこんな感じで選べるようになっている。
右上の自分のアイコンをクリックすると出るメニュー
で、切り替えると2カラムスタイルになるのだけれど、DOMベースで探している都合上、デザインが変わると当然機能しない。 さらに厄介なのが、デザインが変わっただけではなく、 Class名の難読化を入れている
普通の開発ならこんな面倒なことする意味がないので、多分Adblocker対策なんだろうか。
なので、もうこのWebExtensionはあきらめているのだけれど、ふと難読化はしても早々DOMの構造は替えないよな?というアイディアを思いつく。
つまり、ネストを重ねていても、n番目の子のm番目の子のo番目の子の....とやってDOMを辿れば目的の要素を見つけられるはず。 さらに、timelineのように連続した要素を並べる場合でも、n番目の子のすべての子のo番目の子の...とやれば、全部引っこ抜けるはず。
それどうやるの?というと、CSSセレクターに:nth-child
があるのでこれでやります。
例: timeline中のリプライアイコンを消す CSSセレクタ(リツイート、プロモーションは除く)
#react-root > *:nth-child(2) > *:nth-child(2) > *:nth-child(1) > *:nth-child(2) > *:nth-child(1) > *:nth-child(1) > *:nth-child(1) > *:nth-child(2) > *:nth-child(1) > *:nth-child(1) > *:nth-child(2) > *:nth-child(1) > *:nth-child(1) > * > *:nth-child(1) > *:nth-child(1) > *:nth-child(1) > *:nth-child(1) > *:nth-child(2) > *:nth-child(2) > *:nth-child(1)
分かりにくいですが途中に> * >
というセレクタがあり、これがtimelineの連続しているツイートの要素に対応します。
実際に開発者ツールを開いて以下のスクリプトを流せばどのElementが取れるかは分かるはず。
document.querySelector('#react-root > *:nth-child(2) > *:nth-child(2) > *:nth-child(1) > *:nth-child(2) > *:nth-child(1) > *:nth-child(1) > *:nth-child(1) > *:nth-child(2) > *:nth-child(1) > *:nth-child(1) > *:nth-child(2) > *:nth-child(1) > *:nth-child(1) > * > *:nth-child(1) > *:nth-child(1) > *:nth-child(1) > *:nth-child(1) > *:nth-child(2) > *:nth-child(2) > *:nth-child(1)')
これであればデザイン修正されるまでは安定してDOMを消せるんじゃないかな、って思った。 (しかしこっちの方法も無意味なDOMを間に挟まれたり、囲まれたりすると順番が狂うので死ぬ。そんな複雑化することはしないようにしてほしい・・・)
で、これどうやって作るかというと、最初は手で数えてた。しかし結構しんどかったので、JavaScriptを書きました。 ベースになるCSSセレクタと、消したいCSSセレクタと、連続する要素なので無視したいCSSセレクタ(option)の2~nの引数を取るconvertNthChild関数です。
function findTarget(root, target, ignoreElements){ for(let i=0;i<root.children.length; ++i){ if(root.children[i] === target){ return [i] } const idx2 = findTarget(root.children[i], target, ignoreElements) if(idx2 !== undefined){ if(ignoreElements.indexOf(root.children[i]) === -1){ return [i].concat(idx2) }else{ return [-1].concat(idx2) } } } return undefined } function nthchild(arr){ return arr.map(n => n === -1 ? "*" : `*:nth-child(${n+1})`).join(' > ') } function convertNthChild(root, target, ...ignores){ let ignoreElements = ignores.map(el => document.querySelector(el)) const a = findTarget( document.querySelector(root), document.querySelector(target), ignoreElements) if(a){ return root + " > " + nthchild(a) } return undefined } // 使用例 a = convertNthChild( '#react-root', '#react-root > div.rn-1oszu61.rn-1efd50x.rn-14skgim.rn-rull8r.rn-mm0ijv.rn-13yce4e.rn-fnigne.rn-ndvcnb.rn-gxnn5r.rn-deolkf.rn-6koalj.rn-1qe8dj5.rn-1mlwlqe.rn-eqz5dr.rn-1pi2tsx.rn-1mnahxq.rn-61z16t.rn-p1pxzi.rn-11wrixw.rn-ifefl9.rn-bcqeeo.rn-wk8lta.rn-9aemit.rn-1mdbw0j.rn-gy4na3.rn-bnwqim.rn-13qz1uu.rn-1lgpqti > main > div > div.rn-1oszu61.rn-aqfbo4.rn-e84r5y.rn-1efd50x.rn-14skgim.rn-rull8r.rn-mm0ijv.rn-13yce4e.rn-fnigne.rn-ndvcnb.rn-gxnn5r.rn-deolkf.rn-6koalj.rn-1qe8dj5.rn-1mlwlqe.rn-eqz5dr.rn-16y2uox.rn-1mnahxq.rn-61z16t.rn-p1pxzi.rn-11wrixw.rn-ifefl9.rn-bcqeeo.rn-wk8lta.rn-9aemit.rn-1mdbw0j.rn-gy4na3.rn-bnwqim.rn-1lgpqti > div > div.rn-1oszu61.rn-14lw9ot.rn-1efd50x.rn-14skgim.rn-rull8r.rn-mm0ijv.rn-13yce4e.rn-fnigne.rn-ndvcnb.rn-gxnn5r.rn-deolkf.rn-6koalj.rn-1qe8dj5.rn-1mlwlqe.rn-eqz5dr.rn-1mnahxq.rn-lchren.rn-p1pxzi.rn-1jj8364.rn-1ye8kvj.rn-ifefl9.rn-bcqeeo.rn-wk8lta.rn-9aemit.rn-1mdbw0j.rn-gy4na3.rn-bnwqim.rn-13qz1uu.rn-184en5c > div > div.rn-1oszu61.rn-1efd50x.rn-14skgim.rn-rull8r.rn-mm0ijv.rn-13yce4e.rn-fnigne.rn-ndvcnb.rn-gxnn5r.rn-deolkf.rn-6koalj.rn-1qe8dj5.rn-1mlwlqe.rn-eqz5dr.rn-1mnahxq.rn-lchren.rn-p1pxzi.rn-1jj8364.rn-1ye8kvj.rn-ifefl9.rn-bcqeeo.rn-wk8lta.rn-9aemit.rn-gy4na3.rn-6337vo.rn-bnwqim.rn-13qz1uu.rn-1lgpqti > div > section > div > div > div > div:nth-child(4) > div > div > article > div > div.rn-1oszu61.rn-1efd50x.rn-14skgim.rn-rull8r.rn-mm0ijv.rn-13yce4e.rn-fnigne.rn-ndvcnb.rn-gxnn5r.rn-deolkf.rn-6koalj.rn-1qe8dj5.rn-1iusvr4.rn-eqz5dr.rn-46vdb2.rn-1mnahxq.rn-7o8qx1.rn-p1pxzi.rn-1f6r7vd.rn-ifefl9.rn-bcqeeo.rn-wk8lta.rn-9aemit.rn-1mdbw0j.rn-gy4na3.rn-bnwqim.rn-1lgpqti > div.rn-1oszu61.rn-1efd50x.rn-14skgim.rn-rull8r.rn-mm0ijv.rn-13yce4e.rn-fnigne.rn-ndvcnb.rn-gxnn5r.rn-deolkf.rn-6koalj.rn-1qe8dj5.rn-1mlwlqe.rn-18u37iz.rn-1wtj0ep.rn-61z16t.rn-p1pxzi.rn-11wrixw.rn-156q2ks.rn-1mdbhws.rn-ifefl9.rn-bcqeeo.rn-wk8lta.rn-9aemit.rn-1mdbw0j.rn-gy4na3.rn-bnwqim.rn-1lgpqti > div:nth-child(1)', '#react-root > div.rn-1oszu61.rn-1efd50x.rn-14skgim.rn-rull8r.rn-mm0ijv.rn-13yce4e.rn-fnigne.rn-ndvcnb.rn-gxnn5r.rn-deolkf.rn-6koalj.rn-1qe8dj5.rn-1mlwlqe.rn-eqz5dr.rn-1pi2tsx.rn-1mnahxq.rn-61z16t.rn-p1pxzi.rn-11wrixw.rn-ifefl9.rn-bcqeeo.rn-wk8lta.rn-9aemit.rn-1mdbw0j.rn-gy4na3.rn-bnwqim.rn-13qz1uu.rn-1lgpqti > main > div > div.rn-1oszu61.rn-aqfbo4.rn-e84r5y.rn-1efd50x.rn-14skgim.rn-rull8r.rn-mm0ijv.rn-13yce4e.rn-fnigne.rn-ndvcnb.rn-gxnn5r.rn-deolkf.rn-6koalj.rn-1qe8dj5.rn-1mlwlqe.rn-eqz5dr.rn-16y2uox.rn-1mnahxq.rn-61z16t.rn-p1pxzi.rn-11wrixw.rn-ifefl9.rn-bcqeeo.rn-wk8lta.rn-9aemit.rn-1mdbw0j.rn-gy4na3.rn-bnwqim.rn-1lgpqti > div > div.rn-1oszu61.rn-14lw9ot.rn-1efd50x.rn-14skgim.rn-rull8r.rn-mm0ijv.rn-13yce4e.rn-fnigne.rn-ndvcnb.rn-gxnn5r.rn-deolkf.rn-6koalj.rn-1qe8dj5.rn-1mlwlqe.rn-eqz5dr.rn-1mnahxq.rn-lchren.rn-p1pxzi.rn-1jj8364.rn-1ye8kvj.rn-ifefl9.rn-bcqeeo.rn-wk8lta.rn-9aemit.rn-1mdbw0j.rn-gy4na3.rn-bnwqim.rn-13qz1uu.rn-184en5c > div > div.rn-1oszu61.rn-1efd50x.rn-14skgim.rn-rull8r.rn-mm0ijv.rn-13yce4e.rn-fnigne.rn-ndvcnb.rn-gxnn5r.rn-deolkf.rn-6koalj.rn-1qe8dj5.rn-1mlwlqe.rn-eqz5dr.rn-1mnahxq.rn-lchren.rn-p1pxzi.rn-1jj8364.rn-1ye8kvj.rn-ifefl9.rn-bcqeeo.rn-wk8lta.rn-9aemit.rn-gy4na3.rn-6337vo.rn-bnwqim.rn-13qz1uu.rn-1lgpqti > div > section > div > div > div > div:nth-child(4)' )
最後のCSSセレクターの嵐は開発者ツールを使い、以下のようにして取得できます。
1つ目は基準になるlementのCSSセレクタです。ここから下を辿って目的のElementを探します。
2つ目は狙っているElementのCSSセレクタです。連続した要素であってもかまいません。
3つ目以降は連続要素と見なすlementのCSSセレクタを指定します。うまく言葉で説明できないんですが、これも開発者ツールで見ると簡単に見つけられます。
ここまでやって画像をぺたぺたしてる時に気づいたのですが、アクセシビリティの考慮なのか、aria-label
が結構個性的な名前付いていたり、articleタグが使われていたりするので、そっちで見て削除していった方がよさそうにも見えます。
まぁでも、多分対策されるor変更の影響を受けやすすぎて安定しなさそうなので、ちょっと諦めてます。