はじめに
Webサイトでよく見かける、画面右下に浮かぶ「上へ」「下へ」ボタン。レコード数が多い一覧画面や入力項目が多い編集画面では、ページの先頭や末尾にすばやく移動できると便利です。
この記事では、拡張スクリプトと拡張スタイルで画面右下にフローティングボタンを追加する方法を紹介します。
仕組みを整理する
実装のポイントは次のとおりです。
| 項目 | 内容 |
|---|---|
| 実行条件 |
$p.controller === 'items' のときのみ動作 |
| ボタンの配置 |
position: fixed で画面右下に固定。十字パッド型のレイアウト |
| アイコン | Material Symbols(arrow_upward・arrow_downward・arrow_back・arrow_forward) |
| スクロール対象 |
$p.action === 'index' なら Shadow DOM 内 .app-grid-frame + window、その他は window のみ |
| ボタン構成 | 一覧画面は上下左右 4 方向、その他は上下 2 方向。すべて常時表示 |
実装してみよう
拡張スクリプトと拡張スタイルの 2 ファイルで構成します。
| 拡張機能 | 役割 |
|---|---|
| 拡張スタイル | ボタンの見た目と配置を定義 |
| 拡張スクリプト | ボタンの生成・スクロール処理 |
ボタンのスタイル(拡張スタイル)
拡張スタイルとして App_Data/Parameters/ExtendedStyles/ に配置します。
/* --- 十字パッド型コンテナ --- */
.scroll-pad {
position: fixed;
right: 24px;
bottom: 24px;
width: 84px;
height: 84px;
z-index: 900;
}
/* --- 共通ボタンスタイル --- */
.scroll-pad button {
position: absolute;
width: 36px;
height: 36px;
border: none;
border-radius: 50%;
background: #455a64;
color: #fff;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
transition: opacity 0.2s, background 0.2s;
opacity: 0;
pointer-events: none;
padding: 0;
}
.scroll-pad button .material-symbols-outlined {
font-size: 20px;
}
.scroll-pad button.is-visible {
opacity: 1;
pointer-events: auto;
}
.scroll-pad button:hover {
background: #37474f;
}
/* --- 各ボタンの配置(十字型) --- */
.scroll-pad .sp-up {
top: 0;
left: 50%;
transform: translateX(-50%);
}
.scroll-pad .sp-down {
bottom: 0;
left: 50%;
transform: translateX(-50%);
}
.scroll-pad .sp-left {
left: 0;
top: 50%;
transform: translateY(-50%);
}
.scroll-pad .sp-right {
right: 0;
top: 50%;
transform: translateY(-50%);
}
/* --- 中央の装飾円 --- */
.scroll-pad::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 8px;
height: 8px;
border-radius: 50%;
background: rgba(69, 90, 100, 0.3);
pointer-events: none;
}
/* --- window スクロール時はコンパクトに --- */
.scroll-pad.sp-vertical-only {
width: 36px;
height: 84px;
}
.scroll-pad.sp-vertical-only::after {
display: none;
}
.scroll-pad.sp-vertical-only .sp-up {
top: 0;
left: 50%;
transform: translateX(-50%);
}
.scroll-pad.sp-vertical-only .sp-down {
bottom: 0;
left: 50%;
transform: translateX(-50%);
}
ポイントをまとめます。
-
.scroll-padをposition: fixedで画面右下に固定 - 4 つのボタンを
position: absoluteで十字型に配置。中央に小さな装飾円を置き、ゲームパッド風の見た目に -
windowスクロールの画面では.sp-vertical-onlyクラスを付け、上下のみのコンパクトなレイアウトに切り替え - ボタンは初期状態で
opacity: 0かつpointer-events: none(非表示・クリック無効) -
.is-visibleクラスが付くとopacity: 1に変わり表示される -
transitionでふわっと表示・非表示が切り替わる -
z-index: 900でプリザンターの標準UIと干渉しない程度の前面に配置
ボタンの生成とスクロール処理(拡張スクリプト)
拡張スクリプトとして App_Data/Parameters/ExtendedScripts/ に配置します。
$(function () {
// items コントローラーのみ対象
if ($p.controller() !== 'items') return;
var isIndex = $p.action() === 'index';
if (isIndex) {
// カスタム要素のアップグレード完了を待ってから初期化
customElements.whenDefined('grid-container').then(function () {
var gridEl = document.querySelector('grid-container[data-scrollable]');
var frame = gridEl && gridEl.shadowRoot
? gridEl.shadowRoot.querySelector('.app-grid-frame')
: null;
init(frame);
});
} else {
init(null);
}
function init(frame) {
var useGrid = Boolean(frame);
// 十字パッドコンテナを生成
var $pad = $('<div>', {
class: 'scroll-pad' + (useGrid ? '' : ' sp-vertical-only')
});
// ボタン生成ヘルパー
function createBtn(css, icon, title) {
return $('<button>', { type: 'button', class: css, title: title })
.append($('<span>', {
class: 'material-symbols-outlined',
text: icon
}));
}
// 「上へ」
var $up = createBtn('sp-up is-visible', 'arrow_upward', '先頭へ');
$up.on('click', function () {
if (useGrid) frame.scrollTo({ top: 0, behavior: 'smooth' });
window.scrollTo({ top: 0, behavior: 'smooth' });
});
// 「下へ」
var $down = createBtn('sp-down is-visible', 'arrow_downward', '末尾へ');
$down.on('click', function () {
if (useGrid) {
frame.scrollTo({ top: frame.scrollHeight, behavior: 'smooth' });
}
window.scrollTo({
top: document.documentElement.scrollHeight,
behavior: 'smooth'
});
});
// DOM に追加
$pad.append($up).append($down);
// 「左へ」「右へ」(一覧画面のみ)
if (useGrid) {
var $left = createBtn('sp-left is-visible', 'arrow_back', '左端へ');
$left.on('click', function () {
frame.scrollTo({ left: 0, behavior: 'smooth' });
});
var $right = createBtn('sp-right is-visible', 'arrow_forward', '右端へ');
$right.on('click', function () {
frame.scrollTo({ left: frame.scrollWidth, behavior: 'smooth' });
});
$pad.append($left).append($right);
}
$('body').append($pad);
}
});
各処理のポイントを見ていきましょう。
実行条件とモード分岐
$p.controller と $p.action で動作を切り替えます。
| 条件 | 動作 |
|---|---|
$p.controller !== 'items' |
何もしない(管理画面等ではボタン不要) |
$p.action === 'index' |
Shadow DOM 内 .app-grid-frame + window をスクロール→ 上下左右 4 方向 |
その他(edit・new・kamban 等) |
window のみスクロール → 上下 2 方向 |
すべてのボタンは常時表示です。スクロール位置による表示・非表示の切り替えは行わず、いつでもクリックできます。
カスタム要素の初期化待ち
一覧画面($p.action === 'index')では customElements.whenDefined('grid-container') でカスタム要素のアップグレード完了を待ちます。
プリザンターの <grid-container> はカスタム要素(Web Components)として実装されており、$(function(){...})(DOMContentLoaded)の時点ではまだアップグレードが完了していない場合があります。アップグレード前は shadowRoot が null のため、whenDefined() で確実に利用できるタイミングまで待ちます。
Shadow DOM の構造
プリザンターの <grid-container> は data-scrollable 属性があるとき Shadow DOM(mode: 'open')を使い、内部に次の構造を持ちます。
<grid-container data-scrollable="1">
#shadow-root (open)
├─ .app-grid-inner
│ ├─ .app-grid-frame ← 実際のスクロールコンテナ(overflow: auto)
│ │ └─ <slot> ← Light DOM の .grid テーブルが投影
│ └─ .app-scroll-layer
<grid-container> 要素自体には overflow が設定されておらず、scrollTo() や scrollTop を呼んでも効果がありません。Shadow DOM 内の .app-grid-frame が overflow: auto を持つ実際のスクロールコンテナなので、shadowRoot.querySelector('.app-grid-frame') で取得して操作します。
Material Symbols の arrow_upward 等のアイコンを使います。プリザンターでは Google Material Symbols がすでに読み込まれているので、追加のフォント読み込みは不要です。
スクロール処理
一覧画面では Shadow DOM 内の .app-grid-frame と window の両方、その他の画面では window のみをスクロールします。左右ボタンは frame.scrollLeft / frame.scrollWidth を使って水平方向にスクロールします。どの方向も behavior: 'smooth' でスムーズに動作します。
カスタマイズ
ボタンの見た目や動作は CSS と JavaScript の値を変えるだけで調整できます。
ボタンの色を変える
.scroll-pad button {
- background: #455a64;
+ background: #333;
}
.scroll-pad button:hover {
- background: #37474f;
+ background: #555;
}
ボタンの位置を変える
.scroll-pad {
right: 24px;
- bottom: 24px;
+ bottom: 80px;
}
表示例
一覧画面
それ以外(カレンダーやカンバンなど)
まとめ
プリザンターの画面に十字パッド型のスクロールボタンを拡張スクリプトと拡張スタイルだけで追加しました。
-
$p.controller === 'items'のときのみ動作 -
$p.action === 'index'なら Shadow DOM 内.app-grid-frameをスクロールし上下左右 4 方向、その他はwindowスクロールで上下 2 方向 -
customElements.whenDefined()でカスタム要素の初期化完了を待ち、Shadow DOM を確実に取得 - ボタンはすべて常時表示。スクロール位置による出し分けは行わない
-
position: fixedで画面右下に十字パッドを固定配置 - Material Symbols の矢印アイコンを使用
- 拡張スタイルと拡張スクリプトの 2 ファイルだけで動作

