1# Focus Control 2 3Focus control attributes set whether a component is focusable and how it participates in focus navigation. 4 5> **NOTE** 6> 7> - The APIs of this module are supported since API version 8. Updates will be marked with a superscript to indicate their earliest API version. 8> 9> - Custom components are inherently unfocusable, and setting [focusable](#focusable) and [enabled](ts-universal-attributes-enable.md#enabled) attributes to **false** does not impact their child components' ability to gain focus. 10> 11> - Components can actively acquire focus independently of the window's focus state. 12> 13> - For details about focus development, see [Focus Event](../../../ui/arkts-common-events-focus-event.md). 14 15## focusable 16 17focusable(value: boolean) 18 19Sets whether the component is focusable. 20 21**Atomic service API**: This API can be used in atomic services since API version 11. 22 23**System capability**: SystemCapability.ArkUI.ArkUI.Full 24 25**Parameters** 26 27| Name| Type | Mandatory| Description | 28| ------ | ------- | ---- | ------------------------------------------------------------ | 29| value | boolean | Yes | Whether the component is focusable.<br>**NOTE**<br>Components that have default interaction logic, such as [Button](ts-basic-components-button.md) and [TextInput](ts-basic-components-textinput.md), are focusable by default. Other components, such as [Text](ts-basic-components-text.md) and [Image](ts-basic-components-image.md), are not focusable by default. Only focusable components can trigger a [focus event](ts-universal-focus-event.md).| 30 31## tabIndex<sup>9+</sup> 32 33tabIndex(index: number) 34 35Sets the Tab order of the component in sequential focus navigation with the **Tab** key. 36 37**Atomic service API**: This API can be used in atomic services since API version 11. 38 39**System capability**: SystemCapability.ArkUI.ArkUI.Full 40 41**Parameters** 42 43| Name| Type | Mandatory| Description | 44| ------ | ------ | ---- | ------------------------------------------------------------ | 45| index | number | Yes | Tab order of the component in sequential focus navigation with the **Tab** key. When components with positive **tabIndex** values are present, only these components are reachable through sequential focus navigation, and they are navigated cyclically in ascending order based on the **tabIndex** value. When components with positive **tabIndex** values are not present, those components with a **tabIndex** value of **0** are navigated based on the preset focus navigation rule.<br>**tabIndex** is not yet compatible with [UiExtension](../js-apis-arkui-uiExtension.md) component. As such, using **tabIndex** on a page that contains [UiExtension](../js-apis-arkui-uiExtension.md) may lead to disordered focus navigation.<br>- **tabIndex** >= 0: The component is focusable and can be reached through sequential keyboard navigation.<br>- **tabIndex** < 0 (usually **tabIndex** = -1): The component is focusable, but cannot be reached through sequential keyboard navigation.<br>Default value: **0**<br> **NOTE**<br> **tabIndex** and **focusScopeId** cannot be used together. 46| 47 48## defaultFocus<sup>9+</sup> 49 50defaultFocus(value: boolean) 51 52Specifies whether to set the component as the default focus of the page. 53 54**Atomic service API**: This API can be used in atomic services since API version 11. 55 56**System capability**: SystemCapability.ArkUI.ArkUI.Full 57 58**Parameters** 59 60| Name| Type | Mandatory| Description | 61| ------ | ------- | ---- | ------------------------------------------------------------ | 62| value | boolean | Yes | Whether to set the component as the default focus of the page. This parameter takes effect only when the page is new and accessed for the first time.<br>Default value: **false**<br>**NOTE**<br>The value **true** means to set the component as the default focus, and the value **false** has no effect.<br>If no component on the page has **defaultFocus(true)** set:<br>For API version 11 and earlier, the default focus is on the first focusable non-container component on the page.<br>For API version versions later than 11, the default focus is on the page's root container.<br>If **defaultFocus(true)** is set for multiple components on the page, the first component found in the component tree in-depth traversal is used as the default focus.| 63 64## groupDefaultFocus<sup>9+</sup> 65 66groupDefaultFocus(value: boolean) 67 68Specifies whether to set the component as the default focus of the container. 69 70**Atomic service API**: This API can be used in atomic services since API version 11. 71 72**System capability**: SystemCapability.ArkUI.ArkUI.Full 73 74**Parameters** 75 76| Name| Type | Mandatory| Description | 77| ------ | ------- | ---- | ------------------------------------------------------------ | 78| value | boolean | Yes | Whether to set the component as the default focus of the parent container. This parameter takes effect only when the container is new and obtains focus for the first time.<br>Default value: **false**<br>**NOTE**<br>This parameter must be used together with [tabIndex](#tabindex9). When **tabIndex** is set for a container and **groupDefaultFocus(true)** is set for a child in the container or for the container itself, then when the container obtains focus for the first time through sequential Tab navigation, the focus automatically moves to the specified component. If **groupDefaultFocus(true)** is set for multiple components in the container (including the container itself), the first component found in the component tree in-depth traversal receives the focus.| 79 80## focusOnTouch<sup>9+</sup> 81 82focusOnTouch(value: boolean) 83 84Sets whether the component is focusable on touch. 85 86**Atomic service API**: This API can be used in atomic services since API version 11. 87 88**System capability**: SystemCapability.ArkUI.ArkUI.Full 89 90**Parameters** 91 92| Name| Type | Mandatory| Description | 93| ------ | ------- | ---- | ------------------------------------------------------------ | 94| value | boolean | Yes | Whether the component is focusable on touch.<br>Default value: **false**<br>**NOTE**<br>The component is focusable only when it is touchable.| 95 96## focusBox<sup>12+</sup> 97 98focusBox(style: FocusBoxStyle): T 99 100Sets the system focus box style for the component. 101 102**Atomic service API**: This API can be used in atomic services since API version 12. 103 104**System capability**: SystemCapability.ArkUI.ArkUI.Full 105 106**Parameters** 107 108| Name| Type| Mandatory| Description| 109| ---- | ---- | ---- | ---- | 110| style | [FocusBoxStyle](#focusboxstyle12) | Yes | System focus box style for the component.<br>**NOTE**<br>This style affects only the components that display the system focus frame during focus traversal.| 111 112 113## focusControl<sup>9+</sup> 114 115Implements focus control. 116 117**Atomic service API**: This API can be used in atomic services since API version 11. 118 119### requestFocus<sup>9+</sup> 120 121requestFocus(value: string): boolean 122 123Requests the focus to move to the specified component. It is a global API. This API does not take effect in the current frame; the focus change will occur in the next frame. Use the [requestFocus](../js-apis-arkui-UIContext.md#requestfocus12) API in **FocusController** for immediate effect. 124 125**Atomic service API**: This API can be used in atomic services since API version 11. 126 127**Parameters** 128 129| Name| Type| Mandatory| Description| 130| ----- | ------ | ---- | ---- | 131| value | string | Yes | String bound to the target component using **key(value: string)** or **id(value: string)**.| 132 133**Return value** 134 135| Type| Description| 136| ------- | ---- | 137| boolean | Returns whether the focus is successfully moved to the target component. Returns **true** if the specified component exists and the focus is successfully moved to the target component; returns **false** otherwise.| 138 139> **NOTE** 140> 141> The following components support focus control: [TextInput](ts-basic-components-textinput.md), [TextArea](ts-basic-components-textarea.md), [Search](ts-basic-components-search.md), [Button](ts-basic-components-button.md), [Text](ts-basic-components-text.md), [Image](ts-basic-components-image.md), [List](ts-container-list.md), and [Grid](ts-container-grid.md). Currently, the running effect of the focus event can be displayed only on a real device. 142 143 144## FocusController<sup>12+</sup> 145In the following **clearFocus** and **requestFocus** API examples, you must first use [getFocusController()](../js-apis-arkui-UIContext.md#getfocuscontroller12) in **UIContext** to obtain a **UIContext** instance, and then call the APIs using the obtained instance. 146 147 148### clearFocus<sup>12+</sup> 149 150clearFocus(): void 151 152Clears the focus and forcibly moves the focus to the root container node of the current page. Other nodes on the focus chain all lose focus. 153 154> **NOTE** 155> 156> For details, see [clearFocus](../js-apis-arkui-UIContext.md#clearfocus12). 157 158### requestFocus<sup>12+</sup> 159 160requestFocus(key: string): void 161 162Sets focus on the specified entity node in the component tree based on the component ID. 163 164> **NOTE** 165> 166> For details, see [requestFocus](../js-apis-arkui-UIContext.md#requestfocus12). 167 168## FocusBoxStyle<sup>12+</sup> 169 170**Atomic service API**: This API can be used in atomic services since API version 12. 171 172| Name| Type| Mandatory| Description| 173| ---- | ---- | ---- | ---- | 174| margin | [LengthMetrics](../js-apis-arkui-graphics.md#lengthmetrics12) | No| Distance of the focus box from the component's edge.<br>A positive number indicates the outside, and a negative number indicates the inside. The value cannot be in percentage.| 175| strokeColor | [ColorMetrics](../js-apis-arkui-graphics.md#colormetrics12) | No| Stroke color of the focus frame.| 176| strokeWidth | [LengthMetrics](../js-apis-arkui-graphics.md#lengthmetrics12) | No| Width of the focus frame.<br>Negative numbers and percentages are not supported.| 177 178## focusScopePriority<sup>12+</sup> 179 180focusScopePriority(scopeId: string, priority?: FocusPriority): T 181 182Sets the focus priority of this component in a specified container. It must be used together with **focusScopeId**. 183 184**Atomic service API**: This API can be used in atomic services since API version 12. 185 186**System capability**: SystemCapability.ArkUI.ArkUI.Full 187 188**Parameters** 189 190| Name| Type | Mandatory| Description | 191| ------ | ------- | ---- | ------------------------------------------------------------ | 192| scopeId | string | Yes | ID of the container component where the current component's focus priority takes effect.<br>**NOTE**<br>1. The current component must be within the container identified by **scopeId**, or the container to which the current component belongs must be within the container identified by **scopeId**.<br>2. A component cannot set multiple priorities.<br>3. A container component with **focusScopeId** set cannot have its priority set.| 193| priority | [FocusPriority](#focuspriority12) | No | Focus priority.<br>**NOTE**<br>If **priority** is not set, the component uses the default **AUTO** priority.<br>Impact of the priority on focus traversal and component focus:<br>1. When the container gains focus as a whole (page level switching/focus switching to a focus group/container component requesting focus with **requestFocus**), if there is a component with a priority of **PREVIOUS** within the container, that component gains focus; otherwise, the last focused component does.<br>2. When a container does not gain focus as a whole (using **Tab** or arrow keys in non-focus group scenarios), the highest priority component gets focus on first focus; subsequent focus follows position order regardless of priority.| 194 195### FocusPriority<sup>12+</sup> 196 197**Atomic service API**: This API can be used in atomic services since API version 12. 198 199**System capability**: SystemCapability.ArkUI.ArkUI.Full 200 201| Name | Description | 202| ----------- | --------- | 203| AUTO | Default priority, that is, the focus priority assigned by default.| 204| PRIOR | Priority that indicates the component is prioritized in the container. This level is higher than **AUTO**.| 205| PREVIOUS | Priority of a previously focused node in the container. This level is higher than **PRIOR**.| 206 207## focusScopeId<sup>12+</sup> 208 209focusScopeId(id: string, isGroup?: boolean) 210 211Assigns an ID to this container component and specifies whether the container is a focus group. 212 213**Atomic service API**: This API can be used in atomic services since API version 12. 214 215**System capability**: SystemCapability.ArkUI.ArkUI.Full 216 217**Parameters** 218 219| Name| Type | Mandatory| Description | 220| ------ | ------- | ---- | ------------------------------------------------------------ | 221| id | string | Yes | ID of the current container component.<br>**NOTE**<br>The ID must be unique within a single level page.| 222| isGroup | boolean | No | Whether the current container component is a focus group.<br>**NOTE**<br>Focus groups cannot be nested and should not be configured repeatedly.<br> The focus group and **tabIndex** cannot be used together.<br>The focus group enables the container and its elements to navigate focus according to the focus group rules as follows:<br>1. Only arrow keys are allowed for focus traversal within the focus group; the **Tab** key will move the focus out of the focus group.<br>2. When arrow keys are used to move the focus from outside the focus group to inside, if there is a component with a priority of **PREVIOUS** within the focus group, that component gains focus; otherwise, the last focused component does.| 223 224## Example 225 226### Example 1 227 228This example shows how to use **defaultFocus**, **groupDefaultFocus**, and **focusOnTouch**. 229 230**defaultFocus** sets the bound component as the initial focus of the page after the page is created. **groupDefaultFocus** sets the bound component as the initial focus of the **tabIndex** container after the container is created. **focusOnTouch** sets the bound component to obtain focus upon being clicked. 231 232```ts 233// focusTest.ets 234@Entry 235@Component 236struct FocusableExample { 237 @State inputValue: string = '' 238 239 build() { 240 Scroll() { 241 Row({ space: 20 }) { 242 Column({ space: 20 }) { 243 Column({ space: 5 }) { 244 Button('Group1') 245 .width(165) 246 .height(40) 247 .fontColor(Color.White) 248 .focusOnTouch(true) // The button is focusable on touch. 249 Row({ space: 5 }) { 250 Button() 251 .width(80) 252 .height(40) 253 .fontColor(Color.White) 254 Button() 255 .width(80) 256 .height(40) 257 .fontColor(Color.White) 258 .focusOnTouch(true) // The button is focusable on touch. 259 } 260 Row({ space: 5 }) { 261 Button() 262 .width(80) 263 .height(40) 264 .fontColor(Color.White) 265 Button() 266 .width(80) 267 .height(40) 268 .fontColor(Color.White) 269 } 270 }.borderWidth(2).borderColor(Color.Red).borderStyle(BorderStyle.Dashed) 271 .tabIndex(1) // The column is the initial component to have focus in sequential keyboard navigation. 272 Column({ space: 5 }) { 273 Button('Group2') 274 .width(165) 275 .height(40) 276 .fontColor(Color.White) 277 Row({ space: 5 }) { 278 Button() 279 .width(80) 280 .height(40) 281 .fontColor(Color.White) 282 Button() 283 .width(80) 284 .height(40) 285 .fontColor(Color.White) 286 .groupDefaultFocus(true) // The button obtains focus when its upper-level column is in focus. 287 } 288 Row({ space: 5 }) { 289 Button() 290 .width(80) 291 .height(40) 292 .fontColor(Color.White) 293 Button() 294 .width(80) 295 .height(40) 296 .fontColor(Color.White) 297 } 298 }.borderWidth(2).borderColor(Color.Green).borderStyle(BorderStyle.Dashed) 299 .tabIndex(2) // The column is the second component to have focus in sequential keyboard navigation. 300 } 301 Column({ space: 5 }) { 302 TextInput({placeholder: 'input', text: this.inputValue}) 303 .onChange((value: string) => { 304 this.inputValue = value 305 }) 306 .width(156) 307 .defaultFocus(true) // The <TextInput> component is the initial default focus of the page. 308 Button('Group3') 309 .width(165) 310 .height(40) 311 .fontColor(Color.White) 312 Row({ space: 5 }) { 313 Button() 314 .width(80) 315 .height(40) 316 .fontColor(Color.White) 317 Button() 318 .width(80) 319 .height(40) 320 .fontColor(Color.White) 321 } 322 Button() 323 .width(165) 324 .height(40) 325 .fontColor(Color.White) 326 Row({ space: 5 }) { 327 Button() 328 .width(80) 329 .height(40) 330 .fontColor(Color.White) 331 Button() 332 .width(80) 333 .height(40) 334 .fontColor(Color.White) 335 } 336 Button() 337 .width(165) 338 .height(40) 339 .fontColor(Color.White) 340 Row({ space: 5 }) { 341 Button() 342 .width(80) 343 .height(40) 344 .fontColor(Color.White) 345 Button() 346 .width(80) 347 .height(40) 348 .fontColor(Color.White) 349 } 350 }.borderWidth(2).borderColor(Color.Orange).borderStyle(BorderStyle.Dashed) 351 .tabIndex(3) // The column is the third component to have focus in sequential keyboard navigation. 352 }.alignItems(VerticalAlign.Top) 353 } 354 } 355} 356``` 357Diagrams: 358 359On first-time access, the focus is on the **TextInput** component bound to **defaultFocus**. 360 361 362 363When you press the **Tab** key for the first time, the focus switches to the container that matches **tabIndex(1)** and automatically moves to the component bound to **groupDefaultFocus**. 364 365 366 367When you press the **Tab** key for the second time, the focus switches to the container that matches **tabIndex(2)** and automatically moves to the component bound to **groupDefaultFocus**. 368 369 370 371When you press the **Tab** key for the third time, the focus switches to the container that matches **tabIndex(3)** and automatically moves to the component bound to **groupDefaultFocus**. 372 373 374 375Clicking the component bound to **focusOnTouch** sets the focus on the component and removes the focus indicator. Pressing the Tab key again displays the focus indicator. 376 377 378 379### Example 2 380 381This example shows how to use **focusControl.requestFocus** 382 383to move the focus to the specified component. 384```ts 385// requestFocus.ets 386import { promptAction } from '@kit.ArkUI'; 387 388@Entry 389@Component 390struct RequestFocusExample { 391 @State idList: string[] = ['A', 'B', 'C', 'D', 'E', 'F', 'LastPageId'] 392 @State selectId: string = 'LastPageId' 393 394 build() { 395 Column({ space:20 }){ 396 Row({space: 5}) { 397 Button("id: " + this.idList[0] + " focusable(false)") 398 .width(200).height(70).fontColor(Color.White) 399 .id(this.idList[0]) 400 .focusable(false) 401 Button("id: " + this.idList[1]) 402 .width(200).height(70).fontColor(Color.White) 403 .id(this.idList[1]) 404 } 405 Row({space: 5}) { 406 Button("id: " + this.idList[2]) 407 .width(200).height(70).fontColor(Color.White) 408 .id(this.idList[2]) 409 Button("id: " + this.idList[3]) 410 .width(200).height(70).fontColor(Color.White) 411 .id(this.idList[3]) 412 } 413 Row({space: 5}) { 414 Button("id: " + this.idList[4]) 415 .width(200).height(70).fontColor(Color.White) 416 .id(this.idList[4]) 417 Button("id: " + this.idList[5]) 418 .width(200).height(70).fontColor(Color.White) 419 .id(this.idList[5]) 420 } 421 Row({space: 5}) { 422 Select([{value: this.idList[0]}, 423 {value: this.idList[1]}, 424 {value: this.idList[2]}, 425 {value: this.idList[3]}, 426 {value: this.idList[4]}, 427 {value: this.idList[5]}, 428 {value: this.idList[6]}]) 429 .value(this.selectId) 430 .onSelect((index: number) => { 431 this.selectId = this.idList[index] 432 }) 433 Button("RequestFocus") 434 .width(200).height(70).fontColor(Color.White) 435 .onClick(() => { 436 let res = focusControl.requestFocus(this.selectId) // Move the focus to the component specified by this.selectId. 437 if (res) { 438 promptAction.showToast({message: 'Request success'}) 439 } else { 440 promptAction.showToast({message: 'Request failed'}) 441 } 442 }) 443 } 444 }.width('100%').margin({ top:20 }) 445 } 446} 447``` 448 449Diagrams: 450 451Press the **Tab** key to activate the focus state. 452Below shows how the UI behaves when you request focus for a component that does not exist. 453 454 455 456Below shows how the UI behaves when you request focus for a component that is not focusable. 457 458 459 460Below shows how the UI behaves when you request focus for a focusable component. 461 462 463 464### Example 3 465 466This example demonstrates how to use **focusBox** to change the focus box style of a component, making the focus box red, bold, and with an inner border. 467```ts 468import { ColorMetrics, LengthMetrics } from '@kit.ArkUI' 469 470@Entry 471@Component 472struct RequestFocusExample { 473 build() { 474 Column({ space: 30 }) { 475 Button("small black focus box") 476 .focusBox({ 477 margin: new LengthMetrics(0), 478 strokeColor: ColorMetrics.rgba(0, 0, 0), 479 }) 480 Button("large red focus box") 481 .focusBox({ 482 margin: LengthMetrics.px(20), 483 strokeColor: ColorMetrics.rgba(255, 0, 0), 484 strokeWidth: LengthMetrics.px(10) 485 }) 486 } 487 .alignItems(HorizontalAlign.Center) 488 .width('100%') 489 } 490} 491``` 492 493 494 495 496### Example 4 497 498This example illustrates how to use **focusScopePriority** and **focusScopeId**. 499 500**focusScopePriority** makes the bound component the focus when its container first gains focus. **focusScopeId** makes the bound container component a focus group. 501 502```ts 503// focusTest.ets 504@Entry 505@Component 506struct FocusableExample { 507 @State inputValue: string = '' 508 509 build() { 510 Scroll() { 511 Row({ space: 20 }) { 512 Column({ space: 20 }) { // Labeled as Column1. 513 Column({ space: 5 }) { 514 Button('Group1') 515 .width(165) 516 .height(40) 517 .fontColor(Color.White) 518 Row({ space: 5 }) { 519 Button() 520 .width(80) 521 .height(40) 522 .fontColor(Color.White) 523 Button() 524 .width(80) 525 .height(40) 526 .fontColor(Color.White) 527 } 528 Row({ space: 5 }) { 529 Button() 530 .width(80) 531 .height(40) 532 .fontColor(Color.White) 533 Button() 534 .width(80) 535 .height(40) 536 .fontColor(Color.White) 537 } 538 }.borderWidth(2).borderColor(Color.Red).borderStyle(BorderStyle.Dashed) 539 Column({ space: 5 }) { 540 Button('Group2') 541 .width(165) 542 .height(40) 543 .fontColor(Color.White) 544 Row({ space: 5 }) { 545 Button() 546 .width(80) 547 .height(40) 548 .fontColor(Color.White) 549 Button() 550 .width(80) 551 .height(40) 552 .fontColor(Color.White) 553 .focusScopePriority('ColumnScope1', FocusPriority.PRIOR) // Focuses when Column1 first gains focus. 554 } 555 Row({ space: 5 }) { 556 Button() 557 .width(80) 558 .height(40) 559 .fontColor(Color.White) 560 Button() 561 .width(80) 562 .height(40) 563 .fontColor(Color.White) 564 } 565 }.borderWidth(2).borderColor(Color.Green).borderStyle(BorderStyle.Dashed) 566 } 567 .focusScopeId('ColumnScope1') 568 Column({ space: 5 }) { // Labeled as Column2. 569 TextInput({placeholder: 'input', text: this.inputValue}) 570 .onChange((value: string) => { 571 this.inputValue = value 572 }) 573 .width(156) 574 Button('Group3') 575 .width(165) 576 .height(40) 577 .fontColor(Color.White) 578 Row({ space: 5 }) { 579 Button() 580 .width(80) 581 .height(40) 582 .fontColor(Color.White) 583 Button() 584 .width(80) 585 .height(40) 586 .fontColor(Color.White) 587 } 588 Button() 589 .width(165) 590 .height(40) 591 .fontColor(Color.White) 592 .focusScopePriority('ColumnScope2', FocusPriority.PREVIOUS) // Focuses when Column2 first gains focus. 593 Row({ space: 5 }) { 594 Button() 595 .width(80) 596 .height(40) 597 .fontColor(Color.White) 598 Button() 599 .width(80) 600 .height(40) 601 .fontColor(Color.White) 602 } 603 Button() 604 .width(165) 605 .height(40) 606 .fontColor(Color.White) 607 Row({ space: 5 }) { 608 Button() 609 .width(80) 610 .height(40) 611 .fontColor(Color.White) 612 Button() 613 .width(80) 614 .height(40) 615 .fontColor(Color.White) 616 } 617 }.borderWidth(2).borderColor(Color.Orange).borderStyle(BorderStyle.Dashed) 618 .focusScopeId('ColumnScope2', true) // Column2 is a focus group. 619 }.alignItems(VerticalAlign.Top) 620 } 621 } 622} 623``` 624