258 lines
8.8 KiB
HTML
258 lines
8.8 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>CSSOM View - scrollIntoView from position:fixed</title>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width,minimum-scale=1">
|
|
<link rel="author" title="David Bokan" href="mailto:bokan@chromium.org">
|
|
<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-scrollintoview">
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
<style>
|
|
body {
|
|
width: 1000vw;
|
|
height: 1000vh;
|
|
/* stripes so we can see scroll offset more easily */
|
|
background: repeating-linear-gradient(
|
|
45deg,
|
|
#A2CFD9,
|
|
#A2CFD9 100px,
|
|
#C3F3FF 100px,
|
|
#C3F3FF 200px
|
|
);
|
|
}
|
|
|
|
.fixedContainer {
|
|
position: fixed;
|
|
bottom: 10px;
|
|
left: 10px;
|
|
width: 150px;
|
|
height: 150px;
|
|
background-color: coral;
|
|
}
|
|
|
|
.fixedContainer.scrollable {
|
|
overflow: auto;
|
|
left: unset;
|
|
right: 10px;
|
|
}
|
|
|
|
button {
|
|
position: absolute;
|
|
margin: 5px;
|
|
}
|
|
|
|
.target {
|
|
position: absolute;
|
|
width: 10px;
|
|
height: 10px;
|
|
background-color: blue;
|
|
left: 50%;
|
|
top: 50%;
|
|
}
|
|
|
|
.scrollable .target {
|
|
left: 200%;
|
|
top: 200%;
|
|
}
|
|
|
|
iframe {
|
|
width: 96vw;
|
|
height: 300px;
|
|
position: absolute;
|
|
left: 2vw;
|
|
top: 100px;
|
|
}
|
|
</style>
|
|
<script>
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<div style="width:90vw">
|
|
<p>
|
|
The orange boxes are position: fixed. Clicking ScrollIntoView in each box
|
|
will attempt to scroll into view the blue target element inside that fixed
|
|
container to block/inline: start (i.e. aligned with top left corner in RTL).
|
|
</p>
|
|
<p>
|
|
scrollIntoView from a position:fixed element must not scroll its
|
|
containing frame; however, it must scroll further ancestor scrollers as
|
|
the element isn't fixed in relation to them.
|
|
</p>
|
|
</div>
|
|
<iframe></iframe>
|
|
<div class="fixedContainer">
|
|
Box A
|
|
<button id="fixedContainerBtn">ScrollIntoView</button>
|
|
<div class="target"></div>
|
|
</div>
|
|
<div class="fixedContainer scrollable">
|
|
Box C
|
|
<button id="scrollableFixedContainerBtn">ScrollIntoView</button>
|
|
<div class="target"></div>
|
|
</div>
|
|
<script>
|
|
if (typeof setup != 'undefined') {
|
|
setup({ explicit_done: true });
|
|
window.addEventListener("load", runTests);
|
|
}
|
|
|
|
function scrollIntoView(evt) {
|
|
const container = evt.target.closest('.fixedContainer');
|
|
const target = container.querySelector('.target');
|
|
target.scrollIntoView({block: 'start', inline: 'start'});
|
|
}
|
|
|
|
document.querySelectorAll('button').forEach((btn) => {
|
|
btn.addEventListener('click', scrollIntoView);
|
|
});
|
|
|
|
const iframe = document.querySelector('iframe');
|
|
iframe.onload = () => {
|
|
frames[0].document.querySelectorAll('button').forEach((btn) => {
|
|
btn.addEventListener('click', scrollIntoView);
|
|
});
|
|
}
|
|
iframe.srcdoc = `
|
|
<!DOCTYPE html>
|
|
<style>
|
|
body {
|
|
height: 200vh;
|
|
width: 200vw;
|
|
/* stripes so we can see scroll offset more easily */
|
|
background: repeating-linear-gradient(
|
|
45deg,
|
|
#C3A2D9,
|
|
#C3A2D9 50px,
|
|
#E5C0FF 50px,
|
|
#E5C0FF 100px
|
|
);
|
|
}
|
|
.fixedContainer {
|
|
position: fixed;
|
|
bottom: 10px;
|
|
left: 10px;
|
|
width: 150px;
|
|
height: 150px;
|
|
background-color: coral;
|
|
}
|
|
|
|
.fixedContainer.scrollable {
|
|
overflow: auto;
|
|
left: unset;
|
|
right: 10px;
|
|
}
|
|
|
|
button {
|
|
position: absolute;
|
|
margin: 5px;
|
|
}
|
|
|
|
.target {
|
|
position: absolute;
|
|
width: 10px;
|
|
height: 10px;
|
|
background-color: blue;
|
|
left: 50%;
|
|
top: 50%;
|
|
}
|
|
|
|
.scrollable .target {
|
|
left: 200%;
|
|
top: 200%;
|
|
}
|
|
</style>
|
|
IFRAME
|
|
<div class="fixedContainer">
|
|
Box B
|
|
<button id="fixedContainerBtn">ScrollIntoView</button>
|
|
<div class="target"></div>
|
|
</div>
|
|
<div class="fixedContainer scrollable">
|
|
Box D
|
|
<button id="scrollableFixedContainerBtn">ScrollIntoView</button>
|
|
<div class="target"></div>
|
|
</div>
|
|
`;
|
|
|
|
function reset() {
|
|
[document, frames[0].document].forEach((doc) => {
|
|
doc.scrollingElement.scrollLeft = 0;
|
|
doc.scrollingElement.scrollTop = 0;
|
|
doc.querySelectorAll('.fixedContainer').forEach((e) => {
|
|
e.scrollLeft = 0;
|
|
e.scrollTop = 0;
|
|
});
|
|
});
|
|
}
|
|
|
|
function runTests() {
|
|
// Test scrollIntoView from a plain, unscrollable position:fixed element.
|
|
// Nothing should scroll.
|
|
test(() => {
|
|
reset()
|
|
const container = document.querySelector('.fixedContainer:not(.scrollable)');
|
|
const target = container.querySelector('.target');
|
|
target.scrollIntoView({block: 'start', inline: 'start'});
|
|
assert_equals(window.scrollX, 0, 'must not scroll window [scrollX]');
|
|
assert_equals(window.scrollY, 0, 'must not scroll window [scrollY]');
|
|
}, `[Box A] scrollIntoView from unscrollable position:fixed`);
|
|
|
|
// Same as above but from inside an iframe. Since the container is fixed
|
|
// only to the iframe, we should scroll the outer window.
|
|
test(() => {
|
|
reset()
|
|
const container = frames[0].document.querySelector('.fixedContainer:not(.scrollable)');
|
|
const target = container.querySelector('.target');
|
|
target.scrollIntoView({block: 'start', inline: 'start'});
|
|
|
|
// Large approx to account for differences like scrollbars
|
|
assert_approx_equals(window.scrollX, 100, 20, 'must scroll outer window [scrollX]');
|
|
assert_approx_equals(window.scrollY, 300, 20, 'must scroll outer window [scrollY]');
|
|
assert_equals(frames[0].window.scrollX, 0, 'must not scroll iframe [scrollX]');
|
|
assert_equals(frames[0].window.scrollY, 0, 'must not scroll iframe [scrollY]');
|
|
}, `[Box B] scrollIntoView from unscrollable position:fixed in iframe`);
|
|
|
|
// Test scrollIntoView from a scroller that's position fixed. The
|
|
// scroller should be scrolled but shouldn't bubble the scroll as the
|
|
// window scrolling won't change the target's position.
|
|
test(() => {
|
|
reset()
|
|
const container = document.querySelector('.fixedContainer.scrollable');
|
|
const target = container.querySelector('.target');
|
|
target.scrollIntoView({block: 'start', inline: 'start'});
|
|
// Large approx to account for differences like scrollbars
|
|
assert_equals(window.scrollX, 0, 'must not scroll window [scrollX]');
|
|
assert_equals(window.scrollY, 0, 'must not scroll window [scrollY]');
|
|
assert_approx_equals(container.scrollLeft, 145, 20,
|
|
'scrollIntoView in container [scrollLeft]');
|
|
assert_approx_equals(container.scrollTop, 145, 20,
|
|
'scrollIntoView in container [scrollTop]');
|
|
}, `[Box C] scrollIntoView from scrollable position:fixed`);
|
|
|
|
// Same as above but from inside an iframe. In this case, the scroller
|
|
// should bubble the scroll but skip its own frame (as it's fixed with
|
|
// respect to its own frame's scroll offset) and bubble to the outer
|
|
// window.
|
|
test(() => {
|
|
reset()
|
|
const container = frames[0].document.querySelector('.fixedContainer.scrollable');
|
|
const target = container.querySelector('.target');
|
|
target.scrollIntoView({block: 'start', inline: 'start'});
|
|
// Large approx to account for differences like scrollbars
|
|
assert_approx_equals(window.scrollX, 740, 20, 'must scroll outer window [scrollX]');
|
|
assert_approx_equals(window.scrollY, 360, 20, 'must scroll outer window [scrollY]');
|
|
assert_approx_equals(container.scrollLeft, 145, 20,
|
|
'scrollIntoView in container [scrollLeft]');
|
|
assert_approx_equals(container.scrollTop, 145, 20,
|
|
'scrollIntoView in container [scrollTop]');
|
|
assert_equals(frames[0].window.scrollX, 0, 'must not scroll iframe [scrollX]');
|
|
assert_equals(frames[0].window.scrollY, 0, 'must not scroll iframe [scrollY]');
|
|
}, `[Box D] scrollIntoView from scrollable position:fixed in iframe`);
|
|
|
|
done();
|
|
}
|
|
|
|
</script>
|
|
</body>
|
|
</html>
|