PluginProbe
Smart Post – Post Grid, Post Carousel, Post Slider Gutenberg Blocks for Blog & News / 4.0.7
Smart Post – Post Grid, Post Carousel, Post Slider Gutenberg Blocks for Blog & News v4.0.7
4.0.8 4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 4.0.2 4.0.1 2.3.5 2.3.6 2.4.0 2.4.1 2.4.10 2.4.11 2.4.12 2.4.13 2.4.14 2.4.15 2.4.16 2.4.17 2.4.18 2.4.19 2.4.2 2.4.20 2.4.21 All 88 releases
post-carousel / blocks / assets / js / back-to-top.js

back-to-top.js in Smart Post – Post Grid, Post Carousel, Post Slider Gutenberg Blocks for Blog & News 4.0.7, at blocks/assets/js/back-to-top.js

131 lines 5.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 document.addEventListener("DOMContentLoaded", () => {
2 const spBackToTop = document.querySelector("#sp-smart-post-back-to-top-btn");
3 if (!spBackToTop) { return };
4
5 const spBackToTopIcon = spBackToTop.querySelector(".sp-smart-post-back-to-top-icon");
6 const spBackToTopLabel = spBackToTop.querySelector(".sp-smart-post-back-to-top-text.sp-back-to-top-text");
7 const spBackToBottomLabel = spBackToTop.querySelector(".sp-smart-post-back-to-top-text.sp-go-to-bottom-text");
8
9 const transitionDelay = (parseInt(spBackToTop.dataset?.transition) * 1000) || 1000;
10 const goToBottom = spBackToTop.dataset?.goBottom || false;
11 const entranceAnimation = spBackToTop.dataset?.animation || "fade-in";
12
13 const animateTransform = {
14 "fade-in": "translate(0px, 0px)",
15 "slide-in-left": "translate(10px, 0px)",
16 "slide-in-right": "translate(-10px, 0px)",
17 "slide-in-up": "translate(0px, 10px)",
18 };
19
20 spBackToTop.style.transition = `all 0.3s ease`;
21 spBackToTopIcon.style.transition = `transform 0.3s ease`;
22
23 let lastScrollY = window.scrollY;
24 let scrollDirection = null;
25
26 // Helper to toggle labels
27 const toggleLabels = (toBottom) => {
28 if (!spBackToTopLabel || !spBackToBottomLabel) { return };
29 // spBackToTopLabel.style.display = toBottom ? "none" : "block";
30 // spBackToBottomLabel.style.display = toBottom ? "block" : "none";
31 spBackToTopLabel.classList.add( toBottom ? "sp-hide-label" : "sp-show-label" );
32 spBackToTopLabel.classList.remove( toBottom ? "sp-show-label" : "sp-hide-label" );
33 spBackToBottomLabel.classList.add( toBottom ? "sp-show-label" : "sp-hide-label" );
34 spBackToBottomLabel.classList.remove( toBottom ? "sp-hide-label" : "sp-show-label" );
35 };
36
37 function updateBackToTopButton() {
38 const scrollY = window.scrollY;
39 if (scrollY > 350) {
40 spBackToTop.style.display = "inline-block";
41 // eslint-disable-next-line
42 spBackToTop.offsetHeight; // trigger reflow
43 spBackToTop.style.opacity = "1";
44 spBackToTop.style.transform = "translate(0px, 0px)";
45 } else {
46 spBackToTop.style.opacity = "0";
47 spBackToTop.style.transform = animateTransform[entranceAnimation];
48 setTimeout(() => {
49 if (window.scrollY <= 250) { spBackToTop.style.display = "none" };
50 }, 300);
51 }
52 }
53
54 // Smooth scroll function
55 function customScrollToTop(duration, direction = null) {
56 const start = window.scrollY;
57 const target = direction === "down" ? document.body.scrollHeight : 0;
58 const distance = target - start;
59 const startTime = performance.now();
60
61 function animateScroll(currentTime) {
62 const elapsed = currentTime - startTime;
63 const progress = Math.min(elapsed / duration, 1);
64 const ease = progress < 0.5
65 ? 4 * progress ** 3
66 : 1 - Math.pow(-2 * progress + 2, 3) / 2;
67 window.scrollTo(0, start + distance * ease);
68 if (elapsed < duration) {
69 requestAnimationFrame(animateScroll)
70 };
71 }
72
73 requestAnimationFrame(animateScroll);
74 }
75
76 function isButtonAtEndOfPage(tolerance = 50, delay = 0) {
77 return new Promise((resolve) => {
78 setTimeout(() => {
79 const rect = spBackToTop.getBoundingClientRect();
80 const absoluteBottom = rect.top + window.scrollY + rect.height;
81 const docHeight = document.documentElement.scrollHeight;
82 resolve((docHeight - absoluteBottom) <= tolerance);
83 }, delay + 100);
84 });
85 }
86
87 async function buttonChangeDirection() {
88 if (await isButtonAtEndOfPage(100, transitionDelay)) {
89 scrollDirection = "up";
90 spBackToTopIcon.style.transform = `rotateX(0deg)`;
91 toggleLabels(false);
92 }
93 }
94
95 // Scroll direction tracking
96 window.addEventListener("scroll", () => {
97 if (goToBottom) {
98 const currentScrollY = window.scrollY;
99 if (currentScrollY > lastScrollY) {
100 scrollDirection = "down";
101 spBackToTopIcon.style.transform = `rotateX(180deg)`;
102 toggleLabels(true);
103 } else if (currentScrollY < lastScrollY) {
104 scrollDirection = "up";
105 spBackToTopIcon.style.transform = `rotateX(0deg)`;
106 toggleLabels(false);
107 }
108 lastScrollY = currentScrollY;
109 }
110 buttonChangeDirection();
111 });
112
113 // Unified click handler (fixed duplicate listener)
114 spBackToTop.addEventListener("click", (e) => {
115 e.preventDefault();
116 customScrollToTop(transitionDelay, goToBottom ? scrollDirection : null);
117 buttonChangeDirection();
118 });
119
120 // Init setup
121 toggleLabels(false);
122 updateBackToTopButton();
123 buttonChangeDirection();
124
125 // Throttled scroll listener for visibility
126 let scrollTimeout;
127 window.addEventListener("scroll", () => {
128 if (scrollTimeout) { cancelAnimationFrame(scrollTimeout) };
129 scrollTimeout = requestAnimationFrame(updateBackToTopButton);
130 });
131 });