Skip to content

Commit 54d73bd

Browse files
92thunderstipsan
authored andcommitted
fix: scrollable if hidden by frame (#616)
1 parent ebc85bb commit 54d73bd

File tree

4 files changed

+67
-5
lines changed

4 files changed

+67
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`scrollable element is "overflow: visible" but hidden by iframe should scroll to inside iframe 1`] = `
4+
Array [
5+
Object {
6+
"el": "html",
7+
"left": 0,
8+
"top": 116,
9+
},
10+
]
11+
`;

integration/iframe.html

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<!DOCTYPE html>
2+
<meta charset="utf-8">
3+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
4+
<script src="../umd/compute-scroll-into-view.js"></script>
5+
<script src="./utils.js"></script>
6+
<iframe srcdoc="
7+
<div class='container' style='height: 100vh'></div>
8+
<div class='target' style='background: crimson; px; height: 100px; width: 100px;'></div>
9+
">
10+
</iframe>
11+

integration/iframe.test.js

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
beforeAll(async () => {
2+
await page.goto('http://localhost:3000/integration/iframe')
3+
})
4+
5+
describe('scrollable element is "overflow: visible" but hidden by iframe', () => {
6+
test('should scroll to inside iframe', async () => {
7+
expect.assertions(3)
8+
const actual = await page.evaluate(() => {
9+
const iframe = document.querySelector('iframe')
10+
const target = iframe.contentDocument.querySelector('.target')
11+
return window
12+
.computeScrollIntoView(target, {
13+
scrollMode: 'always',
14+
})
15+
.map(window.mapActions)
16+
})
17+
expect(actual).toHaveLength(1)
18+
expect(actual[0]).toMatchObject({ el: 'html' })
19+
expect(actual).toMatchSnapshot()
20+
})
21+
})

src/index.ts

+24-5
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,31 @@ function canOverflow(
6464
return overflow !== 'visible' && overflow !== 'clip'
6565
}
6666

67+
function getFrameElement(el: Element) {
68+
if (!el.ownerDocument || !el.ownerDocument.defaultView) {
69+
return null
70+
}
71+
return el.ownerDocument.defaultView.frameElement
72+
}
73+
74+
function isHiddenByFrame(el: Element): boolean {
75+
const frame = getFrameElement(el)
76+
if (!frame) {
77+
return false
78+
}
79+
80+
return (
81+
frame.clientHeight < el.scrollHeight || frame.clientWidth < el.scrollWidth
82+
)
83+
}
84+
6785
function isScrollable(el: Element, skipOverflowHiddenElements?: boolean) {
6886
if (el.clientHeight < el.scrollHeight || el.clientWidth < el.scrollWidth) {
6987
const style = getComputedStyle(el, null)
7088
return (
7189
canOverflow(style.overflowY, skipOverflowHiddenElements) ||
72-
canOverflow(style.overflowX, skipOverflowHiddenElements)
90+
canOverflow(style.overflowX, skipOverflowHiddenElements) ||
91+
isHiddenByFrame(el)
7392
)
7493
}
7594

@@ -296,14 +315,14 @@ export default (target: Element, options: Options): CustomScrollAction[] => {
296315
block === 'start' || block === 'nearest'
297316
? targetTop
298317
: block === 'end'
299-
? targetBottom
300-
: targetTop + targetHeight / 2 // block === 'center
318+
? targetBottom
319+
: targetTop + targetHeight / 2 // block === 'center
301320
let targetInline: number =
302321
inline === 'center'
303322
? targetLeft + targetWidth / 2
304323
: inline === 'end'
305-
? targetRight
306-
: targetLeft // inline === 'start || inline === 'nearest
324+
? targetRight
325+
: targetLeft // inline === 'start || inline === 'nearest
307326

308327
// Collect new scroll positions
309328
const computations: CustomScrollAction[] = []

0 commit comments

Comments
 (0)