@@ -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 ,
0 commit comments