scroll-initial-target-nested-container.tentative.html (8792B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title> CSS Scroll Snap 2 Test: scroll-initial-target*</title> 6 <link rel="help" href="https://drafts.csswg.org/css-scroll-snap-2/#scroll-initial-target"> 7 <script src="/resources/testharness.js"></script> 8 <script src="/resources/testharnessreport.js"></script> 9 <script src="/resources/testdriver.js"></script> 10 <script src="/resources/testdriver-actions.js"></script> 11 <script src="/resources/testdriver-vendor.js"></script> 12 <script src="/dom/events/scrolling/scroll_support.js"></script> 13 <script src="/css/css-animations/support/testcommon.js"></script> 14 </head> 15 <body> 16 <style> 17 /** 18 large-space divs are added to simplify scrolling calculations 19 (i.e. expected offsets are just distances from scroller border top to 20 scrollee border top). 21 */ 22 .large-space { 23 position: absolute; 24 height: 500vh; 25 width: 500vw; 26 } 27 #space-filler { 28 width: 500px; 29 height: 500px; 30 background-color: green; 31 } 32 #outer-container { 33 width: 400px; 34 height: 400px; 35 overflow: scroll; 36 position: relative; 37 background-color: yellow; 38 } 39 #inner-container { 40 top: 20px; 41 left: 20px; 42 width: 400px; 43 height: 400px; 44 overflow: visible; 45 position: relative; 46 background-color: blue; 47 } 48 #target { 49 width: 100px; 50 height: 100px; 51 background-color: pink; 52 scroll-initial-target: nearest; 53 } 54 </style> 55 <div id="outer-container"> 56 <div class="large-space" id="large_space_outer"></div> 57 <div id="space-filler"></div> 58 <div id="inner-container"> 59 <div class="large-space" id="large_space_inner"></div> 60 <div id="space-filler"></div> 61 <div id="target"> 62 </div> 63 </div> 64 </div> 65 <script> 66 let outer_container = document.getElementById("outer-container"); 67 let inner_container = document.getElementById("inner-container"); 68 let space_filler = document.getElementById("space-filler"); 69 let target = document.getElementById("target"); 70 71 const inner_scroller_top_offset = 20; 72 const target_height = target.getBoundingClientRect().height; 73 const space_filler_height = space_filler.getBoundingClientRect().height; 74 const inner_content_height = target_height + space_filler_height; 75 const inner_container_height = inner_container.getBoundingClientRect().height; 76 77 let outer_to_target_scrolltop = 78 /*outer space-filler*/space_filler_height + 79 /* 20px top */ inner_scroller_top_offset + 80 /*inner space filler*/ space_filler_height; 81 82 let outer_to_inner_scrolltop = 83 /* outer space-filler*/ space_filler_height + 84 /* 20px top */ inner_scroller_top_offset; 85 86 let inner_to_target_scrolltop = 87 /*inner space filler*/ space_filler_height; 88 89 async function resetDisplay() { 90 outer_container.style.display = "block"; 91 inner_container.style.display = "block"; 92 target.style.display = "block"; 93 return waitForAnimationFrames(2); 94 } 95 96 promise_test(async (t) => { 97 await resetDisplay(); 98 assert_equals(outer_container.scrollTop, outer_to_target_scrolltop, 99 "outer-container is scrolled to scroll-initial-target"); 100 101 // Remove large_space_outer so we can observe outer-container adjust to 102 // its new content height. 103 large_space_outer.style.display = "none"; 104 inner_container.style.display = "none"; 105 await waitForAnimationFrames(2); 106 107 assert_equals(outer_container.scrollTop, 108 space_filler_height - outer_container.clientHeight, 109 "outer-container has only the outer space filler to scroll"); 110 111 large_space_outer.style.display = "initial"; 112 inner_container.style.display = "block"; 113 await waitForAnimationFrames(2); 114 115 assert_equals(outer_container.scrollTop, outer_to_target_scrolltop, 116 "outer-scroller is updated as scroll-initial-target reappears"); 117 }, "display:none scroll-initial-target becomes display:block"); 118 119 promise_test(async (t) => { 120 await waitForCompositorCommit(); 121 await resetDisplay(); 122 assert_equals(outer_container.scrollTop, outer_to_target_scrolltop, 123 "outer-container is scrolled to scroll-initial-target"); 124 125 // Remove large_space_outer so we can observe outer-container adjust to 126 // its new content height. 127 large_space_outer.style.display = "none"; 128 inner_container.style.overflow = "scroll"; 129 await waitForAnimationFrames(2); 130 131 // inner-container has become a scroller and should be scrolled to 132 // scroll-initial-target. 133 assert_equals(inner_container.scrollTop, 134 inner_to_target_scrolltop, 135 "inner-container is fully scrolled to target"); 136 // outer-container should be adjusted to its new max scroll offset. 137 assert_equals(outer_container.scrollTop, 138 space_filler_height + inner_scroller_top_offset + 139 inner_container_height - outer_container.clientHeight, 140 "outer-container's overflowing content is only its direct " + 141 "children"); 142 143 large_space_outer.style.display = "initial"; 144 inner_container.style.overflow = "visible"; 145 await waitForAnimationFrames(2); 146 147 assert_equals(inner_container.scrollTop, 0, 148 "inner-container is no longer a scroll container"); 149 assert_equals(outer_container.scrollTop, outer_to_target_scrolltop, 150 "outer-scroller is the scroll container for target once again"); 151 }, "intermediate overflow:visible container becomes overflow:scroll"); 152 153 promise_test(async (t) => { 154 // This test verifies that: 155 // 1. when both the child and grandchild are scroll-initial-targets, the 156 // first-in-tree-order (i.e. the child) wins/is scrolled to. 157 // 2. if/when the grandchild stops being a scroll-initial-target, the 158 // child (inner container) is scrolled to. 159 await waitForCompositorCommit(); 160 await resetDisplay(); 161 t.add_cleanup(async () => { 162 inner_container.style.scrollInitialTarget = "none"; 163 await waitForAnimationFrames(2); 164 }); 165 166 assert_equals(outer_container.scrollTop, outer_to_target_scrolltop, 167 "outer-container is scrolled to scroll-initial-target"); 168 // Make the inner container a scroll-initial-target. 169 inner_container.style.scrollInitialTarget = "nearest"; 170 await waitForAnimationFrames(2); 171 172 // The inner container has overflow: visible, so it's not the scroll 173 // container of target. 174 assert_equals(outer_container.scrollTop, 175 outer_to_inner_scrolltop, 176 "outer-container is scrolled to inner-container (which is before the" + 177 "inner scroll-initial-target in tree order)"); 178 }, "outer scroll-initial-target takes precedence over inner"); 179 180 promise_test(async (t) => { 181 // This test verifies that a child which is a scroller, is a 182 // scroll-initial-target, and has a scroll-initial-target is scrolled to 183 // by its scrolling container, and also scrolls to its own 184 // scroll-initial-target. 185 await waitForCompositorCommit(); 186 await resetDisplay(); 187 t.add_cleanup(async () => { 188 inner_container.style.overflow = "visible"; 189 inner_container.style.scrollInitialTarget = "none"; 190 await waitForAnimationFrames(2); 191 }); 192 193 assert_equals(outer_container.scrollTop, outer_to_target_scrolltop, 194 "outer-container is scrolled to scroll-initial-target"); 195 196 // Make the inner container a scroll-initial-target. 197 inner_container.style.scrollInitialTarget = "nearest"; 198 await waitForAnimationFrames(2); 199 200 assert_equals(outer_container.scrollTop, 201 outer_to_inner_scrolltop, 202 "outer-container is still scrolled to inner scroll-container" + 203 "as it is a scroll-initial-target and comes before #target in " + 204 "tree-order"); 205 206 // Make the inner container a scroller. 207 inner_container.style.overflow = "scroll"; 208 await waitForAnimationFrames(2); 209 210 assert_equals(outer_container.scrollTop, 211 outer_to_inner_scrolltop, 212 "outer-container is scrolled to the inner container"); 213 assert_equals(inner_container.scrollTop, 214 inner_to_target_scrolltop, 215 "inner-container is scrolled to target"); 216 }, "scroll containers can also be scroll-initial-targets"); 217 </script> 218 </body> 219 </html>