Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs(autofocus): playground examples for setFocus #3258

Merged
merged 11 commits into from
Nov 28, 2023
244 changes: 244 additions & 0 deletions docs/developing/managing-focus.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
---
title: Managing Focus
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

<head>
<title>Managing Focus</title>
<meta
name="description"
content="Learn how to manage focus in Ionic applications using the setFocus API instead of the autofocus attribute."
/>
</head>

## Autofocus Attribute

The `autofocus` attribute is a standard HTML attribute that allows developers to set focus to an element when a page loads. This attribute is commonly used to set focus to the first input element on a page. However, the `autofocus` attribute can cause issues in routing applications when navigating between pages. This is because the `autofocus` attribute will set focus to the element when the page loads, but will not set focus to the element when the page is revisited.

## Managing Focus
averyjohnston marked this conversation as resolved.
Show resolved Hide resolved

Ionic provides a `setFocus` API that allows developers to manually set focus to an element. This API should be used in place of the `autofocus` attribute and called within:
sean-perkins marked this conversation as resolved.
Show resolved Hide resolved

- The `ionViewDidEnter` lifecycle event for routing applications when a page is entered.
- The `didPresent` lifecycle event for overlays when an overlay is presented.
- The `appload` event for vanilla JavaScript applications when the application loads.
- The result of a user gesture or interaction.

### Platform Restrictions

There are platform restrictions you should be aware of when using the `setFocus` API, including:

1. Android requires user interaction before setting focus to an element. This can be as simple as a user tapping on the screen.
2. Interactive elements can only focused a result of a user gesture on Mobile Safari (iOS), such as calling `setFocus` as the result of a button click.

### Basic Usage

The example below demonstrates how to use the `setFocus` API to request focus on an input when the user clicks a button.

import Basic from '@site/static/usage/v7/input/set-focus/index.md';

<Basic />

### Routing

Developers can use the `ionViewDidEnter` lifecycle event to set focus to an element when a page is entered.

````mdx-code-block
<Tabs
groupId="framework"
defaultValue="angular"
values={[
{ value: 'angular', label: 'Angular' },
{ value: 'react', label: 'React' },
{ value: 'vue', label: 'Vue' },
]
}>

<TabItem value="angular">

```ts
/* example.component.ts */
import { Component, ViewChild } from '@angular/core';
import { IonInput } from '@ionic/angular';

@Component({
selector: 'app-example',
templateUrl: './example.component.html',
})
export class ExampleComponent {
@ViewChild('input') input!: IonInput;

ionViewDidEnter() {
this.input.setFocus();
}
}
```
</TabItem>
<TabItem value="react">

```tsx
import React, { useRef } from 'react';
import { IonInput, IonPage, useIonViewDidEnter } from '@ionic/react';

const Home = () => {
const input = useRef<HTMLIonInputElement>(null);

useIonViewDidEnter(() => {
input.current?.setFocus();
});

return (
<IonPage>
<IonInput ref={input} label="setFocus" labelPlacement="floating"></IonInput>
</IonPage>
);
};

export default Home;
```

</TabItem>
<TabItem value="vue">

```html
<template>
<ion-page>
<ion-input ref="input" label="setFocus" labelPlacement="floating"></ion-input>
sean-perkins marked this conversation as resolved.
Show resolved Hide resolved
</ion-page>
</template>

<script setup lang="ts">
import { IonInput, IonPage, onIonViewDidEnter } from '@ionic/vue';
import { ref } from 'vue';

const input = ref();
onIonViewDidEnter(() => {
requestAnimationFrame(() => {
// requestAnimationFrame is currently required due to:
// https://github.com/ionic-team/ionic-framework/issues/24434
input.value.$el.setFocus();
});
});
</script>
```

</TabItem>
</Tabs>
````

### Overlays

Developers can use the `didPresent` lifecycle event to set focus to an element when an overlay is presented.

````mdx-code-block
<Tabs
groupId="framework"
defaultValue="javascript"
values={[
{ value: 'javascript', label: 'Javascript' },
{ value: 'angular', label: 'Angular' },
{ value: 'react', label: 'React' },
{ value: 'vue', label: 'Vue' },
]
}>

<TabItem value="javascript">

```html
<ion-modal>
<ion-input></ion-input>
</ion-modal>

<script>
const modal = document.querySelector('ion-modal');
modal.addEventListener('didPresent', () => {
const input = modal.querySelector('ion-input');
input.setFocus();
});
</script>
```

</TabItem>

<TabItem value="angular">

```ts
/* example.component.ts */
import { Component, ViewChild } from '@angular/core';
import { IonInput } from '@ionic/angular';

@Component({
selector: 'app-example',
templateUrl: './example.component.html',
})
export class ExampleComponent {
@ViewChild('input') input!: IonInput;

onDidPresent() {
this.input.setFocus();
}
}
```

```html
<!-- example.component.html -->
<ion-modal (didPresent)="onDidPresent()">
<ion-input #input></ion-input>
</ion-modal>
```

</TabItem>
<TabItem value="react">

```tsx
import React, { useRef } from 'react';
import { IonInput, IonModal, IonPage } from '@ionic/react';

const Home = () => {
const input = useRef<HTMLIonInputElement>(null);

const onDidPresent = () => {
input.current?.setFocus();
};

return (
<IonPage>
<IonModal onDidPresent={onDidPresent}>
<IonInput ref={input}></IonInput>
</IonModal>
</IonPage>
);
};

export default Home;
```

</TabItem>
<TabItem value="vue">

```html
<template>
<ion-page>
<ion-modal @didPresent="onDidPresent">
<ion-input ref="input"></ion-input>
</ion-modal>
</ion-page>
</template>

<script setup lang="ts">
import { IonInput, IonPage, IonModal } from '@ionic/vue';
import { ref } from 'vue';

const input = ref();

function onDidPresent() {
input.value.$el.setFocus();
}
</script>
```

</TabItem>
</Tabs>
````
1 change: 1 addition & 0 deletions sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ module.exports = {
'developing/hardware-back-button',
'developing/keyboard',
'developing/config',
'developing/managing-focus',
],
},
{
Expand Down
10 changes: 10 additions & 0 deletions static/usage/v7/input/set-focus/angular.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
```html
<ion-list>
<ion-item>
<ion-button (click)="input.setFocus()">Click to set focus</ion-button>
</ion-item>
<ion-item>
<ion-input #input label="Email" labelPlacement="floating"></ion-input>
</ion-item>
</ion-list>
```
33 changes: 33 additions & 0 deletions static/usage/v7/input/set-focus/demo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>setFocus</title>
<link rel="stylesheet" href="../../../common.css" />
<script src="../../../common.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core@7/dist/ionic/ionic.esm.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@7/css/ionic.bundle.css" />
</head>

<body>
<ion-app>
<ion-content>
<ion-list>
<ion-item>
<ion-button onclick="setFocus()">Click to set focus</ion-button>
</ion-item>
<ion-item>
<ion-input label="Email" label-placement="floating"></ion-input>
</ion-item>
</ion-list>
</ion-content>
</ion-app>
<script>
function setFocus() {
const input = document.querySelector('ion-input');
input.setFocus();
}
</script>
</body>
</html>
17 changes: 17 additions & 0 deletions static/usage/v7/input/set-focus/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Playground from '@site/src/components/global/Playground';

import javascript from './javascript.md';
import angular from './angular.md';
import vue from './vue.md';
import react from './react.md';

<Playground
version="7"
code={{
javascript,
vue,
angular,
react,
}}
src="usage/v7/input/set-focus/demo.html"
/>
17 changes: 17 additions & 0 deletions static/usage/v7/input/set-focus/javascript.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
```html
<ion-list>
<ion-item>
<ion-button onclick="setFocus()">Click to set focus</ion-button>
</ion-item>
<ion-item>
<ion-input label="Email" label-placement="floating"></ion-input>
</ion-item>
</ion-list>

<script>
function setFocus() {
const input = document.querySelector('ion-input');
input.setFocus();
}
</script>
```
23 changes: 23 additions & 0 deletions static/usage/v7/input/set-focus/react.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
```tsx
import React, { useRef } from 'react';
import { IonInput, IonItem, IonList, IonButton, IonPage } from '@ionic/react';

const Home = () => {
const input = useRef<HTMLIonInputElement>(null);

return (
<IonPage>
<IonList>
<IonItem>
<IonButton onClick={() => input.current?.setFocus()}>Click to set focus</IonButton>
</IonItem>
<IonItem>
<IonInput ref={input} label="Email" labelPlacement="floating"></IonInput>
</IonItem>
</IonList>
</IonPage>
);
};

export default Home;
```
sean-perkins marked this conversation as resolved.
Show resolved Hide resolved
25 changes: 25 additions & 0 deletions static/usage/v7/input/set-focus/vue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
```html
<template>
<ion-page>
<ion-list>
<ion-item>
<ion-button @click="setFocus">Click to set focus</ion-button>
</ion-item>
<ion-item>
<ion-input ref="input" label="Email" labelPlacement="floating"></ion-input>
sean-perkins marked this conversation as resolved.
Show resolved Hide resolved
</ion-item>
</ion-list>
</ion-page>
</template>

<script setup lang="ts">
import { IonInput, IonItem, IonList, IonButton, IonPage } from '@ionic/vue';
import { ref } from 'vue';

const input = ref();

function setFocus() {
input.value.$el.setFocus();
}
</script>
```
sean-perkins marked this conversation as resolved.
Show resolved Hide resolved