- Xのタイムラインを見ていたら、画面のメニューバーの透過度が上下のスクロールに応じて変化していることに気づきました。
- 少し下にスクロールすると半透明になり、少し上にスクロールすると元に戻っています。
- 自分のサイトのヘッダーメニュー(mobile-header-menu-buttons)でも、このような処理をするように、JavaScriptを作りたいと思います。
画面内の情報の表示範囲が増えるからです。
1. javascript.jsとstyle.css
ユーザーがページを下にスクロールするとヘッダーメニューが半透明になり、上にスクロールするか、ヘッダーメニューをタッチすると、ヘッダーメニューが完全に不透明になります。
テーマのjavascript.jsに以下のコードを追加しました。
これで、全ページでスクリプトが動作します。
/** 下にスクロールするとヘッダーメニューを半透明にするようにした 2024-05-12 */
let lastScrollPosition = 0;
const headerMenu = document.querySelector('.mobile-header-menu-buttons');
window.addEventListener('scroll', function() {
const scrollPosition = window.pageYOffset;
if (scrollPosition > lastScrollPosition) {
// 下にスクロールした場合
headerMenu.style.opacity = '0.5';
} else {
// 上にスクロールした場合
headerMenu.style.opacity = '1';
}
lastScrollPosition = scrollPosition;
});
headerMenu.addEventListener('touchstart', function() {
headerMenu.style.opacity = '1';
});
また、style.cssに追加します。
.mobile-header-menu-buttons {
transition: opacity 0.3s ease;
}
2. スクリプトの説明
このスクリプトでは、スクロールとタッチイベントに応じて、ヘッダーメニューの透明度を変更する機能を実装しています。
まず、変数を準備します。
lastScrollPosition
変数を宣言し、初期値を0に設定します。この変数は、前回のスクロール位置を保持するために使用されます。- 次に、
.mobile-header-menu-buttons
クラスを持つ要素を選択し、headerMenu
変数に代入します。
そして、ウィンドウのスクロールイベントにリスナーを追加します。
スクロールが発生すると、以下の処理が行われます。
- 現在のスクロール位置を
scrollPosition
変数に取得します。 scrollPosition
がlastScrollPosition
より大きい場合、下にスクロールしたと判断されます。この場合、headerMenu
の不透明度を0.5
に設定し、半透明にします。- それ以外の場合、上にスクロールしたと判断されます。この場合、
headerMenu
の不透明度を1
に設定し、完全に不透明にします。 - 現在のスクロール位置を
lastScrollPosition
に保存します。次のスクロールイベントで比較するために使用されます。
最後に、headerMenu
にタッチイベントのリスナーを追加します。headerMenu
がタッチされると、すぐに完全に不透明に戻ります。
CSSでは、opacity(透過度)のプロパティが変更された場合、0.3秒かけてスムーズに変化するように「transition: opacity 0.3s ease;
」を追加しています。
3. 【追記】透明度の調整とクリック操作への対応(2024-05-17)
当初、ヘッダー要素を半透明にしていましたが、見えにくいことに気づきました。
そこで、半透明ではなく完全に透明に変更しました。
また、パソコンでは、クリックに反応しないという問題がありました。
そのため、タッチ操作とクリック操作の両方に対応できるように、handleEvent関数を用意しました。
let lastScrollPosition = 0;
const headerMenu = document.querySelector('.mobile-header-menu-buttons');
window.addEventListener('scroll', function() {
const scrollPosition = window.pageYOffset;
if (scrollPosition > lastScrollPosition) {
// 下にスクロールした場合
headerMenu.style.opacity = '0';
} else {
// 上にスクロールした場合
headerMenu.style.opacity = '1';
}
lastScrollPosition = scrollPosition;
});
function handleEvent(event) {
if (event.type === 'touchstart') {
event.preventDefault();
}
headerMenu.style.opacity = '1';
}
headerMenu.addEventListener('touchstart', handleEvent);
headerMenu.addEventListener('click', handleEvent);
ただし、タッチデバイスでは、タッチ時にタッチスタートイベントとクリックイベントの両方が発火してしまうと思って、preventDefault()
を使ってタッチイベントのデフォルトの動作をキャンセルし、タッチ時には1回だけ処理が実行されるようにしました。
3-1. 【追記】スクロール量が20px以上の場合にのみ透過度を変更(2024-05-18)
しかし、確認してみたらメニューがタッチ操作に反応しなくなってしまいました。
そこで、preventDefault()
は削除しました。
また、scrollThresholdを指定して、スクロール閾値を超えたときに透明化処理を戻すことにしました。
/** 下にスクロールするとヘッダーメニューを半透明にするようにした 2024-05-12
* 2024-05-17: メニューをクリックしたときの反応
* 2024-05-18: スクロール量が20px以上の場合にのみ透過度を変更
*/
let lastScrollPosition = 0;
const headerMenu = document.querySelector('.mobile-header-menu-buttons');
window.addEventListener('scroll', function() {
const scrollPosition = window.pageYOffset;
const scrollThreshold = 20; // スクロールしきい値(px)を設定
if (scrollPosition > lastScrollPosition + scrollThreshold) {
// 下にスクロールした場合
headerMenu.style.opacity = '0';
lastScrollPosition = scrollPosition;
}
if (scrollPosition < lastScrollPosition - scrollThreshold) {
// 上にスクロールした場合
headerMenu.style.opacity = '1';
lastScrollPosition = scrollPosition;
}
});
function handleEvent(event) {
headerMenu.style.opacity = '1';
}
headerMenu.addEventListener('touchstart', handleEvent);
headerMenu.addEventListener('click', handleEvent);