このブログは更新を終了しました。移転先はこちらです。

2023-02-23

NTA-DIY:1ヶ月目⑧~ScrapboxのUserScriptを作ってみる~

 ブックマークレットが作れるようになったので、今度はScrapboxのUserScriptに挑戦することにしました。


 ScrapboxのUserScriptは私がJavaScriptを習得したいと思った動機のひとつです。是非とも作りたい何かがあったわけではありませんが、コーディングできる人が自由にカスタマイズして便利にしている一方、そのコードをすっかり公開までしてくれているのに自分はそれを参考にすることもできないのが少し悔しかったのです。

 一応作ろうと思えば作れるようになった今となっては、逆に「別に作ろうとしなくても構わないな」という結論に至っているのですが、やりたくてもやれないのとやれるけどやらないのとでは心持ちが違いますね。


 さて、UserScriptでできることというのは色々あります。その可能性の広さがどのくらいのものか私はまだ全然わかりませんが、私が思いつくだけでも方向性は様々です。

 当初は処理の実行タイミングのコントロール(つまりイベントの設定)がよくわからなかったので、まずPage Menuのボタンを作る形を試しました。自分でイベントリスナーやsetInterval、あるいはUserScript Eventsを設定できるようになれば自由にイベントを発火させられることになりますが、その域に行くのは当分先のことです(私の場合は)。

 Page Menuのボタンクリックで動かすとして、そこでやれるのは例えば次のようなことです。

  • 予め用意した文字列などをalertで表示する
  • どれかのページのコードブロックの内容を取得して表示する
  • 自分のプロジェクトのページデータを取得し特定の条件に合うページを抽出して何かする
  • DOM操作でclassを付け外ししてCSSを変更する
  • DOM操作でstyle要素を付け外ししてCSSを変更する

 他にも数多の可能性があります。今挙げたのは1ヶ月目の時点で私が実際にやってみたことです。つまり簡単な処理です。

 Pege Menuの作り方の細かい説明はここではしませんが、一番下にいくつかリンクを貼ったので必要な方はご参照ください。


 まずPage Menuのメニュー追加の書き方を知るためにalertを表示するだけのコードを書きました。

// 変数が他に影響しないように即時関数内で実行する
(function () {
// メニューとタイトルを決めて変数に入れておく
const menuTitle = "備忘録";
// Itemにするためのオブジェクトを用意して配列にする
const list = [
{ title: '備忘録①', detail: 'alertで表示したい内容をここに書く' },
{ title: '備忘録②', detail: '改行したいときは\nこうする' },
]
// メニューを作る
scrapbox.PageMenu.addMenu({
title: menuTitle, // タイトルが「備忘録」になる
image: 'https://i.gyazo.com/cf195383cbce26ac8697af2ced43343d.png', // メモの絵文字画像
});
// listの中身それぞれについて繰り返し処理
for (const obj of list) {
// 「備忘録」メニューの中にItemを作る
scrapbox.PageMenu(menuTitle).addItem({
title: obj.title,
onClick: () => alert(obj.detail), // Itemをクリックするとalertで内容を表示
})
}
})();
view raw UserScript01.js hosted with ❤ by GitHub

 実際には、当時ブログ記事をScrapboxで書くことがあったので、記事を書く際の組み立てのヒントを表示するようにして使っていました。


 次は特定のページのコードブロックの内容の取得です。この処理には「FetchAPIを使ってScrapboxAPIを叩く」という工程が必要です。当時はAPIも非同期処理もよくわからなかったので見様見真似でやりました。

(function () {
// 取得したいコードブロックのURL
const url = '/api/code/noratetsu/●PageMenu:任意のコードブロックの中身をalertで表示する/sample.txt';
scrapbox.PageMenu.addMenu({
title: 'コードブロック表示',
image: 'https://i.gyazo.com/7057219f5b20ca8afd122945b72453d3.png', // Scrapboxアイコン
onClick: () => {
// fetchで指定した場所のデータを取得し、thenの中で任意の処理を実行
fetch(url)
.then(response => response.text()) // データから文字列を取り出す
.then(text => {
alert(text); // 指定したコードブロックの中身のテキストを表示する
});
},
});
})();
view raw UserScript02.js hosted with ❤ by GitHub


 今度はScrapboxAPIの別のプロパティを使い、自分のプロジェクト内のページの中から特定のキーワードをタイトルに含むページを抽出して、リンクを一覧にして今開いているページに追記することを試みました。

(function () {
scrapbox.PageMenu.addMenu({
title: 'タイトル抽出',
image: 'https://i.gyazo.com/7057219f5b20ca8afd122945b72453d3.png', // Scrapboxアイコン
onClick: () => {
const keyword = prompt('ピックアップする文字列を入力してください。');
if (!keyword) return;
// 現在のプロジェクトの全ページについて、指定した文字列をタイトルに含むか判定する
let body = `「${keyword}」を含むページ\n`;
scrapbox.Project.pages.forEach(data => {
if (!data.exists) return; // 空リンクはスキップ
if (!data.title.includes(keyword)) return;
body += `\t[${data.title}]\n`;
})
// 追記内容を現在のページのURLに付与して別ウインドウで開く
const write = window.open(`https://scrapbox.io/${scrapbox.Project.name}/${scrapbox.Page.title}?body=${encodeURIComponent(body)}`);
setTimeout(() => write.close(), 15000); // 15秒後に閉じる(長過ぎるようなら短くする)
},
})
})();
view raw UserScript03.js hosted with ❤ by GitHub


 DOM操作によってCSSをコントロールすることもできます。まずUserCSSに予めスタイルを書いておいてclassで切り替える方法です。daiizさんのコードを参考にさせていただきました(新書モード - daiiz)。例えばコードブロックのフォントを切り替えるとしたらこう書けます。

UserCSS

.changefont .line code {
font-family: "UD デジタル 教科書体 NK-B", "MotoyaLMaru W3 mono", "Kosugi Maru", "Roboto",Helvetica,Arial,"Hiragino Sans",sans-serif;
color: #220;
}

UserScript

(function () {
scrapbox.PageMenu.addMenu({
title: 'フォント変更',
image: 'https://i.gyazo.com/7057219f5b20ca8afd122945b72453d3.png', // Scrapboxアイコン
onClick: () => {
const className = 'changefont'; // 適当なクラス名を用意する
const editor = document.getElementById('editor'); // 本文部分の親要素
// クラスが既に付与されていれば外す、付与されていなければ付与する
editor.classList.contains(className) ? editor.classList.remove(className) : editor.classList.add(className);
},
})
})();
view raw UserScript04.js hosted with ❤ by GitHub


 UserCSSは使わずにstyle要素の追加と除去で切り替える方法もあります。こちらはページの背景をリーガルパッド風にするUserScript - Scrapbox研究会を参考にしています。例えば以前作った、関連リンクを左列に持ってくるUserCSSのオン・オフを切り替えるとしたらこうなります。

(function (){
scrapbox.PageMenu.addMenu({
title: '関連リンクを左に',
image: 'https://i.gyazo.com/7057219f5b20ca8afd122945b72453d3.png', // Scrapboxアイコン
onClick: () => {
const url = '/api/code/noratetsu/_CSS_related-page-list_left/style.css'; // CSSが書いてあるコードブロック
const styleId = 'related-left'; // 適当なidを設定する
const elm = document.getElementById(styleId);
if(elm){ // 既に設定されている時
elm.remove(); // 除去する
}else{ // 設定されていない時
fetch(url) // 指定したコードブロックの中身を取得
.then(response => response.text())
.then(text => {
// style要素にCSSの記述を入れてbody要素に追加する
const style = document.createElement('style');
style.id = styleId;
style.append(document.createTextNode(text));
document.body.append(style);
});
};
},
})
})();
view raw UserScript05.js hosted with ❤ by GitHub


 Scrapboxの公開プロジェクトには当時作ったコードが色々置いてありますが、今となってはあまりよろしくない書き方があちこちにあります(直すのも面倒で…)。なので参考にしてくださいとはちょっと言えませんが、自分が振り返る分には「手探りで頑張っていたなあ」と懐かしく思い出されます。なお今回記事内に載せたコードは当時のものを適宜直しています。


 まだ単純なことしかできていないわけですが、「自分でメニューを増やして、自分が作ったコードを何かしら実行できる」という体験をしたことにより、UserScriptへの漠然とした憧れはこれで大方解放されました。特にCSSの切り替えについては、UserCSSで設定した状態に固定されてしまうことに不都合を感じていたので、それを解消できたことでかなりの達成感がありました。

 ずぶの素人がUserScriptをJavaScriptの勉強のための実験場として使うのはちょっと危なく感じられたこともあり、UserScriptについてはそこそこにして、ここからいよいよHTML・CSS・JavaScriptの三点セットでツールの自作に挑んでいくことになります。


参考

当時の咀嚼



このシリーズ記事の概要はこちら→ノートテイキングアプリDIY体験記