UNPKG

8.26 kBJavaScriptView Raw
1(function (global, factory) {
2 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3 typeof define === 'function' && define.amd ? define(factory) :
4 (global = global || self, global.computeScrollIntoView = factory());
5}(this, (function () { 'use strict';
6
7 function isElement(el) {
8 return el != null && typeof el === 'object' && el.nodeType === 1;
9 }
10
11 function canOverflow(overflow, skipOverflowHiddenElements) {
12 if (skipOverflowHiddenElements && overflow === 'hidden') {
13 return false;
14 }
15
16 return overflow !== 'visible' && overflow !== 'clip';
17 }
18
19 function getFrameElement(el) {
20 if (!el.ownerDocument || !el.ownerDocument.defaultView) {
21 return null;
22 }
23
24 return el.ownerDocument.defaultView.frameElement;
25 }
26
27 function isHiddenByFrame(el) {
28 var frame = getFrameElement(el);
29
30 if (!frame) {
31 return false;
32 }
33
34 return frame.clientHeight < el.scrollHeight || frame.clientWidth < el.scrollWidth;
35 }
36
37 function isScrollable(el, skipOverflowHiddenElements) {
38 if (el.clientHeight < el.scrollHeight || el.clientWidth < el.scrollWidth) {
39 var style = getComputedStyle(el, null);
40 return canOverflow(style.overflowY, skipOverflowHiddenElements) || canOverflow(style.overflowX, skipOverflowHiddenElements) || isHiddenByFrame(el);
41 }
42
43 return false;
44 }
45
46 function alignNearest(scrollingEdgeStart, scrollingEdgeEnd, scrollingSize, scrollingBorderStart, scrollingBorderEnd, elementEdgeStart, elementEdgeEnd, elementSize) {
47 if (elementEdgeStart < scrollingEdgeStart && elementEdgeEnd > scrollingEdgeEnd || elementEdgeStart > scrollingEdgeStart && elementEdgeEnd < scrollingEdgeEnd) {
48 return 0;
49 }
50
51 if (elementEdgeStart <= scrollingEdgeStart && elementSize <= scrollingSize || elementEdgeEnd >= scrollingEdgeEnd && elementSize >= scrollingSize) {
52 return elementEdgeStart - scrollingEdgeStart - scrollingBorderStart;
53 }
54
55 if (elementEdgeEnd > scrollingEdgeEnd && elementSize < scrollingSize || elementEdgeStart < scrollingEdgeStart && elementSize > scrollingSize) {
56 return elementEdgeEnd - scrollingEdgeEnd + scrollingBorderEnd;
57 }
58
59 return 0;
60 }
61
62 var index = (function (target, options) {
63 var scrollMode = options.scrollMode,
64 block = options.block,
65 inline = options.inline,
66 boundary = options.boundary,
67 skipOverflowHiddenElements = options.skipOverflowHiddenElements;
68 var checkBoundary = typeof boundary === 'function' ? boundary : function (node) {
69 return node !== boundary;
70 };
71
72 if (!isElement(target)) {
73 throw new TypeError('Invalid target');
74 }
75
76 var scrollingElement = document.scrollingElement || document.documentElement;
77 var frames = [];
78 var cursor = target;
79
80 while (isElement(cursor) && checkBoundary(cursor)) {
81 cursor = cursor.parentNode;
82
83 if (cursor === scrollingElement) {
84 frames.push(cursor);
85 break;
86 }
87
88 if (cursor === document.body && isScrollable(cursor) && !isScrollable(document.documentElement)) {
89 continue;
90 }
91
92 if (isScrollable(cursor, skipOverflowHiddenElements)) {
93 frames.push(cursor);
94 }
95 }
96
97 var viewportWidth = window.visualViewport ? visualViewport.width : innerWidth;
98 var viewportHeight = window.visualViewport ? visualViewport.height : innerHeight;
99 var viewportX = window.scrollX || pageXOffset;
100 var viewportY = window.scrollY || pageYOffset;
101
102 var _target$getBoundingCl = target.getBoundingClientRect(),
103 targetHeight = _target$getBoundingCl.height,
104 targetWidth = _target$getBoundingCl.width,
105 targetTop = _target$getBoundingCl.top,
106 targetRight = _target$getBoundingCl.right,
107 targetBottom = _target$getBoundingCl.bottom,
108 targetLeft = _target$getBoundingCl.left;
109
110 var targetBlock = block === 'start' || block === 'nearest' ? targetTop : block === 'end' ? targetBottom : targetTop + targetHeight / 2;
111 var targetInline = inline === 'center' ? targetLeft + targetWidth / 2 : inline === 'end' ? targetRight : targetLeft;
112 var computations = [];
113
114 for (var index = 0; index < frames.length; index++) {
115 var frame = frames[index];
116
117 var _frame$getBoundingCli = frame.getBoundingClientRect(),
118 height = _frame$getBoundingCli.height,
119 width = _frame$getBoundingCli.width,
120 top = _frame$getBoundingCli.top,
121 right = _frame$getBoundingCli.right,
122 bottom = _frame$getBoundingCli.bottom,
123 left = _frame$getBoundingCli.left;
124
125 if (scrollMode === 'if-needed' && targetTop >= 0 && targetLeft >= 0 && targetBottom <= viewportHeight && targetRight <= viewportWidth && targetTop >= top && targetBottom <= bottom && targetLeft >= left && targetRight <= right) {
126 return computations;
127 }
128
129 var frameStyle = getComputedStyle(frame);
130 var borderLeft = parseInt(frameStyle.borderLeftWidth, 10);
131 var borderTop = parseInt(frameStyle.borderTopWidth, 10);
132 var borderRight = parseInt(frameStyle.borderRightWidth, 10);
133 var borderBottom = parseInt(frameStyle.borderBottomWidth, 10);
134 var blockScroll = 0;
135 var inlineScroll = 0;
136 var scrollbarWidth = 'offsetWidth' in frame ? frame.offsetWidth - frame.clientWidth - borderLeft - borderRight : 0;
137 var scrollbarHeight = 'offsetHeight' in frame ? frame.offsetHeight - frame.clientHeight - borderTop - borderBottom : 0;
138
139 if (scrollingElement === frame) {
140 if (block === 'start') {
141 blockScroll = targetBlock;
142 } else if (block === 'end') {
143 blockScroll = targetBlock - viewportHeight;
144 } else if (block === 'nearest') {
145 blockScroll = alignNearest(viewportY, viewportY + viewportHeight, viewportHeight, borderTop, borderBottom, viewportY + targetBlock, viewportY + targetBlock + targetHeight, targetHeight);
146 } else {
147 blockScroll = targetBlock - viewportHeight / 2;
148 }
149
150 if (inline === 'start') {
151 inlineScroll = targetInline;
152 } else if (inline === 'center') {
153 inlineScroll = targetInline - viewportWidth / 2;
154 } else if (inline === 'end') {
155 inlineScroll = targetInline - viewportWidth;
156 } else {
157 inlineScroll = alignNearest(viewportX, viewportX + viewportWidth, viewportWidth, borderLeft, borderRight, viewportX + targetInline, viewportX + targetInline + targetWidth, targetWidth);
158 }
159
160 blockScroll = Math.max(0, blockScroll + viewportY);
161 inlineScroll = Math.max(0, inlineScroll + viewportX);
162 } else {
163 if (block === 'start') {
164 blockScroll = targetBlock - top - borderTop;
165 } else if (block === 'end') {
166 blockScroll = targetBlock - bottom + borderBottom + scrollbarHeight;
167 } else if (block === 'nearest') {
168 blockScroll = alignNearest(top, bottom, height, borderTop, borderBottom + scrollbarHeight, targetBlock, targetBlock + targetHeight, targetHeight);
169 } else {
170 blockScroll = targetBlock - (top + height / 2) + scrollbarHeight / 2;
171 }
172
173 if (inline === 'start') {
174 inlineScroll = targetInline - left - borderLeft;
175 } else if (inline === 'center') {
176 inlineScroll = targetInline - (left + width / 2) + scrollbarWidth / 2;
177 } else if (inline === 'end') {
178 inlineScroll = targetInline - right + borderRight + scrollbarWidth;
179 } else {
180 inlineScroll = alignNearest(left, right, width, borderLeft, borderRight + scrollbarWidth, targetInline, targetInline + targetWidth, targetWidth);
181 }
182
183 var scrollLeft = frame.scrollLeft,
184 scrollTop = frame.scrollTop;
185 blockScroll = Math.max(0, Math.min(scrollTop + blockScroll, frame.scrollHeight - height + scrollbarHeight));
186 inlineScroll = Math.max(0, Math.min(scrollLeft + inlineScroll, frame.scrollWidth - width + scrollbarWidth));
187 targetBlock += scrollTop - blockScroll;
188 targetInline += scrollLeft - inlineScroll;
189 }
190
191 computations.push({
192 el: frame,
193 top: blockScroll,
194 left: inlineScroll
195 });
196 }
197
198 return computations;
199 });
200
201 return index;
202
203})));