Laravel

Alpine.jsでスクロールして出てくるページのトップに戻るボタンを実装する方法

最近TALLスタックで開発することが多くなり、LivewireやAlpine.jsの知見が溜まってきました。

今回はAlpine.jsでスクロールすると出てくるページのトップに戻るボタンを実装する方法について紹介します。

ちなみにTALLスタックとはT: Tailwind.css, A: Alpine.js, L: Laravel, L: Livewireを使った開発手法のことです。

https://tallstack.dev/

サンプル

ページをスクロールしますと、右下にボタンが表示され、押すとページのトップに戻るというよくある実装です。

目のアイコンを押すとソースコードの閲覧ができます。

コード全体

スタイル調整はCDNで読み込んだTailwind.cssを使用しています。

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="csrf-token" content="{{ csrf_token() }}">

        <title>{{ config('app.name', 'Laravel') }}</title>

        <style>
        [x-cloak] { 
            display:none !important;
        }
        </style>

        <!-- Fonts -->
        <link rel="preconnect" href="https://fonts.googleapis.com">
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
        <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+JP:wght@500;700&display=swap" rel="stylesheet">
        
        <script src="https://cdn.tailwindcss.com"></script>
        <script defer src="https://unpkg.com/alpinejs@3.10.5/dist/cdn.min.js"></script>
    </head>
    <body class="antialiased">
        <div id="app">
            <div class="relative">
                <header class="bg-black h-14 text-white flex items-center">Header</header>
                <div id="wrapper" class="max-w-[480px] mx-auto">
                    <main class="mb-14">
                        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla nec laoreet dolor. Pellentesque diam nunc, commodo vel rhoncus a, eleifend eget dolor. Aenean consequat tincidunt pretium. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam sed nulla rhoncus, accumsan ligula sit amet, euismod risus. Mauris placerat augue nec augue pharetra, placerat maximus neque rutrum. Integer ut finibus lectus. Praesent lacinia blandit nibh et cursus. Donec interdum tincidunt nisl, in dapibus quam aliquam a.</p>
                        <p>Quisque id tincidunt urna. Praesent lacus nulla, interdum ut elit ac, placerat lacinia sem. Pellentesque malesuada risus felis, id mattis ligula viverra euismod. Nunc sed commodo ipsum. Aenean ullamcorper finibus arcu ut pulvinar. Maecenas aliquet dictum interdum. Sed placerat mi ut massa feugiat, eget mollis ante gravida. Nullam vitae orci velit. Integer hendrerit fermentum eleifend. Nullam facilisis et diam in vehicula.</p>
                        <p>Aliquam sit amet rhoncus urna. Suspendisse vitae nisl id dui ornare varius. Nunc elementum tortor at diam aliquet posuere. Phasellus quis congue lacus, vitae lobortis elit. Sed pharetra consectetur tincidunt. Integer aliquam mi non erat faucibus, eu semper ex viverra. Curabitur lacus diam, posuere et vestibulum in, gravida vitae felis. Pellentesque eget laoreet tortor. Pellentesque non sem volutpat, pretium ipsum at, pharetra nisl. Donec venenatis augue eget accumsan dictum. Aenean pharetra rhoncus urna, ut blandit nisi feugiat non. Nam id augue ac metus luctus gravida. Mauris in nibh vel magna sagittis euismod. Nulla hendrerit, neque et ullamcorper vulputate, massa massa placerat mi, sit amet maximus tellus ante id orci. Nulla ultrices sem id mi rutrum, sed porta dui sollicitudin. Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
                        <p>Praesent in rhoncus lacus. Quisque finibus lacus vitae nisi ullamcorper porta. Donec in mattis nisi. Fusce turpis erat, ultricies sit amet ligula venenatis, suscipit congue arcu. Etiam convallis dui et nisi bibendum, ut faucibus sapien finibus. Ut sollicitudin erat bibendum tincidunt tempus. Mauris et purus quis tellus euismod consequat. Ut euismod laoreet nunc, id scelerisque est. Ut congue ullamcorper arcu sit amet mattis.</p>
                        <p>Integer nec sem felis. Cras varius viverra ipsum, quis gravida nunc porta eu. Nunc faucibus sit amet massa ut porttitor. Ut iaculis maximus ex nec placerat. Integer maximus eros nunc. Donec mattis condimentum aliquam. Quisque consequat sem venenatis nisl sollicitudin, sed ultrices turpis congue. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec pretium, tellus ac efficitur bibendum, sapien enim finibus lacus, feugiat congue risus massa ut quam. In hac habitasse platea dictumst. Integer ultricies faucibus vestibulum. Aliquam erat volutpat. Nam vel finibus turpis, vitae ullamcorper purus. Nullam sollicitudin mauris enim, eu fringilla felis ornare non.</p>
                    </main>
                    <div x-cloak x-data="{scroll : false}" @scroll.window="document.documentElement.scrollTop > 20 ? scroll = true : scroll = false" x-show="scroll" @click="window.scrollTo({top: 0, behavior: 'smooth'})" data-mdb-ripple-color="light" class="fixed z-10 transition duration-150 ease-in-out bottom-20 right-3 cursor-pointer" x-transition.opacity>
                        <div class="flex flex-col items-center justify-center bg-gray-500 w-12 h-12 rounded-full">
                            <span class="w-4 h-4 border-t-2 border-l-2 border-white rotate-45 mt-2"></span>
                        </div>
                    </div>
                </div>
                <footer class="bg-black h-14 text-white flex items-center">Footer</footer>
            </div>
        </div>
    </body>
</html>

コード解説

<div x-cloak x-data="{scroll : false}"
 @scroll.window="document.documentElement.scrollTop > 20 ? scroll = true : scroll = false" 
x-show="scroll" 
@click="window.scrollTo({top: 0, behavior: 'smooth'})" 
class="fixed z-10 transition duration-150 ease-in-out bottom-20 right-3 cursor-pointer"
 x-transition.opacity>
  <div class="flex flex-col items-center justify-center bg-gray-500 w-12 h-12 rounded-full">
    <span class="w-4 h-4 border-t-2 border-l-2 border-white rotate-45 mt-2"></span>
  </div>
</div>

処理の流れは以下の通りです。

  1. x-dataディレクティブでscroll変数を宣言
  2. scrollをx-showの制御値に設定
  3. @scroll.windowに20pxスクロールしたらscrollの値をTrueに変更する処理を実装
  4. 3により、x-showのscrollがTrueになるので要素が表示される。

以下、使用しているAlpine.jsディレクティブの説明です。

ディレクティブ名説明
x-data各要素でAlpine.jsを使用するために必要で最初に記述するディレクティブ。
オブジェクト形式で変数やメソッドなどを定義していく。
これがないと子要素でAlpine.jsが使えない。
x-cloakAlpine.jsの初期化が完了するまでAlpine.jsを使用して隠している要素は表示されてしまい、ちらつきが発生する。これを防ぐために、x-dataを設定したところにこのディレクティブをつけ、以下のスタイルを追加する。
[x-cloak] { display: none !important; }
初期化が完了すると、x-cloakは要素から削除されるため、表示のちらつきを防ぐことができる。
@scroll.windowwindow.scroll()に相当するディレクティブ。
x-show値がTrueの時に要素を表示するディレクティブ。
@clickクリックイベントを発行するディレクティブ。
クリックすると値の内容が実行される。
x-transition要素のtransitionを設定するディレクティブ。

おわりに

今回特に難しいことはしていませんが、Alpine.jsに慣れていないと中々とっつきづらいのかなと思っています。

Vue.jsの書き方と似ているようですが、ほぼReactにしか触れてこなかった身としては慣れるまでに時間がかかりました⏳

LivewireやAlpine.js情報が少ないので、これからもっと知見を共有していきたいと思います👍