| <!DOCTYPE html> |
| <meta charset="utf-8"> |
| <title>Keyboard scrolling targets the last clicked element</title> |
| <link rel="help" href=""> |
| <link rel="author" href="flackr@chromium.org"> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="/resources/testdriver.js"></script> |
| <script src="/resources/testdriver-vendor.js"></script> |
| <script src="/resources/testdriver-actions.js"></script> |
| <script src="/css/css-scroll-snap/support/common.js"></script> |
| <script src="/dom/events/scrolling/scroll_support.js"></script> |
| <style> |
| .scroller { |
| overflow: auto; |
| height: 200px; |
| border: 1px solid black; |
| } |
| .spacer { |
| height: 200vh; |
| } |
| </style> |
| <body id="body"> |
| <div class="scroller" id="outer"> |
| <div class="scroller" id="inner"> |
| <div id="target"><span>This is the targeted node</span></div> |
| <div class="spacer"></div> |
| </div> |
| <div class="spacer"></div> |
| </div> |
| <div class="spacer"></div> |
| </body> |
| <script> |
| |
| const target = document.getElementById("target"); |
| const outer = document.getElementById("outer"); |
| const inner = document.getElementById("inner"); |
| const scrollTargets = [document, outer, inner]; |
| |
| function raf() { |
| return new Promise((resolve) => { |
| requestAnimationFrame(resolve); |
| }); |
| } |
| |
| async function getKeyboardScrollingElement(test, clickTarget, onclick) { |
| const click_promise = waitForEvent("click", test, clickTarget); |
| await test_driver.click(clickTarget); |
| await click_promise; |
| await onclick(); |
| await raf(); |
| const scrollEndPromise = waitForScrollEndFallbackToDelayWithoutScrollEvent(scrollTargets); |
| await keyPress(document.body, "ArrowDown"); |
| return scrollEndPromise; |
| } |
| |
| function friendlyName(node) { |
| if (node == document) return "document"; |
| if (node.id) return `#${node.id}`; |
| return `<${node.tagName}>`; |
| } |
| |
| async function resetScroll() { |
| for (const scrollTarget of scrollTargets) { |
| const scroller = scrollTarget.scrollingElement || scrollTarget; |
| scroller.scrollTo(0, 0); |
| } |
| return raf(); |
| } |
| |
| promise_test(async (test) => { |
| test.add_cleanup(resetScroll); |
| const scrolled = await getKeyboardScrollingElement(test, target, async () => { |
| target.remove(); |
| test.add_cleanup(() => { |
| inner.insertBefore(target, inner.firstChild); |
| }); |
| }); |
| assert_equals(friendlyName(scrolled), "#inner"); |
| }, "Keyboard scrolling scrolls the scroller when clicked target is removed"); |
| |
| // Notably removing all children is a different code path than removing |
| // a single child. This is a regression test for https://crbug.com/40941145. |
| promise_test(async (test) => { |
| test.add_cleanup(resetScroll); |
| const scrolled = await getKeyboardScrollingElement(test, target.firstElementChild, async () => { |
| const previous = target.innerHTML; |
| target.innerHTML = ""; |
| test.add_cleanup(() => { |
| target.innerHTML = previous; |
| }); |
| }); |
| assert_equals(friendlyName(scrolled), "#inner"); |
| }, "Keyboard scrolling scrolls the scroller when clicked children are removed"); |
| |
| promise_test(async (test) => { |
| test.add_cleanup(resetScroll); |
| const scrolled = await getKeyboardScrollingElement(test, target, async () => { |
| inner.remove(); |
| test.add_cleanup(() => { |
| outer.insertBefore(inner, outer.firstChild); |
| }); |
| }); |
| assert_equals(friendlyName(scrolled), "#outer"); |
| }, "Keyboard scrolling scrolls the next nearest scroller if the clicked scroller is removed"); |
| |
| </script> |