テーブルを横スクロールできるようにする
https://scrapbox.io/files/62d9493cfe1e610021488f59.png
Scrapboxの仕組みをしっかり理解すればCSSだけでできるようになるかも…?ならないかも
動作確認
Firefox 98.0.1
iOS Firefox Daylight 97.0
既知の不具合
書きかけで幅が一致しない行があると表示が崩れる
内部的には各行個別にスクロールしているので、スクロールバーの長さが一致しないとそれまで
とくに最下行より多くの列を持つ行がある場合
余った列は画面外に消える カーソル以外で到達できない
スクロールバーを高速で動かすと表示が乱れる
大きな支障はないが壮絶に気持ち悪い
タエロ
中クリックでのスクロールはできない
ここまでくるとやりたくなってくるがscrapboxちからが足りない
ポップアップの出る位置が変
スクロールバーから移動幅計算して補正?
これを有効にしたままプロジェクト間移動すると効かなくなる
汚した環境のお掃除とか全然してない
そのへんのuserscriptお作法を全く理解してないため
お作法ガイドを発見するか気合で調査するかしないと治らないやつ
個人プロジェクトでしか使ってないため
解決済みの不具合
なにもわからない 俺たちは雰囲気でUserScriptを書いている
line全体じゃなくてtable-block-rowだけにoverflowを当ててあげればよさげ
そうした
末尾行以外をスクロールすると他の行が追従しない
(スクロールバーがデフォで表示されず、表スワイプで動かす)モバイル端末で問題になる
テーブルどこでもscrollイベント拾う形にすればよさげ
そうした
code:script.js
// Functions
/** @param {HTMLElement} movedElem */
const scrollWholeTable = movedElem => {
const tableName = movedElem.className.match(new RegExp(${CLASS_COMMON}-\\d+))0 for (let tableElem of document.querySelectorAll(.${tableName})) {
if (tableElem === movedElem) { continue }
tableElem.scrollLeft = movedElem.scrollLeft
}
}
/**
* @param {HTMLElement} line
* @return {HTMLElement}
*/
const line2Elem = line => document.getElementById(L${line.id})
// ticking=falseのときだけ再描画を依頼、再描画時にfalseに戻すことでイベントを間引く
/** @return {function(HTMLElement)} */
const tickClosure = () => {
let ticking = false
const scrollWholeTableWithTick = movedElem => e => {
if (!ticking) {
window.requestAnimationFrame(() => {
scrollWholeTable(movedElem)
ticking = false
})
ticking = true
}
}
return scrollWholeTableWithTick
}
// Settings
const CLASS_COMMON = 'scrollTable'
const CLASS_BOTTOM = ${CLASS_COMMON}Bottom
const getTableUniqueName = n => ${CLASS_COMMON}-${n}
// main
const enableTableScroll = () => {
let isTable = false
let nTable = 0
let currentTableName = ''
let currentLineElem
// Scrapbox側のDOM操作(例: cursor-line付与)でtable名のクラスが消える問題対策
const ensureTableName = mut => {
const withPrefix = mut => mut.target.classList.contains(CLASS_COMMON)
if ((mut.type === 'attributes') && !withPrefix(mut)) { enableTableScroll() }
}
const observer = new MutationObserver(mutations => mutations.forEach(ensureTableName))
// テーブルへのクラス名付与
// テーブル最下行を特別扱いしたいので、逆順に読む
for (const line of scrapbox.Page.lines.reverse()) {
if ('tableBlock' in line) {
const tableRowElem = line2Elem(line).querySelector('.table-block-row')
if (!tableRowElem) { isTable = false; continue } // テーブルヘッダ = 逆順での読み終わり行
const addClass = _className => tableRowElem.classList.add(_className)
// テーブル最下行発見
if (!isTable) {
isTable = true
nTable++
currentTableName = getTableUniqueName(nTable)
addClass(CLASS_BOTTOM)
}
// テーブル共通処理
addClass(CLASS_COMMON)
addClass(currentTableName)
// 他を追従させる処理 + DOM変化をobserveしてクラス名消失を防ぐ
const scrollWholeTableWithTick = tickClosure()
tableRowElem.addEventListener('scroll', scrollWholeTableWithTick(tableRowElem))
// onclick?でDOM更新されてクラス名が消えてしまうので、監視して再度付与
observer.observe(tableRowElem, { attributes: true })
}
}
}
enableTableScroll()
scrapbox.on('lines:changed', enableTableScroll)
code:style.css
.scrollTable { overflow-x: scroll; }
.scrollTable:not(.scrollTableBottom)::-webkit-scrollbar { display: none; }
.scrollTable:not(.scrollTableBottom) {
-ms-overflow-style: none;
scrollbar-width: none;
}
UserScript.icon
UserCSS.icon