X のタイムラインでリポストだけを非表示にしたり表示に戻したりと切り替えたくなることはないでしょうか。
この記事では、Google Chrome 用の拡張機能を自作する手順をまとめます。ツールバーのアイコンからポップアップを開き、スイッチを切り替えると、表示中の X のタブのタイムラインから、リポスト行を非表示にしたり、再び表示したりできます。
できること
- タイムライン(おすすめ・リスト等)に表示されるリポストを非表示にできる
- 表示・非表示をいつでも切り替えられる
- 表示・非表示状態をブラウザ内に保存し、閉じても状態を維持する
用意するファイル
同じフォルダに次を置く(icon.png は任意)。
manifest.jsoncontent.jspopup.html/popup.jsicon.png(256×256 推奨。無ければ manifest のiconsごと削る)
manifest.json
下の JSON は manifest_version が 3 の Manifest V3 向け。ファイル名 manifest.json で保存する。
content_scripts…x.com/twitter.comでcontent.jsを注入action.default_popup… ツールバーアイコン用の HTMLpermissionsのstorage… ポップアップと content でrtFilterEnabledを共有
{
"name": "X リポスト非表示",
"manifest_version": 3,
"version": "1.0",
"permissions": ["storage"],
"content_scripts": [
{
"matches": ["https://twitter.com/*", "https://x.com/*"],
"js": ["content.js"]
}
],
"action": {
"default_popup": "popup.html"
},
"icons": {
"256": "icon.png"
}
} content.js
detectRetweet の文字列は環境で変わる。日本語 UI 向けに「さんがリポスト」を足した例になっている。
(function () {
'use strict';
// ポップアップで保存した値。未設定なら false(リポストは隠さない)
let isRtFilterEnabled = false;
chrome.storage.local.get(['rtFilterEnabled'], (result) => {
isRtFilterEnabled = result.rtFilterEnabled === true;
applyVisibilityAll();
});
// ポップアップでスイッチを変えたあと、開いているタブにすぐ反映させる
chrome.storage.onChanged.addListener((changes) => {
if (changes.rtFilterEnabled) {
isRtFilterEnabled = changes.rtFilterEnabled.newValue;
applyVisibilityAll();
}
});
// article 全体の text でリポスト用ラベルを探す。日本語 UI なら「さんがリポスト」など環境に合わせて増やす(誤爆に注意)
function detectRetweet(article) {
const t = article.textContent || '';
return (
t.includes('Reposted by') ||
t.includes('Retweeted by') ||
t.includes('reposted') ||
t.includes('さんがリポスト')
);
}
function processTweets() {
const articles = document.querySelectorAll('article[data-testid="tweet"]');
articles.forEach((article) => {
// 一度判定したノードは dataset を使い回して表示だけ直す
if (article.dataset.rtChecked === 'true') {
updateArticleVisibility(article);
return;
}
article.dataset.isRetweet = detectRetweet(article);
article.dataset.rtChecked = 'true';
updateArticleVisibility(article);
});
}
function updateArticleVisibility(article) {
const isRt = article.dataset.isRetweet === 'true';
if (isRtFilterEnabled && isRt) {
if (article.style.display !== 'none') article.style.display = 'none';
} else if (article.style.display === 'none') {
article.style.display = '';
}
}
// OFF にしたとき隠していた行をまとめて戻す
function applyVisibilityAll() {
document.querySelectorAll('article[data-testid="tweet"]').forEach((article) => {
updateArticleVisibility(article);
});
}
// 無限スクロールで article が増えたら再スキャン
const observer = new MutationObserver(() => processTweets());
observer.observe(document.body, { childList: true, subtree: true });
})(); popup.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<style>
/* ポップアップは横幅が狭いのでコンパクトに */
body { width: 220px; padding: 12px; font-family: sans-serif; margin: 0; }
.row { display: flex; align-items: center; justify-content: space-between; }
.label { font-size: 14px; font-weight: bold; }
/* チェックボックスを隠して span.slider をスイッチの見た目にする */
.switch { position: relative; display: inline-block; width: 40px; height: 24px; }
.switch input { opacity: 0; width: 0; height: 0; }
.slider {
position: absolute; cursor: pointer; inset: 0;
background: #ccc; transition: 0.2s; border-radius: 24px;
}
.slider::before {
position: absolute; content: ''; height: 16px; width: 16px;
left: 4px; bottom: 4px; background: #fff; transition: 0.2s; border-radius: 50%;
}
input:checked + .slider { background: #1d9bf0; }
input:checked + .slider::before { transform: translateX(16px); }
</style>
</head>
<body>
<!-- id は popup.js から getElementById するので変えない -->
<div class="row">
<span class="label">リポスト非表示</span>
<label class="switch">
<input type="checkbox" id="toggleRtFilter" />
<span class="slider"></span>
</label>
</div>
<script src="popup.js"></script>
</body>
</html> popup.js
document.addEventListener('DOMContentLoaded', () => {
const toggleRtFilter = document.getElementById('toggleRtFilter');
// 前回の状態を復元(未保存なら OFF)
chrome.storage.local.get(['rtFilterEnabled'], (result) => {
toggleRtFilter.checked = result.rtFilterEnabled === true;
});
// 変更は即 storage に書く → content 側の onChanged で拾う
toggleRtFilter.addEventListener('change', () => {
chrome.storage.local.set({ rtFilterEnabled: toggleRtFilter.checked });
});
}); インストール手順
- Chrome で
chrome://extensions/を開く - 「デベロッパーモード」を ON
- 「パッケージ化されていない拡張機能を読み込む」で上記のファイルを配置したフォルダを指定
X を開き直す。拡張アイコンでポップアップを開き、スイッチの ON/OFF で表示を確かめる。
補足・限界
-
本文に
repostedが紛れるとリポスト以外も消える。data-testid頼みにすると X の DOM 変更で壊れやすい。文字列マッチは荒い代わりに短く書ける。 datasetは一度付いたら残る。変なときはタブをリロード。- 関連: タイムラインを画像のみ表示にする、長文ツイートを非表示にする