1# Implementing Nested Scrolling 2 3There may be times when you want to implement nested scrolling for the **Web** component. A typical use case is a page that contains multiple scrollable areas including the **Web** component, whose scrolling is intrinsically linked with the scroll positions in other areas. To implement nested scrolling between **Web** components and ArkUI scrollable containers ([Grid](../reference/apis-arkui/arkui-ts/ts-container-grid.md), [List](../reference/apis-arkui/arkui-ts/ts-container-list.md), [Scroll](../reference/apis-arkui/arkui-ts/ts-container-scroll.md), [Swiper](../reference/apis-arkui/arkui-ts/ts-container-swiper.md), [Tabs](../reference/apis-arkui/arkui-ts/ts-container-tabs.md), [WaterFlow](../reference/apis-arkui/arkui-ts/ts-container-waterflow.md), [Refresh](../reference/apis-arkui/arkui-ts/ts-container-refresh.md) and [bindSheet](../reference/apis-arkui/arkui-ts/ts-universal-attributes-sheet-transition.md#bindsheet)), you should set the ArkUI [NestedScrollMode](../reference/apis-arkui/arkui-ts/ts-appendix-enums.md#nestedscrollmode10) attribute for the **Web** components after receiving the scrolling gesture events. 4 5The following provides two solutions for implementing nested scrolling of **Web** components: Solution 1: [Using the nestedScroll Attribute](#using-the-nestedscroll-attribute); Solution 2: [Distributing Scrolling Offsets Through the Parent Scroll Component](#distributing-scrolling-offsets-through-the-parent-scroll-component). You can select a solution based on the specific service scenario. To associate **Web** components with other parent components, you are advised to use solution 1. To customize the scrolling of **Web** components and other scrolling components, especially in some complex scenarios, you are advised to use solution 2. 6 7> **NOTE** 8> 9> If a **Web** component uses full expansion (**layoutMode** is set to **WebLayoutMode.FIT_CONTENT**), the rendering mode (**RenderMode.SYNC_RENDER**) must be explicitly specified. For details, see [layoutMode](../reference/apis-arkweb/arkts-basic-components-web-attributes.md#layoutmode11). 10 11## Using the nestedScroll Attribute 12 13Call the [nestedScroll](../reference/apis-arkweb/arkts-basic-components-web-attributes.md#nestedscroll11) attribute of the **Web** component to set the nested scrolling mode in the up, down, left, and right directions or in the forward and backward directions to implement the scrolling association with the parent component. This attribute also allows the nested scrolling mode to be dynamically changed during the process. 14 15**Complete Code** 16```ts 17// xxx.ets 18import { webview } from '@kit.ArkWeb'; 19 20@Entry 21@ComponentV2 22struct NestedScroll { 23 private scrollerForScroll: Scroller = new Scroller() 24 private listScroller: Scroller = new Scroller() 25 controller: webview.WebviewController = new webview.WebviewController(); 26 @Local arr: Array<number> = [] 27 28 aboutToAppear(): void { 29 for (let i = 0; i < 10; i++) { 30 this.arr.push(i) 31 } 32 } 33 34 build() { 35 Scroll(this.scrollerForScroll) { 36 Column() { 37 Web({ src: $rawfile("index.html"), controller: this.controller }) 38 .nestedScroll({ 39 scrollUp: NestedScrollMode.PARENT_FIRST,// Scroll up the parent component first. 40 scrollDown: NestedScrollMode.SELF_FIRST,// Scroll down the child component first. 41 }).height("100%") 42 Repeat<number>(this.arr) 43 .each((item: RepeatItem<number>) => { 44 Text("Scroll Area") 45 .width("100%") 46 .height("40%") 47 .backgroundColor(0X330000FF) 48 .fontSize(16) 49 .textAlign(TextAlign.Center) 50 }) 51 } 52 } 53 } 54} 55``` 56HTML file to be loaded: 57 58```html 59<!-- index.html --> 60<!DOCTYPE html> 61<html> 62<head> 63 <meta name="viewport" id="viewport" content="width=device-width, initial-scale=1.0"> 64 <style> 65 .blue { 66 background-color: lightblue; 67 } 68 .green { 69 background-color: lightgreen; 70 } 71 .blue, .green { 72 font-size:16px; 73 height:200px; 74 text-align: center; /* Horizontally centered */ 75 line-height: 200px; /* Vertically centered (the height matches the container height) */ 76 } 77 </style> 78</head> 79<body> 80<div class="blue" >webArea</div> 81<div class="green">webArea</div> 82<div class="blue">webArea</div> 83<div class="green">webArea</div> 84<div class="blue">webArea</div> 85<div class="green">webArea</div> 86<div class="blue">webArea</div> 87</body> 88</html> 89``` 90 91 92## Distributing Scrolling Offsets Through the Parent Scroll Component 93 94**How to Implement** 95 961. Scrolling up: 97 98 (1) If the web page does not scroll to the bottom, the **Scroll** component sends the scrolling offset to the **Web** component, and the **Scroll** component does not scroll. 99 100 (2) If the web page scrolls to the bottom but the **Scroll** component does not scroll to the bottom, only the **Scroll** component scrolls and it does not send the scrolling offset to the **Web** and **List** components. 101 102 (3) If the **Scroll** component scrolls to the bottom, the scrolling offset is sent to the **List** component, and the **Scroll** component does not scroll. 1032. Scrolling down: 104 105 (1) If the **List** component does not scroll to the top, the **Scroll** component sends the scrolling offset to the **List** component, and the **Scroll** component does not scroll. 106 107 (2) When the **List** component scrolls to the top but the **Scroll** component does not, the **Scroll** component automatically scrolls. The scrolling offset is not sent to the **List** component and **Web** component. 108 109 (3) If the **Scroll** component scrolls to the top, the scrolling offset is sent to the **Web** component, and the **Scroll** component does not scroll. 110 111**Implementation** 112 1131. Disable the scrolling gestures of the **Web** component. 114 115 (1) Call [setScrollable](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#setscrollable12) to disable touch scrolling. 116 117 ```ts 118 this.webController.setScrollable(false, webview.ScrollType.EVENT); 119 ``` 120 (2) Call [onGestureRecognizerJudgeBegin](../reference/apis-arkui/arkui-ts/ts-gesture-blocking-enhancement.md#ongesturerecognizerjudgebegin13) to disable the scrolling gesture of the **Web** component. 121 122 ```ts 123 .onGestureRecognizerJudgeBegin((event: BaseGestureEvent, current: GestureRecognizer, others: Array<GestureRecognizer>) => { 124 if (current.isBuiltIn() && current.getType() == GestureControl.GestureType.PAN_GESTURE) { 125 return GestureJudgeResult.REJECT; 126 } 127 return GestureJudgeResult.CONTINUE; 128 }) 129 ``` 1302. Disable the gestures of the [List](../reference/apis-arkui/arkui-ts/ts-container-list.md) component. 131 ```ts 132 .enableScrollInteraction(false) 133 ``` 1343. Check whether the **List** and **Scroll** components scroll to the boundary. 135 136 (1) Scroll to the top boundary: **scroller.currentOffset().yOffset <= 0**; 137 138 (2) Scroll to the bottom boundary: **scroller.isAtEnd() == true**; 139 1404. Check whether the **Web** component scrolls to the boundary. 141 142 (1) Obtain the height, content height, and current scrolling offset of the **Web** component. 143 144 (2) Scroll to the top boundary: **webController.getScrollOffset() == 0**; 145 146 (3) Scroll to the bottom boundary: **webController.getScrollOffset().y + this.webHeight >= webController.getPageHeight()**; 147 148 (4) Height of the **Web** component: **webController.[getPageHeight()](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#getpageheight)**; 149 150 (5) Height of the **Web** component window: **webController?.[runJavaScriptExt](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#runjavascriptext10)('window. innerHeight')**; 151 152 (6) Scrolling offset of the **Web** component: **webController.[getScrollOffset()](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#getscrolloffset13)**; 1535. Disable the scrolling feature of the **Scroll** component. 154 155 Bind the **Scroll** component to the [onScrollFrameBegin](../reference/apis-arkui/arkui-ts/ts-container-scroll.md#onscrollframebegin9) event and set the remaining scrolling offset to **0**. Then the **Scroll** component does not scroll, and the inertial scrolling animation does not stop. 1566. Distribute the scrolling offset to the **List** component. 157 ```ts 158 this.listScroller.scrollBy(0, offset) 159 ``` 1607. Distribute the scrolling offset to the **Web** component. 161 ```ts 162 this.webController.scrollBy(0, offset) 163 ``` 164**Complete Code** 165```ts 166// xxx.ets 167import { webview } from '@kit.ArkWeb'; 168 169@Entry 170@ComponentV2 171struct Index { 172 private scroller:Scroller = new Scroller() 173 private listScroller:Scroller = new Scroller() 174 private webController: webview.WebviewController = new webview.WebviewController() 175 private isWebAtEnd:boolean = false 176 private webHeight:number = 0 177 private scrollTop:number = 0 178 @Local arr: Array<number> = [] 179 180 aboutToAppear(): void { 181 for (let i = 0; i < 10; i++) { 182 this.arr.push(i) 183 } 184 } 185 186 getWebHeight() { 187 try { 188 this.webController?.runJavaScriptExt('window.innerHeight', 189 (error, result) => { 190 if (error || !result) { 191 return; 192 } 193 if (result.getType() === webview.JsMessageType.NUMBER) { 194 this.webHeight = result.getNumber() 195 } 196 }) 197 } catch (error) { 198 } 199 } 200 201 getWebScrollTop() { 202 this.isWebAtEnd = false; 203 if (this.webController.getScrollOffset().y + this.webHeight >= this.webController.getPageHeight()) { 204 this.isWebAtEnd = true; 205 } 206 } 207 208 build() { 209 Scroll(this.scroller) { 210 Column() { 211 Web({ 212 src: $rawfile("index.html"), 213 controller: this.webController, 214 }).height("100%") 215 .onPageEnd(() => { 216 this.webController.setScrollable(false, webview.ScrollType.EVENT); 217 this.getWebHeight(); 218 }) 219 // When the recognizer is about to succeed, set whether to enable the recognizer based on the current component status. 220 .onGestureRecognizerJudgeBegin((event: BaseGestureEvent, current: GestureRecognizer, others: Array<GestureRecognizer>) => { 221 if (current.isBuiltIn() && current.getType() == GestureControl.GestureType.PAN_GESTURE) { 222 return GestureJudgeResult.REJECT; 223 } 224 return GestureJudgeResult.CONTINUE; 225 }) 226 List({ scroller: this.listScroller }) { 227 Repeat<number>(this.arr) 228 .each((item: RepeatItem<number>) => { 229 ListItem() { 230 Text("Scroll Area") 231 .width("100%") 232 .height("40%") 233 .backgroundColor(0X330000FF) 234 .fontSize(16) 235 .textAlign(TextAlign.Center) 236 } 237 }) 238 }.height("100%") 239 .maintainVisibleContentPosition(true) 240 .enableScrollInteraction(false) 241 } 242 } 243 .onScrollFrameBegin((offset: number, state: ScrollState)=>{ 244 this.getWebScrollTop(); 245 if (offset > 0) { 246 if (!this.isWebAtEnd) { 247 this.webController.scrollBy(0, offset) 248 return {offsetRemain:0} 249 } else if (this.scroller.isAtEnd()) { 250 this.listScroller.scrollBy(0, offset) 251 return {offsetRemain:0} 252 } 253 } else if (offset < 0) { 254 if (this.listScroller.currentOffset().yOffset > 0) { 255 this.listScroller.scrollBy(0, offset) 256 return {offsetRemain:0} 257 } else if (this.scroller.currentOffset().yOffset <= 0) { 258 this.webController.scrollBy(0, offset) 259 return {offsetRemain:0} 260 } 261 } 262 return {offsetRemain:offset} 263 }) 264 } 265} 266``` 267HTML file to be loaded: 268 269```html 270<!-- index.html --> 271<!DOCTYPE html> 272<html> 273<head> 274 <meta name="viewport" id="viewport" content="width=device-width, initial-scale=1.0"> 275 <style> 276 .blue { 277 background-color: lightblue; 278 } 279 .green { 280 background-color: lightgreen; 281 } 282 .blue, .green { 283 font-size:16px; 284 height:200px; 285 text-align: center; /* Horizontally centered */ 286 line-height: 200px; /* Vertically centered (the height matches the container height) */ 287 } 288 </style> 289</head> 290<body> 291<div class="blue" >webArea</div> 292<div class="green">webArea</div> 293<div class="blue">webArea</div> 294<div class="green">webArea</div> 295<div class="blue">webArea</div> 296<div class="green">webArea</div> 297<div class="blue">webArea</div> 298</body> 299</html> 300``` 301 302<!--RP1--><!--RP1End--> 303