@@ -306,6 +306,102 @@ const get_scroll_y = (scroll_reference) => {
306
306
: scroll_reference . scrollY ;
307
307
} ;
308
308
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
+
309
405
/**
310
406
* Get data stored directly on the node instance.
311
407
* We are using a prefix to make sure the data doesn't collide with other attributes.
@@ -450,6 +546,10 @@ const dom = {
450
546
find_scroll_container : find_scroll_container ,
451
547
get_scroll_x : get_scroll_x ,
452
548
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 ,
453
553
get_data : get_data ,
454
554
set_data : set_data ,
455
555
delete_data : delete_data ,
0 commit comments