Skip to content

Commit 8015206

Browse files
authored
Merge pull request #1166 from Patternslib/scrolling-helpers
Scrolling helpers
2 parents fb4b1c1 + f37bc99 commit 8015206

File tree

3 files changed

+116
-78
lines changed

3 files changed

+116
-78
lines changed

Diff for: src/core/dom.js

+100
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,102 @@ const get_scroll_y = (scroll_reference) => {
306306
: scroll_reference.scrollY;
307307
};
308308

309+
/**
310+
* Get the elements position relative to another element.
311+
*
312+
* @param {Node} el - The DOM element to get the position for.
313+
* @param {Node} [reference_el=document.body] - The DOM element to get the position relative to.
314+
*
315+
* @returns {{top: number, left: number}} - The position of the element relative to the other element.
316+
*/
317+
const get_relative_position = (el, reference_el = document.body) => {
318+
// Get the reference element to which against we calculate
319+
// the relative position of the target.
320+
// In case of a scroll container of window, we do not have
321+
// getBoundingClientRect method, so get the body instead.
322+
if (reference_el === window) {
323+
reference_el = document.body;
324+
}
325+
326+
// Calculate absolute [¹] position difference between
327+
// scroll_container and scroll_target.
328+
// Substract the container's border from the scrolling
329+
// value, as this one isn't respected by
330+
// getBoundingClientRect [²] and would lead to covered
331+
// items [³].
332+
// ¹) so that it doesn't make a difference, if the element
333+
// is below or above the scrolling container. We just need
334+
// to know the absolute difference.
335+
// ²) Calculations are based from the viewport.
336+
// ³) See:
337+
// https://docs.microsoft.com/en-us/previous-versions//hh781509(v=vs.85)
338+
// https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect
339+
const left = Math.abs(
340+
el.getBoundingClientRect().left +
341+
reference_el.scrollLeft -
342+
reference_el.getBoundingClientRect().left -
343+
dom.get_css_value(reference_el, "border-left-width", true)
344+
);
345+
const top = Math.abs(
346+
el.getBoundingClientRect().top +
347+
reference_el.scrollTop -
348+
reference_el.getBoundingClientRect().top -
349+
dom.get_css_value(reference_el, "border-top-width", true)
350+
);
351+
352+
return { top, left };
353+
};
354+
355+
/**
356+
* Scroll to a given element.
357+
* The element will be scrolled to the top of the scroll container.
358+
*
359+
* @param {Node} el - The element which should be scrolled to.
360+
* @param {Node} scroll_container - The element which is scrollable.
361+
* @param {number} [offset=0] - Optional offset in pixels to stop scrolling before the target position. Can also be a negative number.
362+
* @param {string} [direction="top"] - The direction to scroll to. Can be either "top", "left" or "both".
363+
*/
364+
const scroll_to_element = (el, scroll_container, offset = 0, direction = "top") => {
365+
// Get the position of the element relative to the scroll container.
366+
const position = get_relative_position(el, scroll_container);
367+
368+
const options = { behavior: "auto" };
369+
if (direction === "top" || direction === "both") {
370+
options.top = position.top - offset;
371+
}
372+
if (direction === "left" || direction === "both") {
373+
options.left = position.left - offset;
374+
}
375+
376+
// Scroll to the target position.
377+
scroll_container.scrollTo(options);
378+
};
379+
380+
/**
381+
* Scroll to the top of a scrolling container.
382+
*
383+
* @param {Node} [scroll_container = document.body] - The element which is scrollable.
384+
* @param {number} [offset=0] - Optional offset in pixels to stop scrolling before the target position. Can also be a negative number.
385+
*/
386+
const scroll_to_top = (scroll_container = document.body, offset = 0) => {
387+
// Just scroll up, period.
388+
scroll_container.scrollTo({ top: 0 - offset, behavior: "auto" });
389+
};
390+
391+
/**
392+
* Scroll to the bottom of a scrolling container.
393+
*
394+
* @param {Node} [scroll_container = document.body] - The element which is scrollable.
395+
* @param {number} [offset=0] - Optional offset in pixels to stop scrolling before the target position. Can also be a negative number.
396+
*/
397+
const scroll_to_bottom = (scroll_container = document.body, offset = 0) => {
398+
// Just scroll up, period.
399+
//
400+
const top = (scroll_container === window ? document.body : scroll_container)
401+
.scrollHeight;
402+
scroll_container.scrollTo({ top: top - offset, behavior: "auto" });
403+
};
404+
309405
/**
310406
* Get data stored directly on the node instance.
311407
* We are using a prefix to make sure the data doesn't collide with other attributes.
@@ -450,6 +546,10 @@ const dom = {
450546
find_scroll_container: find_scroll_container,
451547
get_scroll_x: get_scroll_x,
452548
get_scroll_y: get_scroll_y,
549+
get_relative_position: get_relative_position,
550+
scroll_to_element: scroll_to_element,
551+
scroll_to_top: scroll_to_top,
552+
scroll_to_bottom: scroll_to_bottom,
453553
get_data: get_data,
454554
set_data: set_data,
455555
delete_data: delete_data,

Diff for: src/pat/inject/inject.js

+4-47
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ const inject = {
489489
if (cfg.scroll && cfg.scroll !== "none") {
490490
// Get the scroll target for
491491
// 1) finding the scroll container
492-
// 2) getting the scroll offset (if not "top" or "target")
492+
// 2) getting the element to scroll to (if not "top")
493493
const scroll_target = ["top", "target"].includes(cfg.scroll)
494494
? cfg.$target[0]
495495
: $(cfg.scroll, $injected)[0];
@@ -500,53 +500,10 @@ const inject = {
500500
window
501501
);
502502

503-
// default for scroll===top
504-
let top = 0;
505-
let left = 0;
506-
507-
if (cfg.scroll !== "top") {
508-
// Get the reference element to which against we calculate
509-
// the relative position of the target.
510-
// In case of a scroll container of window, we do not have
511-
// getBoundingClientRect method, so get the body instead.
512-
const scroll_container_ref =
513-
scroll_container === window ? document.body : scroll_container;
514-
515-
// Calculate absolute [¹] position difference between
516-
// scroll_container and scroll_target.
517-
// Substract the container's border from the scrolling
518-
// value, as this one isn't respected by
519-
// getBoundingClientRect [²] and would lead to covered
520-
// items [³].
521-
// ¹) so that it doesn't make a difference, if the element
522-
// is below or above the scrolling container. We just need
523-
// to know the absolute difference.
524-
// ²) Calculations are based from the viewport.
525-
// ³) See:
526-
// https://docs.microsoft.com/en-us/previous-versions//hh781509(v=vs.85)
527-
// https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect
528-
left = Math.abs(
529-
scroll_target.getBoundingClientRect().left +
530-
scroll_container_ref.scrollLeft -
531-
scroll_container_ref.getBoundingClientRect().left -
532-
dom.get_css_value(
533-
scroll_container_ref,
534-
"border-left-width",
535-
true
536-
)
537-
);
538-
top = Math.abs(
539-
scroll_target.getBoundingClientRect().top +
540-
scroll_container_ref.scrollTop -
541-
scroll_container_ref.getBoundingClientRect().top -
542-
dom.get_css_value(scroll_container_ref, "border-top-width", true)
543-
);
544-
}
545-
if (scroll_container === window) {
546-
scroll_container.scrollTo(left, top);
503+
if (cfg.scroll === "top") {
504+
dom.scroll_to_top(scroll_container);
547505
} else {
548-
scroll_container.scrollLeft = left;
549-
scroll_container.scrollTop = top;
506+
dom.scroll_to_element(scroll_target, scroll_container);
550507
}
551508
}
552509

Diff for: src/pat/scroll/scroll.js

+12-31
Original file line numberDiff line numberDiff line change
@@ -69,36 +69,6 @@ class Pattern extends BasePattern {
6969
window
7070
);
7171

72-
const scroll_options = { behavior: "auto" }; // Set the behavior in CSS.
73-
if (this.options.selector === "top") {
74-
// Just scroll up or left, period.
75-
scroll_options[this.options.direction] = 0;
76-
} else if (this.options.selector === "bottom") {
77-
// Just scroll down or right, period.
78-
if (this.options.direction === "top") {
79-
scroll_options.top = (
80-
scroll_container === window ? document.body : scroll_container
81-
).scrollHeight;
82-
} else {
83-
scroll_options.left = (
84-
scroll_container === window ? document.body : scroll_container
85-
).scrollWidth;
86-
}
87-
} else if (this.options.direction === "top") {
88-
scroll_options.top = target.offsetTop;
89-
} else if (this.options.direction === "left") {
90-
scroll_options.left = target.offsetLeft;
91-
}
92-
93-
if (typeof scroll_options.top !== "undefined") {
94-
scroll_options.top -= this.options.offset;
95-
}
96-
if (typeof scroll_options.left !== "undefined") {
97-
scroll_options.left -= this.options.offset;
98-
}
99-
100-
log.debug("scroll_options: ", scroll_options);
101-
10272
// Set/remove classes on beginning and end of scroll
10373
this.el.classList.add("pat-scroll-animated");
10474
const debounced_scroll_end = utils.debounce(() => {
@@ -115,7 +85,18 @@ class Pattern extends BasePattern {
11585
debounced_scroll_end();
11686

11787
// now scroll
118-
scroll_container.scrollTo(scroll_options);
88+
if (this.options.selector === "top") {
89+
dom.scroll_to_top(scroll_container, this.options.offset);
90+
} else if (this.options.selector === "bottom") {
91+
dom.scroll_to_bottom(scroll_container, this.options.offset);
92+
} else {
93+
dom.scroll_to_element(
94+
target,
95+
scroll_container,
96+
this.options.offset,
97+
this.options.direction
98+
);
99+
}
119100
}
120101
}
121102

0 commit comments

Comments
 (0)