JetTheme ボトムに飛ぶボタンを追加
<!-- markdown-mode-on -->
<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1SCQmdE2aca4GsQyJIxiqacb-r3rhaTnGGiei0QQUVHx4KdZf6MUfF2SuaBEAl7Ix-A57MHy4Z1Hx4Hy0lBTwSE4LHIRk5rpCyBSRi9TInALixZOjBQJjqNFmF4Q-AwUanELn4SKNBnSKVH4c5MjezYbHRqAqUh1zDfmC6FP6xEW8CZI/s1600/Gemini_256_Image_8t20c58t20c58t20.png" style="display:none;">
## **概要**
JetTheme にはトップに戻るボタンはあっても、ボトムに飛ぶボタンがないので新設しました。
方策やデザインはJetThemeオリジナルを踏襲しています。すなわち、**オリジナルのコードにボトムに飛ぶ機能のコードを追加する**カスタマイズを行います。
<a name="more"></a>
<style>
.highlight {
background-color: #4a4a4a !important; /* よりグレーに近い色 */
padding: 2px 4px;
border-radius: 4px;
}
code {
white-space: pre-wrap !important;
}
</style>
---
## カスタマイズ内容
JetThemeのデザインXMLを調べると、トップに戻るボタンはHTML、CSS、JavaScript で構成されていました。したがって、ボトムに飛ぶボタン用に同様の処理をそれぞれの場所に追加します。
### 1. HTMLの役割:ボタンを「置く」
* **今回の改造内容:**
* 既存の「トップへジャンプ」ボタン(`#back-to-top`)に加えて、新しい「ボトムへジャンプ」ボタン(`#back-to-bottom`)の**HTML要素を追加**しました。
* これらのボタンが**縦に並ぶ**ように、それぞれのHTML要素の配置情報(`style='right:20px;bottom:70px'` や `style='right:20px;bottom:20px'`)を変更しました。
* 初期状態では画面に表示されないよう、Bootstrapの`d-none`クラスを付けています。
### 2. CSSの役割:ボタンの「見た目を整える」
* **今回の改造内容:**
* 「ボトムへジャンプ」ボタンのアイコン(元は上向き矢印)を**下向きにする**ため、CSSでアイコンを**180度回転**させる指示を追加しました。これにより、視覚的に下へのジャンプであることがわかります。
### 3. JavaScriptの役割:ボタンを「表示・非表示にしたり、制御する」
* **今回の改造内容:**
* `jtCallback()`関数内に、以下の二つの主要な「動き」に関する指示を追加しました。
1. **表示・非表示の制御:** ユーザーがページをスクロールした距離に応じて、必要に応じてボタンを表示し、不要な場合は非表示にします。例えば、ページ上部にいるときは両方のボタンを隠し、真ん中あたりにいるときは両方を表示するといった制御です。これはBootstrapの`d-none`クラスをJavaScriptで付け外しすることで実現します。
2. **ジャンプ機能の実行:**
* 「トップへジャンプ」ボタンがクリックされたら、ページを**最上部へ「一瞬で」移動**させます。
* 「ボトムへジャンプ」ボタンがクリックされたら、ページを**最下部へ「一瞬で」移動**させます。
* これらの動作は、テーマが持つ既存のスクリプトによる上書きを防ぐため、ページの<b>全要素読み込み完了後(`window.onload`)に実行</b>されるように設定しています。
---
## HTML
`<div class='position-fixed d-none' id='back-to-top' style='right:20px;bottom:20px'>` を探します。
### 変更前
<pre style="white-space: pre-wrap;"><code class="language-html">
<div class='position-fixed d-none' id='back-to-top' style='right:20px;bottom:20px'><a aria-label='Back to Top' class='btn btn-sm jt-btn-light rounded-circle jt-icon-center' href='#back-to-top' onclick='window.scroll({top:0,left: 0,behavior:&apos;smooth&apos;});'><svg aria-hidden='true' class='jt-icon' height='1em' width='1em'><use xlink:href='#i-arrow-t'/></svg></a></div>
</code></pre>
### 変更後
<pre style="white-space: pre-wrap;"><code class="language-html">
<div class='position-fixed d-none' id='back-to-top' style='right:20px;bottom:<span class="highlight">70</span>px'> <a aria-label='Back to Top' class='btn btn-sm jt-btn-light rounded-circle jt-icon-center' href='#back-to-top' onclick='window.scroll({top:0,left: 0,behavior:&apos;smooth&apos;});'>
<svg aria-hidden='true' class='jt-icon' height='1em' width='1em'><use xlink:href='#i-arrow-t'/></svg></a></div>
<span class="highlight"><div class='position-fixed d-none' id='back-to-bottom' style='right:20px;bottom:20px'> <a aria-label='Scroll to Bottom' class='btn btn-sm jt-btn-light rounded-circle jt-icon-center' href='#bottom-of-page'>
<svg aria-hidden='true' class='jt-icon' height='1em' width='1em'><use xlink:href='#i-arrow-t'/></svg></a></div></span>
</code></pre>
## CSS
### 追加
```css
#back-to-bottom .jt-icon {
transform: rotate(180deg);
transition: transform 0.3s ease-in-out; /* スムーズに回転させるためのアニメーション */
}
```
## JavaScript
`function jtCallback(){`を探します。
### 変更前
<pre style="white-space: pre-wrap;"><code class="language-javascript">
function jtCallback(){
/*Your Script is here to maintain performance.*/
// the example below if you use url.
// Defer.css('your_css_url','your-style-id',100);
// Defer.js('your_script_url','your-script-id',100);
}
</code></pre>
### 変更後
<pre style="white-space: pre-wrap;"><code class="language-javascript">
function jtCallback(){
/*Your Script is here to maintain performance.*/
// ボタン要素の取得
var backToTopButtonDiv = document.getElementById('back-to-top');
var backToBottomButtonDiv = document.getElementById('back-to-bottom');
// ページ上部へジャンプするリンク
var backToTopLink = backToTopButtonDiv ? backToTopButtonDiv.querySelector('a') : null;
// ページ下部へジャンプするリンク
var backToBottomLink = backToBottomButtonDiv ? backToBottomButtonDiv.querySelector('a') : null;
// --- スクロールイベントによる表示/非表示の制御 ---
// スクロールイベントリスナーは、いずれかのボタンが存在する場合のみ設定
if (backToTopButtonDiv || backToBottomButtonDiv) {
window.addEventListener('scroll', function() {
// ページの上部から閾値以上スクロールされたか
var scrollThreshold = 200; // この値は必要に応じて調整してください
var scrolledDownEnough = window.scrollY > scrollThreshold;
// ページの最下部から閾値以上離れているか
// (window.innerHeight + window.scrollY) は現在表示されている画面の最下部のY座標
// document.body.scrollHeight はページの全体の高さ
var notAtBottom = (window.innerHeight + window.scrollY) &lt; (document.body.scrollHeight - scrollThreshold);
// 文頭へジャンプボタンの表示/非表示
if (backToTopButtonDiv) {
if (scrolledDownEnough) {
backToTopButtonDiv.classList.remove('d-none'); // 表示
} else {
backToTopButtonDiv.classList.add('d-none'); // 非表示
}
}
// 文末へジャンプボタンの表示/非表示
if (backToBottomButtonDiv) {
// ページの途中(上から閾値より下、かつ下から閾値より上)にいる場合のみ表示
if (scrolledDownEnough && notAtBottom) {
backToBottomButtonDiv.classList.remove('d-none'); // 表示
} else {
backToBottomButtonDiv.classList.add('d-none'); // 非表示
}
}
});
}
// --- クリックイベントによるジャンプ制御 ---
// 文頭へジャンプボタンのクリックイベント(既存のonclick属性を上書きして「一瞬でジャンプ」にする)
if (backToTopLink) {
backToTopLink.addEventListener('click', function(e) {
e.preventDefault(); // デフォルトのリンク動作をキャンセル
window.scrollTo(0, 0); // ページトップへ一瞬でスクロール
});
}
// 文末へジャンプボタンのクリックイベント(新しく追加して「一瞬でジャンプ」にする)
if (backToBottomLink) {
backToBottomLink.addEventListener('click', function(e) {
e.preventDefault(); // デフォルトのリンク動作をキャンセル
window.scrollTo(0, document.body.scrollHeight); // ページ最下部へ一瞬でスクロール
});
}
// the example below if you use url.
// Defer.css('your_css_url','your-style-id',100);
// Defer.js('your_script_url','your-script-id',100);
}
</code></pre>
## 文末へジャンプか、ボトムへジャンプか
前項のスクリプトは**文末へジャンプ**とコメントしていますが、正しくは**ボトムへジャンプ**です。フッターの下までジャンプします。
記事本文の最後までのジャンプに変更するならば、`// 文末へジャンプボタンのクリックイベント(新しく追加して「一瞬でジャンプ」にする)`以降を次のコードに書き換えします。
`var bottomOffset` はジャンプ先の記事文末と画面の下端を調整するための変数です。ジャンプ動作に私では原因究明できないバラツキがあって、妥協の値にしています。
```javascript
// 文頭へジャンプボタンのクリックイベント (共通)
if (backToTopLink) {
backToTopLink.removeAttribute('onclick'); // これは共通部分にはありませんでしたが、以前の議論で追加されています
backToTopLink.addEventListener('click', function(e) {
e.preventDefault();
window.scrollTo(0, 0);
});
}
// === 注目点:文末へジャンプボタンのクリックイベントのロジック ===
if (backToBottomLink) {
backToBottomLink.addEventListener('click', function(e) {
e.preventDefault();
var targetElement = document.getElementById('single-content');
if (targetElement) {
// id="single-content"の最下部を基準に、画面下部に合わせる複雑な計算
var targetBottomAbsolute = targetElement.getBoundingClientRect().top + window.scrollY + targetElement.offsetHeight;
var viewportHeight = window.innerHeight;
var bottomOffset = -320; // ★この値で記事文末と画面下部との調整を行う。マイナスは戻すの意味
var scrollToPosition = targetBottomAbsolute - viewportHeight + bottomOffset;
// スクロール位置の範囲チェック
scrollToPosition = Math.max(0, scrollToPosition);
scrollToPosition = Math.min(scrollToPosition, document.body.scrollHeight - viewportHeight);
window.scrollTo(0, scrollToPosition);
} else {
// フォールバックとして、ページ最下部へジャンプ
console.warn("ターゲットセクション(id='single-content')が見つかりませんでした。ページ最下部にジャンプします。");
window.scrollTo(0, document.body.scrollHeight);
}
});
}
```
## 関連リンク