-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[carousel] Pin the selected scroll-marker for targeted scrolls
The CSS working group resolved[1] that when a scroll operation is aimed at an element, i.e. Element.scrollIntoView, the associated scroll-marker-group should select the active scroll-marker based on the element which the operation is intending to scroll to. In such cases, the scroll-marker that should be selected is the scroll-marker associated with the first scroll target (a scroll target is an element which generates a scroll-marker) found through a search starting from the target of the scrollIntoView itself and backwards in tree-order. As soon as some other type of scroll occurs, e.g. Element.scrollTo, or a user gesture scroll, the scroll-marker-group should no longer consider its active marker pinned, i.e. it should be based on the scroll position. This patch implements this for elements in general, but not for ::column pseudo elements which may also act as scroll targets since ::column::scroll-marker is allowed. The ::column case needs to be handled specially as ::column pseudos are not parents of the elements which are flowed into them in the DOM tree. This will be done in a follow-up patch. [1] w3c/csswg-drafts#10738 (comment) Bug: 380062280 Change-Id: I363e0f055f9791ead0b35f4bbe037db91f299624 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6089232 Reviewed-by: Steve Kobes <skobes@chromium.org> Commit-Queue: David Awogbemila <awogbemila@chromium.org> Cr-Commit-Position: refs/heads/main@{#1399197}
- Loading branch information
1 parent
3f44436
commit 9bbcdbf
Showing
1 changed file
with
247 additions
and
0 deletions.
There are no files selected for viewing
247 changes: 247 additions & 0 deletions
247
css/css-overflow/targeted-scroll-marker-selection.tentative.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,247 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
|
||
<head> | ||
<meta charset="utf-8"> | ||
<title>CSS Test: scroll tracking for ::scroll-markers whose orignatin elements cannot be scroll-aligned </title> | ||
<link rel="help" href="https://drafts.csswg.org/css-overflow-5/#scroll-container-scroll"> | ||
<script src="/resources/testharness.js"></script> | ||
<script src="/resources/testharnessreport.js"></script> | ||
<script src="/resources/testdriver.js"></script> | ||
<script src="/resources/testdriver-actions.js"></script> | ||
<script src="/resources/testdriver-vendor.js"></script> | ||
<script src="/css/css-transitions/support/helper.js"></script> | ||
<script src="/dom/events/scrolling/scroll_support.js"></script> | ||
</head> | ||
|
||
<body> | ||
<style> | ||
.wrapper { | ||
display: grid; | ||
justify-content: center; | ||
} | ||
|
||
.carousel { | ||
display: grid; | ||
grid-auto-flow: column; | ||
width: 1600px; | ||
height: 512px; | ||
overflow-x: scroll; | ||
scroll-snap-type: x mandatory; | ||
list-style-type: none; | ||
scroll-behavior: smooth; | ||
border: solid 2px grey; | ||
padding-top: 10%; | ||
text-align: center; | ||
counter-set: markeridx -1; | ||
|
||
div>.item { | ||
&>.itemchild { | ||
|
||
} | ||
scroll-snap-align: center; | ||
height: 80%; | ||
width: 318px; | ||
border: 1px solid; | ||
place-content: center; | ||
|
||
&::scroll-marker { | ||
content: counter(markeridx); | ||
counter-increment: markeridx; | ||
align-content: center; | ||
text-align: center; | ||
width: 35px; | ||
height: 35px; | ||
border: 3px solid gray; | ||
border-radius: 50%; | ||
margin: 3px; | ||
background-color: red; | ||
} | ||
|
||
&::scroll-marker:target-current { | ||
background-color: green; | ||
} | ||
&::scroll-marker:checked { | ||
background-color: green; | ||
} | ||
} | ||
|
||
scroll-marker-group: after; | ||
|
||
&::scroll-marker-group { | ||
height: 45px; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
border: solid 1px black; | ||
border-radius: 30px; | ||
} | ||
} | ||
</style> | ||
<div class="wrapper" id="wrapper"> | ||
<div class="carousel" id="carousel"> | ||
<div> | ||
<div class="item" id="item0" tabindex=0> | ||
<div class="itemchild" id="itemchild0">0</div> | ||
</div> | ||
<div class="itemsibling" id="itemsibling0"></div> | ||
</div> | ||
<div> | ||
<div class="item" id="item1" tabindex=0> | ||
<div class="itemchild" id="itemchild1">1</div> | ||
</div> | ||
<div class="itemsibling" id="itemsibling1"></div> | ||
</div> | ||
<div> | ||
<div class="item" id="item2" tabindex=0> | ||
<div class="itemchild" id="itemchild2">2</div> | ||
</div> | ||
<div class="itemsibling" id="itemsibling2"></div> | ||
</div> | ||
<div> | ||
<div class="item" id="item3" tabindex=0> | ||
<div class="itemchild" id="itemchild3">3</div> | ||
</div> | ||
<div class="itemsibling" id="itemsibling3"></div> | ||
</div> | ||
<div> | ||
<div class="item" id="item4" tabindex=0> | ||
<div class="itemchild" id="itemchild4">4</div> | ||
</div> | ||
<div class="itemsibling" id="itemsibling4"></div> | ||
</div> | ||
<div> | ||
<div class="item" id="item5" tabindex=0> | ||
<div class="itemchild" id="itemchild5">5</div> | ||
</div> | ||
<div class="itemsibling" id="itemsibling5"></div> | ||
</div> | ||
<div> | ||
<div class="item" id="item6" tabindex=0> | ||
<div class="itemchild" id="itemchild6">6</div> | ||
</div> | ||
<div class="itemsibling" id="itemsibling6"></div> | ||
</div> | ||
<div> | ||
<div class="item" id="item7" tabindex=0> | ||
<div class="itemchild" id="itemchild7">7</div> | ||
</div> | ||
<div class="itemsibling" id="itemsibling7"></div> | ||
</div> | ||
<div> | ||
<div class="item" id="item8" tabindex=0> | ||
<div class="itemchild" id="itemchild8">8</div> | ||
</div> | ||
<div class="itemsibling" id="itemsibling8"></div> | ||
</div> | ||
<div> | ||
<div class="item" id="item9" tabindex=0> | ||
<div class="itemchild" id="itemchild9">9</div> | ||
</div> | ||
<div class="itemsibling" id="itemsibling9"></div> | ||
</div> | ||
<div> | ||
<div class="item" id="item10" tabindex=0> | ||
<div class="itemchild" id="itemchild10">10</div> | ||
</div> | ||
<div class="itemsibling" id="itemsibling10"></div> | ||
</div> | ||
<div> | ||
<div class="item" id="item11" tabindex=0> | ||
<div class="itemchild" id="itemchild11">11</div> | ||
</div> | ||
<div class="itemsibling" id="itemsibling11"></div> | ||
</div> | ||
<div> | ||
<div class="item" id="item12" tabindex=0> | ||
<div class="itemchild" id="itemchild12">12</div> | ||
</div> | ||
<div class="itemsibling" id="itemsibling12"></div> | ||
</div> | ||
<div> | ||
<div class="item" id="item13" tabindex=0> | ||
<div class="itemchild" id="itemchild13">13</div> | ||
</div> | ||
<div class="itemsibling" id="itemsibling13"></div> | ||
</div> | ||
<div> | ||
<div class="item" id="item14" tabindex=0> | ||
<div class="itemchild" id="itemchild14">14</div> | ||
</div> | ||
<div class="itemsibling" id="itemsibling14"></div> | ||
</div> | ||
<div> | ||
<div class="item" id="item15" tabindex=0> | ||
<div class="itemchild" id="itemchild15">15</div> | ||
</div> | ||
<div class="itemsibling" id="itemsibling15"></div> | ||
</div> | ||
</div> | ||
</div> | ||
<script> | ||
|
||
const carousel = document.getElementById("carousel"); | ||
const items = document.querySelectorAll(".item"); | ||
const wrapper = document.getElementById("wrapper"); | ||
|
||
RED = "rgb(255, 0, 0)"; | ||
GREEN = "rgb(0, 128, 0)"; | ||
|
||
function verifySelectedMarker(selected_idx) { | ||
for (let idx = 0; idx < items.length; idx++) { | ||
const should_be_selected = idx == selected_idx | ||
let expected_color = should_be_selected ? GREEN : RED; | ||
const color = | ||
getComputedStyle(items[idx], "::scroll-marker").backgroundColor; | ||
assert_equals(color, expected_color, | ||
`marker ${idx} should be ${should_be_selected ? "" : "un"}selected.`); | ||
} | ||
} | ||
|
||
const max_scroll_offset = carousel.scrollWidth - carousel.clientWidth; | ||
async function testTargetedHasActiveMarker(test, element, expected_idx) { | ||
// Start from somewhere in the middle, ensuring that scrolling to the | ||
// extremes generates scrollend events. | ||
await waitForScrollReset(test, carousel, max_scroll_offset / 2); | ||
const color = | ||
getComputedStyle(items[expected_idx], "::scroll-marker").backgroundColor; | ||
assert_not_equals(color, GREEN, | ||
`Target item ${expected_idx} is not selected yet.`); | ||
const scrollend_promise = waitForScrollendEventNoTimeout(carousel); | ||
element.scrollIntoView({behavior: "smooth"}); | ||
await scrollend_promise; | ||
verifySelectedMarker(expected_idx); | ||
} | ||
|
||
promise_test(async(t) => { | ||
// Item 1 cannot be snap-aligned because there is not enough | ||
// room for its scroll container to be aligned to its center. | ||
const target_item = items[1]; | ||
await testTargetedHasActiveMarker(t, target_item, 1); | ||
}, "scroll-marker of target (idx 1) of scrollIntoView is selected"); | ||
|
||
promise_test(async(t) => { | ||
// Item 14 cannot be snap-aligned because there is not enough | ||
// room for its scroll container to be aligned to its center. | ||
const target_item = items[14]; | ||
await testTargetedHasActiveMarker(t, target_item, 14); | ||
}, "scroll-marker of target (idx 14) of scrollIntoView is selected"); | ||
|
||
promise_test(async(t) => { | ||
const item14 = items[14]; | ||
const target_item = item14.querySelector(".itemchild"); | ||
// Item 14 is the parent. We call scrollIntoView on its child. | ||
await testTargetedHasActiveMarker(t, target_item, 14); | ||
}, "scroll-marker of parent of target of scrollIntoView is selected"); | ||
|
||
promise_test(async(t) => { | ||
const item14 = items[14]; | ||
const target_item = item14.nextElementSibling; | ||
// Item 14 is before itemsibling14 in the DOM. We call scrollIntoView on | ||
// itemsibling14. | ||
await testTargetedHasActiveMarker(t, target_item, 14); | ||
}, "scroll-marker of earlier sibling of target of scrollIntoView is selected"); | ||
</script> | ||
</body> | ||
|
||
</html> |