pixivにメニューバーを追加するUserScript(Chrome)

pixivにメニューを追加するUserScript

最近pixivのUI改悪で「作品管理」や「ブックマーク」のメニューが格納されて右上のアイコンをクリックしないと表示されなくなってアクセスが悪くなりました。

pixiv右上部のアイコンをクリックしないとよく使うメニューが表示されない
よく使うメニューが格納された…

という事でヘッダーにメニューを追加するためのUserScriptを作成しました。

自分が使う分にはこれで十分かな…という簡易的な物ですが、ご自由にお使いください。

メニュー追加
pixivに簡易メニューを追加
こんな感じに追加されます

追加される位置が最上部に変わりました(ver.1.1~)

スポンサーリンク

pixivにメニューを追加するUserScript

シェア用: notepad

// ==UserScript==
// @name        pixivにメニューを追加
// @namespace   http://web.monogusa-note.com/add-menu-to-pixiv
// @version     1.1
// @description pixivに簡易的なメニューを追加します。
// @author      草村
// @match       https://www.pixiv.net/*
// @grant       none
// ==/UserScript==

( () => {
	const user_id = '';
	const menu = {
//		"ホーム":["/"],
		"作品投稿":["/upload.php", {
			"イラスト":["/upload.php"],
//			"うごイラ":["/ugoira_upload.php"],
			"マンガ":["/upload.php?type=manga"],
//			"小説":["/novel/upload.php"],
		}],
		"作品管理":["/manage/illusts"],
//		"フォロー":["/users/[id]/following"],
//		"フォロワー":["/users/[id]/followers"],
		"フォロー中":["/bookmark.php?type=user"],
		"ブックマーク":["/bookmark.php"],
//		"閲覧履歴":["/history.php"],
//		"しおり":["/novel/marker_all.php"],
//		"設定":["/setting_user.php"],
//		"イラスト・漫画":["/"],
//		"小説":["/novel"],
//		"フォロー新着":["/bookmark_new_illust.php"],
//		"ディスカバリー":["/discovery"],
//		"フィード":["/stacc?mode=unify"],
//		"ランキング":["/ranking.php"],
//		"みんなの新着":["/new_illust.php"],
//		"コンテスト":["/contest"],
	};
	const css = `
		#add-menubar {
			background: #fff;
			font-size: 14px;
			padding: 5px 0;
			animation: add-menu-feedin .5s backwards;
		}
		#add-menubar>ul {
			width: 970px;
			margin: 0 auto;
			padding: 0;
		}
		#add-menubar li {
			position: relative;
			list-style: none;
		}
		#add-menubar>ul>li {
			display: inline-block;
		}
		#add-menubar>ul>li+li {
			border-left: 1px solid #ccc;
		}
		#add-menubar li ul {
			min-width: 100px;
			background: #fff;
			position: absolute;
			z-index: 10;
			padding: 10px;
			opacity: 0;
			box-shadow: 2px 5px 5px rgba(0,0,0,.2);
			transition: .3s;
			transform: translateY(-10px);
			pointer-events: none;
		}
		#add-menubar li:hover ul {
			opacity: 1;
			transform: translateY(0);
			pointer-events: auto;
		}
		#add-menubar li.none a {
			pointer-events: none;
		}
		#add-menubar li.none:before,
		#add-menubar li.none:after {
			position: absolute;
			bottom: 100%;
			left: 50%;
			transform: translate(-50%, 5px);
			opacity: 0;
			transition: .3s;
			pointer-events: none;
		}
		#add-menubar li.none:before {
			content:"ユーザーIDを設定してください";
			color: #fff;
			background: #333;
			border-radius: 3px;
			padding: 5px 10px;
			white-space: nowrap;
		}
		#add-menubar li.none:after {
			content: "";
			margin: 0 0 -10px;
			border-width: 10px 6px 0;
			border-style: solid;
			border-color: #333 transparent;
		}

		#add-menubar li.none:hover:before,
		#add-menubar li.none:hover:after {
			transform: translate(-50%, -5px);
			opacity: 1;
		}
		#add-menubar a {
			display: block;
			padding: 5px 10px;
		}
		#add-menubar a:hover {
			text-decoration: underline;
		}
		#add-menubar .sub:before,
		#add-menubar .sub:after {
			content: "";
			width: 6px;
			border-bottom: 2px solid #ccc;
			display: inline-block;
			transform: rotate(-45deg);
			transform-origin: left bottom;
		}
		#add-menubar .sub:before {
			transform: rotate(45deg);
			transform-origin: right bottom;
			margin-left: .3em;
		}
		@keyframes add-menu-feedin {
			0% { opacity: 0; transform: translateY(-10px); }
			100% { opacity: 1; transform: translateY(0); }
		}
	`;
	const createlist = ( obj ) => {
		let list = '';
		Object.keys(obj).forEach(key => {
			let url = obj[key][0], sub = obj[key][1];
			const has_sub = sub && Object.keys(sub).length;
			const test_id = /\[id\]/.test(url);
			url = url.replace(/\[id\]/, user_id);
			list += '<li'+ ( !user_id && test_id? ' class="none"' : '' ) +'><a href="'+url+'">'+ key + ( has_sub? '<span class="sub"></span>' : '' ) +'</a>';
			if( has_sub ) {
				list += createlist( sub );
			}
			list += '</li>';
		});
		return '<ul>'+ list + '</ul>';
	}
	const navi = document.createElement('div');
	navi.id = 'add-menubar';
	navi.innerHTML = createlist( menu );
	let insert = document.body;
	insert.prepend(navi);

	const style = document.createElement('style');
	style.textContent = css;
	document.getElementsByTagName('head')[0].appendChild(style);
})();

メニューが挿入されない・挿入位置がおかしい場合ブラウザもしくはpixivの仕様にアップデートが入った可能性があります。

2021-06-25(ver.1.1):初回表示時に実行されない現象が起きるためコードを変更しました。表示位置が最上部に変更されました。
以下古いコード(初回時に起動せず、リロードすれば動く 謎)

// ==UserScript==
// @name        pixivにメニューを追加
// @namespace   http://web.monogusa-note.com/add-menu-to-pixiv
// @version     1.0.1
// @description pixivに簡易的なメニューを追加します。
// @author      草村
// @match       https://www.pixiv.net/*
// @grant       none
// ==/UserScript==

window.addEventListener( 'load', () => {
	const user_id = '';
	const menu = {
//		"ホーム":["/"],
		"作品投稿":["/upload.php", {
			"イラスト":["/upload.php"],
//			"うごイラ":["/ugoira_upload.php"],
			"マンガ":["/upload.php?type=manga"],
//			"小説":["/novel/upload.php"],
		}],
		"作品管理":["/manage/illusts"],
//		"フォロー":["/users/[id]/following"],
//		"フォロワー":["/users/[id]/followers"],
		"フォロー中":["/bookmark.php?type=user"],
		"ブックマーク":["/bookmark.php"],
//		"閲覧履歴":["/history.php"],
//		"しおり":["/novel/marker_all.php"],
//		"設定":["/setting_user.php"],
//		"イラスト・漫画":["/"],
//		"小説":["/novel"],
//		"フォロー新着":["/bookmark_new_illust.php"],
//		"ディスカバリー":["/discovery"],
//		"フィード":["/stacc?mode=unify"],
//		"ランキング":["/ranking.php"],
//		"みんなの新着":["/new_illust.php"],
//		"コンテスト":["/contest"],
	};
	const css = `
		#add-menubar {
			background: #fff;
			font-size: 14px;
			padding: 5px 0;
			animation: add-menu-feedin .5s backwards;
		}
		#add-menubar>ul {
			width: 970px;
			margin: 0 auto;
			padding: 0;
		}
		#add-menubar li {
			position: relative;
			list-style: none;
		}
		#add-menubar>ul>li {
			display: inline-block;
		}
		#add-menubar>ul>li+li {
			border-left: 1px solid #ccc;
		}
		#add-menubar li ul {
			min-width: 100px;
			background: #fff;
			position: absolute;
			z-index: 10;
			padding: 10px;
			opacity: 0;
			box-shadow: 2px 5px 5px rgba(0,0,0,.2);
			transition: .3s;
			transform: translateY(-10px);
			pointer-events: none;
		}
		#add-menubar li:hover ul {
			opacity: 1;
			transform: translateY(0);
			pointer-events: auto;
		}
		#add-menubar li.none a {
			pointer-events: none;
		}
		#add-menubar li.none:before,
		#add-menubar li.none:after {
			position: absolute;
			bottom: 100%;
			left: 50%;
			transform: translate(-50%, 5px);
			opacity: 0;
			transition: .3s;
			pointer-events: none;
		}
		#add-menubar li.none:before {
			content:"ユーザーIDを設定してください";
			color: #fff;
			background: #333;
			border-radius: 3px;
			padding: 5px 10px;
			white-space: nowrap;
		}
		#add-menubar li.none:after {
			content: "";
			margin: 0 0 -10px;
			border-width: 10px 6px 0;
			border-style: solid;
			border-color: #333 transparent;
		}

		#add-menubar li.none:hover:before,
		#add-menubar li.none:hover:after {
			transform: translate(-50%, -5px);
			opacity: 1;
		}
		#add-menubar a {
			display: block;
			padding: 5px 10px;
		}
		#add-menubar a:hover {
			text-decoration: underline;
		}
		#add-menubar .sub:before,
		#add-menubar .sub:after {
			content: "";
			width: 6px;
			border-bottom: 2px solid #ccc;
			display: inline-block;
			transform: rotate(-45deg);
			transform-origin: left bottom;
		}
		#add-menubar .sub:before {
			transform: rotate(45deg);
			transform-origin: right bottom;
			margin-left: .3em;
		}
		@keyframes add-menu-feedin {
			0% { opacity: 0; transform: translateY(-10px); }
			100% { opacity: 1; transform: translateY(0); }
		}
	`;
	const createlist = ( obj ) => {
		let list = '';
		Object.keys(obj).forEach(key => {
			let url = obj[key][0], sub = obj[key][1];
			const has_sub = sub && Object.keys(sub).length;
			const test_id = /\[id\]/.test(url);
			url = url.replace(/\[id\]/, user_id);
			list += '<li'+ ( !user_id && test_id? ' class="none"' : '' ) +'><a href="'+url+'">'+ key + ( has_sub? '<span class="sub"></span>' : '' ) +'</a>';
			if( has_sub ) {
				list += createlist( sub );
			}
			list += '</li>';
		});
		return '<ul>'+ list + '</ul>';
	}
	const navi = document.createElement('div');
	navi.id = 'add-menubar';
	navi.innerHTML = createlist( menu );
	let insert = document.getElementById('js-mount-point-header') || document.getElementById('root').firstElementChild;
	insert.parentNode.insertBefore(navi, insert.nextSibling);

	const style = document.createElement('style');
	style.textContent = css;
	document.getElementsByTagName('head')[0].appendChild(style);
});

2020-11-11(ver.1.0.1): CSSにbr要素の改行が出力されていたので修正しました。※現時点で修正前でもCSSは適用されるので特に影響はないです。

style.textContent = css;
// 変更前: style.innerText = css;

インストールと使い方

UserScriptはChromeの場合Tampermonkeyという拡張機能、Firefoxの場合Greasemonkeyというアドオンをインストールすると使用できます。
TampermonkeyはSafariやEdge版もあるみたいです。

ここではWindows版ChromeでのUserScriptの追加方法を解説します。

Tampermonkeyをインストールする

chromeウェブストアにあるTampermonkeyで「Chromeに追加」をクリック

ダッシュボードを開く
  1. Chromeに追加した「Tampermonkeyのアイコン」をクリック
  2. 「ダッシュボード」に移動します
Tampermonkeyのアイコンをクリックしてダッシュボードを開く
コードをコピペする
  1. 「+」タブをクリックすると<新規スクリプト>を追加できます。
  2. デフォルトでサンプルコードが記入されているのでそれを消去。
  3. pixivにメニューを追加するコードをコピペします。
+をクリック、新規スクリプトにコピペする
保存する
  1. 左上の「ファイル」をクリック
  2. 「保存」でコードを保存します。

もしくはCtrl+Sキーで保存できます。

コピペしたUserScriptを保存する
追加された事を確認

「インストール済みUserScript」タブに移動して「Pixivにメニューを追加」が表示されていればOKです。

不具合が起きた・不要になった場合には削除またはスクリプトを無効にしてください。

挿入位置について気になる点がある場合追記を確認してみてください

UserScriptとは?

ブラウザでユーザー側が用意したスクリプトを実行する為の仕組み、主にはそのスクリプトを指すと思います。中身は JavaScript です。

古く新しい!でも便利な UserScript を Chrome で使ってみよう!

この様に自由に用意できて様々な事が出来るので、配布されているスクリプトを利用する場合どのような動作をしているのか注意し信用できる物のみ利用しましょう。

Tampermonkeyをpixiv以外で無効化

むやみに拡張機能を有効にするのも不安なのでpixiv以外はアクセスを制限すればほんのちょっと安心です。

  1. https://www.pixiv.net/を開く
  2. 「Tampermonkeyのアイコン」を右クリック
  3. 「サイトデータの読み取りと変更を行います」にカーソルを合わせる
  4. 「pixiv.net」をクリック

以上の手順でTampermonkeyはpixiv.netでのみ有効状態になります。

カスタマイズする

メニューを追加・削除する

先頭に//がある行はコメントアウトされています。
//を削除すればメニューに表示されます。逆に非表示にしたいメニューには//を追加すればOKです。

例えば以下だとフォローは非表示になっています。

		"作品管理":["/manage/illusts"],
//		"フォロー":["/users/[id]/following"],

好きなメニューを追加する

menuは以下のような構造になっています。
好きなメニューを追加する時はこれにならってmenu={~}の中に追記すればOKです。

const menu = {
	"メニュー名":["メニューURL"],
	"メニュー名":["メニューURL", {"サブメニュー名":["サブメニューURL"]}],
}

URL内では[id]はユーザーIDに置換されます(ユーザーIDは手動で設定してください)
例えばユーザーIDが012345の場合、/users/[id]/following/users/012345/followingになります。

ユーザーIDを設定する

ユーザーIDが含まれるURLは[id]の部分をユーザーIDに置換するようになっています。
URLに[id]を含んだメニューを表示する時は手動で設定してください。

const user_id = 'ここにユーザーID(数字)を記入する'; // '012345' ←こんな感じ

※ユーザーIDは自分のプロフィールのURLでわかりますhttps://www.pixiv.net/users/ここの数字

デザインを変更する

const css = `~`のCSSを変更すればデザインをカスタマイズできます。
出力されるHTMLは次のような構造です。

<div id="add-menubar">
  <ul>
    <li><a href="URL">メニュー名<span class="sub"></span></a>
      <ul>
        <li><a href="URL">サブメニュー名</a></li>
        <li><a href="URL">サブメニュー名</a></li>
      </ul>
    </li>
    <li><a href="URL">メニュー名</a></li>
    <li><a href="URL">メニュー名</a></li>
  </ul>
</div>

ダークテーマで使用する場合

コード内のbackground:#fff;の部分を#1f1f1fに変更すると背景が黒系になるかと思います。

background=背景色、#~はカラーコードを意味します。

追記(2023-11-27):部分的にダークテーマが適用されていないページがあり、その場合にリンクが見えづらい事があるようなので、background: var(--charcoal-background1, #fff);とすると、切り替わりに対応できるかと思います。

除外(実行しない)ページを指定する

URLがhttps://www.pixiv.net/~のページでメニューが追加されますが(@matchで指定)
もし一部のページで除外したい場合は@excludeで除外できます。以下のように@matchの下あたりに追記すればOKです。

例えば以下だとイラストページでは実行しないようになります。

// @match       https://www.pixiv.net/*
// @exclude     https://www.pixiv.net/artworks/*

*はワイルドカードです。

注意点

pixivでは頻繁にHTMLの構造やclass名の変更が行われるようです。
もしかするとサーバに負荷が掛かるような改変への対策かもしれません。

ここで紹介しているメニュー追加はサーバ負荷になるような処理はないので大丈夫だと思いますが…。
ただ、構造の変更等で適用されなくなる場合がありますので予めご了承ください
もし対応できそうなら追記していきます。

メニューが追加される位置※ver.1.0.1未満

上部にお知らせ等が表示されているとメニューが追加される位置がずれます。(サイト名より上部に追加されたりする)

例えばプライバシーポリシーに関するお知らせは「OK」を押すとお知らせが消えるので、再度ページを表示するとメニューが意図した位置に表示されるかと思います。

上記と関係なくメニューが挿入されない・挿入位置がおかしい場合「追記」をご確認ください。

Firefoxで使う時

イラストページ等でメニューが表示されないかもしれません。
多分スクリプトの実行順の問題だと思うんですが知識不足によりスマートな解決法がわからず…とりあえず以下の部分を

insert.parentNode.insertBefore(navi, insert.nextSibling);

こんな感じで少し遅らせて実行すれば一応追加されます。

if( !insert ) {
	timeoutID = window.setTimeout( ()=> {
		insert = document.getElementById('root').firstElementChild;
		insert.parentNode.insertBefore(navi, insert.nextSibling);
	}, 1000);
} else {
	insert.parentNode.insertBefore(navi, insert.nextSibling);
}
タイトルとURLをコピーしました