• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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![defaultFocus](figures/defaultFocus.png)
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![groupDefaultFocus1](figures/groupDefaultFocus1.png)
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![groupDefaultFocus2](figures/groupDefaultFocus2.png)
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![groupDefaultFocus3](figures/groupDefaultFocus3.png)
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![focusOnTouch](figures/focusOnTouch.png)
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![requestFocus1](figures/requestFocus1.png)
455
456Below shows how the UI behaves when you request focus for a component that is not focusable.
457
458![requestFocus2](figures/requestFocus2.png)
459
460Below shows how the UI behaves when you request focus for a focusable component.
461
462![requestFocus3](figures/requestFocus3.png)
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![focusBox](figures/focusBox.gif)
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