Як встановити фокус на полі введення після візуалізації?


629

Яким чином реагує фокусування на певному текстовому полі після надання компонента?

Документація, схоже, пропонує використання посилань, наприклад:

Встановіть ref="nameInput"моє поле введення у функції візуалізації, а потім зателефонуйте:

this.refs.nameInput.getInputDOMNode().focus(); 

Але де мені це закликати? Я спробував кілька місць, але не можу змусити його працювати.

Відповіді:


668

Ви повинні зробити це в componentDidMountі refs callbackзамість цього. Щось на зразок цього

componentDidMount(){
   this.nameInput.focus(); 
}

class App extends React.Component{
  componentDidMount(){
    this.nameInput.focus();
  }
  render() {
    return(
      <div>
        <input 
          defaultValue="Won't focus" 
        />
        <input 
          ref={(input) => { this.nameInput = input; }} 
          defaultValue="will focus"
        />
      </div>
    );
  }
}
    
ReactDOM.render(<App />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react-dom.js"></script>
<div id="app"></div>


107
Це правильна відповідь, але це не спрацювало для мене, оскільки мій компонент спочатку нічого не дає, доки не буде натиснуто ще одну кнопку. Це означало, що він уже встановлений, тому мені довелося додати this.refs.nameInput.getDOMNode (). Focus (); в компонентDidUpdate замість компонентаDidMount.
Дейв

9
Чому при виклику element.focus () він ставить курсор на початку введення? Я бачив цю помилку (що я вважаю) помилкою в моєму додатку, в хромі, насправді в <textarea>, і тепер перевіряю ваші демонстрації, тут це те саме.
davnicwil

15
Попередження: React.findDOMNode застаріло. Будь ласка, використовуйте ReactDOM.findDOMNode від requ ('react-dom') замість цього.
Андре Пена

5
@HuwDavies Я думаю, ви зробили б це за допомогою посилання Атрибут зворотного виклику на <input>елементі. Щось на кшталт<input ref={ (component) => ReactDOM.findDOMNode(component).focus() } />
герман

5
Чому ми не просто використовуємо ref = {(input) => {input.focus ()}} ? Це рішення добре працює для мене.
HCLiu

840

@ Відповідь Dhiraj правильна, і для зручності ви можете використовувати опору autoFocus для автоматичного фокусування входу при встановленні:

<input autoFocus name=...

Зауважте, що в jsx це autoFocus( величина капіталу F) на відміну від звичайного старого html, який не відрізняється від регістру.


95
Зауважте, що у jsx його автоматичний F ocus (з великої літери F) на відміну від простого старого html, який не враховує регістр.
prauchfuss

8
Дуже добре, потрапив сюди після довгого безрезультатного пошуку :) FYI - я закінчив використовувати React.DOM.input ({type: 'text', defaultValue: content, autoFocus: true, onFocus: function (e) {e.target. select ();}})
mlo55

4
Я вважаю, що автофокус працює лише на візуалізації на першій сторінці. Дивіться codepen.io/ericandrewlewis/pen/PbgwqJ?editors=1111, вхід повинен бути сфокусований через 3 секунди.
Ерік Ендрю Льюїс

45
+1 для цього методу. Варто зазначити, що для цього не використовується просто ненадійний autofocusатрибут HTML5 , він фактично використовується focus()на монтажі DOM,react-dom тому він досить надійний.
Аарон Білл

3
Не тільки "для зручності", але і якщо ваш компонент є функціональним компонентом.
phillyslick

167

Станом на React 0.15 , найбільш стислий метод:

<input ref={input => input && input.focus()}/>

5
Це також обробляє сценарії поза початковим візуалізацією, тоді як використання автофокусу - це не так.
Метт Стеннетт

питання, коли введення буде помилковим? Я маю на увазі вираз усередині функції стрілки.
JaeGeeTee

2
@JaeGeeTee це недійсне значення, поки компонент не змонтований та / або після його відключення (я точно не пам’ятаю, що в цьому випадку).
Ілля Семенов

12
Єдине питання в цьому полягає в тому, що він зосереджує вклад на будь-яке повторне відображення, яке може бути небажаним ..
Ярослав Бенк

У моєму випадку не працює (використовуючи вхідний компонент Ant Design )
vsync

118

Зосередьтеся на кріпленні

Якщо ви просто хочете сфокусувати елемент, коли він монтується (спочатку виводиться), просте використання атрибута автофокусування буде просто.

<input type="text" autoFocus />

Динамічний фокус

для динамічного управління фокусом використовуйте загальну функцію, щоб приховати деталі реалізації від своїх компонентів.

React 16.8 + функціональний компонент - використовуйте гачок фокусування

const FocusDemo = () => {

    const [inputRef, setInputFocus] = useFocus()

    return (
        <> 
            <button onClick={setInputFocus} >
               FOCUS
            </button>
            <input ref={inputRef} />
        </>
    )

}
const useFocus = () => {
    const htmlElRef = useRef(null)
    const setFocus = () => {htmlElRef.current &&  htmlElRef.current.focus()}

    return [ htmlElRef, setFocus ] 
}

Повна демонстрація

Реагуйте 16.3 + компоненти класу - використовуйте Фокус

class App extends Component {
  constructor(props){
    super(props)
    this.inputFocus = utilizeFocus()
  }

  render(){
    return (
      <> 
          <button onClick={this.inputFocus.setFocus}>
             FOCUS
          </button>
          <input ref={this.inputFocus.ref}/>
      </>
    )
  } 
}
const utilizeFocus = () => {
    const ref = React.createRef()
    const setFocus = () => {ref.current &&  ref.current.focus()}

    return {setFocus, ref} 
}

Повна демонстрація


2
Ця відповідь містить правильний підхід для React Hooks. Супер! Він не вибирає прапорець як є у TypeScript, але один (некрасивий) спосіб змусити його працювати: (1) (htmlElRef.current as any).focus()та (2) return {htmlElRef, setFocus}замість масиву.
Ахмед Фасіх

@AhmedFasih, я знаю, що ви говорите, але я думаю, що це тема не виходить за межі. Якщо ви повертаєте об'єкт, це ускладнює контроль за назвою змінної, що може бути проблемою, якщо ви хочете використовувати useFocusдля більш ніж одного елемента.
Бен Карп

8
Тут useFocusнаписано в Typescript. gist.github.com/carpben/de968e377cbac0ffbdefe1ab56237573
Ben Carp

2
Супер соковите, дякую !!! Нічого собі, я не знав про твердження про суть (що у as constвас є), дуже освітні!
Ахмед Фасіх

@BenCarp Невелика пропозиція щодо гачків, можливо, краще поставити setна друге місце, як const [inputRef, setInputFocus] = useFocus(). Ці збіги використовуютьState more. Спочатку об'єкт, потім сетер цього об’єкта
Рубанов

59

Якщо ви просто хочете зробити автофокус в React, це просто.

<input autoFocus type="text" />

Хоча, якщо ви просто хочете знати, куди поставити цей код, відповідь є в компонентDidMount ().

v014.3

componentDidMount() {
    this.refs.linkInput.focus()
}

У більшості випадків ви можете приєднати посилання на вузол DOM і взагалі не уникати використання findDOMNode.

Прочитайте документи API тут: https://facebook.github.io/react/docs/top-level-api.html#reactdom.finddomnode


9
І не забудьте скористатися цим F! (Зверніть увагу на себе та інших, а не на відповідь).
2540625

29

React 16.3 додав новий зручний спосіб вирішити це, створивши ref в конструкторі компонента та використовуючи його, як показано нижче:

class MyForm extends Component {
  constructor(props) {
      super(props);

      this.textInput = React.createRef();
  }

  componentDidMount() {
    this.textInput.current.focus(); // one important change here is that we need to access the element via current.
  }

  render() {
    // instead of using arrow function, the created ref can be used directly.
    return(
      <div>
        <input ref={this.textInput} />
      </div>
    );
  }
}

Для отримання більш детальної інформації ви можете переглянути цю статтю в блозі React

Оновлення:

Починаючи з React 16.8 , useRefгак можна використовувати в функціональних компонентах для досягнення того ж результату:

import React, { useEffect, useRef } from 'react';

const MyForm = () => {
  const textInput = useRef(null);

  useEffect(() => {
    textInput.current.focus();
  }, []);

  return (
    <div>
      <input ref={textInput} />
    </div>
  );
};

26

Я щойно зіткнувся з цим питанням, і я використовую синтаксис react 15.0.1 15.0.2, і я використовую синтаксис ES6, і я не зовсім отримав те, що мені потрібно з інших відповідей, оскільки v.15 випав тижнів тому і деякі this.refsвластивості були застарілі та вилучені .

Загалом, мені було потрібно:

  1. Сфокусуйте перший елемент введення (поля), коли компонент кріпиться
  2. Сфокусуйте перший елемент (поля) з помилкою (після надсилання)

Я використовую:

  • Реактивний контейнер / презентаційний компонент
  • Редукс
  • React-Router

Сфокусуйте перший елемент введення

Я використовував autoFocus={true}на першій <input />сторінці, так що, коли компонент змонтується, він отримає фокус.

Сфокусуйте перший елемент введення за допомогою помилки

Це зайняло більше часу і було більш звивистим. Я зберігаю код, який не стосується рішення для стислості.

Магазин Redux / Штат

Мені потрібен глобальний стан, щоб знати, чи слід встановлювати фокус, і відключати його, коли він був встановлений, тому я не продовжую повторно налаштовувати фокус, коли компоненти повторно рендеруються (я буду використовувати componentDidUpdate()для перевірки встановлення фокусу. )

Це може бути розроблено так, як ви вважаєте, що підходить для вашої програми.

{
    form: {
        resetFocus: false,
    }
}

Компонент контейнера

Для resetfocusочищення властивості для компонента потрібно встановити властивість та callBack, якщо він закінчиться, налаштувавши фокус на себе.

Також зауважте, я організував Моїх творців дій в окремі файли, в основному завдяки мій проект досить великий, і я хотів розбити їх на більш керовані шматки.

import { connect } from 'react-redux';
import MyField from '../presentation/MyField';
import ActionCreator from '../actions/action-creators';

function mapStateToProps(state) {
    return {
        resetFocus: state.form.resetFocus
    }
}

function mapDispatchToProps(dispatch) {
    return {
        clearResetFocus() {
            dispatch(ActionCreator.clearResetFocus());
        }
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(MyField);

Компонент презентації

import React, { PropTypes } form 'react';

export default class MyField extends React.Component {
    // don't forget to .bind(this)
    constructor(props) {
        super(props);
        this._handleRef = this._handleRef.bind(this);
    }

    // This is not called on the initial render so
    // this._input will be set before this get called
    componentDidUpdate() {
        if(!this.props.resetFocus) {
            return false;
        }

        if(this.shouldfocus()) {
            this._input.focus();
            this.props.clearResetFocus();
        }
    }

    // When the component mounts, it will save a 
    // reference to itself as _input, which we'll
    // be able to call in subsequent componentDidUpdate()
    // calls if we need to set focus.
    _handleRef(c) {
        this._input = c;
    }

    // Whatever logic you need to determine if this
    // component should get focus
    shouldFocus() {
        // ...
    }

    // pass the _handleRef callback so we can access 
    // a reference of this element in other component methods
    render() {
        return (
            <input ref={this._handleRef} type="text" />
        );
    }
}

Myfield.propTypes = {
    clearResetFocus: PropTypes.func,
    resetFocus: PropTypes.bool
}

Огляд

Загальна думка полягає в тому, що кожне поле форми, яке могло б мати помилку і бути зосередженим, має перевірити себе, і якщо потрібно встановити фокус на себе.

Існує бізнес-логіка, яка повинна відбутися, щоб визначити, чи дане поле є правильним полем, на яке слід зосередити увагу. Це не відображається, оскільки це залежатиме від конкретної програми.

Під час подання форми для цієї події потрібно встановити прапор глобального фокусу resetFocusна "true". Тоді, як кожен компонент оновлює себе, він побачить, що він повинен перевірити, чи отримує він фокус, а якщо це так, відправте подію, щоб скинути фокус, щоб інші елементи не повинні продовжувати перевірку.

редагування Як бічна примітка, я мав свою бізнес-логіку у файлі "утиліти", і я просто експортував метод і викликав його в кожномуshouldfocus() методі.

Ура!


25

Документи React тепер мають розділ для цього. https://facebook.github.io/react/docs/more-about-refs.html#the-ref-callback-attribute

 render: function() {
  return (
    <TextInput
      ref={function(input) {
        if (input != null) {
          input.focus();
        }
      }} />
    );
  },

1
Я думаю, що це хороший спосіб зробити це для цього конкретного сценарію.
fabio.sussetto

Мені не потрібно було autofocusгоріти, просто шукав, щоб елемент залишався зосередженим при введенні значення. Це прекрасно працювало за тим сценарієм. (використовуючи реакцію 15)
Метт Паррілья

13

Це вже не найкраща відповідь. Станом на v0.13, він this.refsможе бути недоступним до componentDidMount()запуску ПІСЛЯ , у деяких випадкових випадках.

Просто додайте autoFocusтег у своє поле введення, як показав FakeRainBrigand вище.


4
Кілька <input autofocus>полів не будуть вести себе добре
ᆼ ᆺ ᆼ

4
Звичайно, ні. Лише один фокус на сторінці. Якщо у вас кілька автофокусів, ви повинні перевірити свій код та наміри.
GAEfan

2
@ Dave питання <input>
стосувався

1
Чи є спосіб автофокусування змусити клавіатуру iOS також відкритись?
Remi Sture

1
@RemiSture ті ж запитання. Хтось має вирішення цієї проблеми?
Nam Lê Quý

12

Реф. @ Коментар Дейва на відповідь @ Dhiraj; альтернативою є використання функцій зворотного виклику атрибута ref на елементі, що рендерується (після того, як компонент спочатку надає):

<input ref={ function(component){ React.findDOMNode(component).focus();} } />

Більше інформації


Коли я спробував це, я отримав:Uncaught TypeError: Cannot read property 'focus' of null
reectrix

1
Ви повинні перевірити параметр, він буде нульовим, коли компонент не змонтований. Так просто component && React.findDomNode.... Детальніше про це читайте тут: facebook.github.io/react/docs/…
Per Wiklander

12

Це правильний спосіб, як зробити автофокус. Якщо ви використовуєте зворотний дзвінок замість рядка як значення ref, він автоматично викликається. Ви отримали довідку, ніж не потрібно торкатися до DOM за допомогоюgetDOMNode

render: function() {
  return <TextInput ref={(c) => this._input = c} />;
},
componentDidMount: function() {
  this._input.focus();
},

2
як щодо контрольованої форми?
піксель 67

@ pixel67 Також. Ви можете встановити посилання на елементи, але і на компоненти. Але ви повинні знати про це, працюючи з ним. Тож ви не спробуєте отримати доступ до .value введення, якщо ви встановите посилання на React.Component, що обертає вхід html.
Павло Хасала

10

Зауважте, що жодна з цих відповідей не працювала для мене із компонентом material-u TextField . Per Як встановити фокус на materialUI TextField? Мені довелося перестрибувати кілька обручів, щоб це спрацювало:

const focusUsernameInputField = input => {
  if (input) {
    setTimeout(() => {input.focus()}, 100);
  }
};

return (
  <TextField
    hintText="Username"
    floatingLabelText="Username"
    ref={focusUsernameInputField}
  />
);

2
Схоже, якщо ваш компонент анімує, виклик до focus()цього потрібно затримати до кінця анімації.
adriendenat

9

Ви можете встановити цей виклик методу всередині функції візуалізації. Або всередині методу життєвого циклу,componentDidUpdate


1
компонентDidUpdate - це те, що працювало в моєму випадку. Мені потрібно було встановити фокус на конкретній кнопці після виклику візуалізації.
FariaC

8

Вам не потрібно getInputDOMNode ?? в цьому випадку...

Просто отримайте refі focus()це, коли компонент встановлюється - компонентDidMount ...

import React from 'react';
import { render } from 'react-dom';

class myApp extends React.Component {

  componentDidMount() {
    this.nameInput.focus();
  }

  render() {
    return(
      <div>
        <input ref={input => { this.nameInput = input; }} />
      </div>
    );
  }

}

ReactDOM.render(<myApp />, document.getElementById('root'));

5

Автофокус найкраще працював для мене. Мені потрібно було змінити текст на вхід із цим текстом подвійним клацанням, так що я закінчив:

<input autoFocus onFocus={this.setCaretToEnd} value={this.state.editTodo.value} onDoubleClick={this.updateTodoItem} />

ПРИМІТКА. Щоб вирішити проблему, де React розміщує каре на початку тексту, використовуйте цей метод:

setCaretToEnd(event) {
    var originalText = event.target.value;
    event.target.value = '';
    event.target.value = originalText;
}

Знайдено тут: https://coderwall.com/p/0iz_zq/how-to-put-focus-at-the-end-of-an-input-with-react-js


3

У мене така ж проблема, але у мене є і анімація, тому колега пропонує скористатися window.requestAnimationFrame

це атрибут ref мого елемента:

ref={(input) => {input && window.requestAnimationFrame(()=>{input.focus()})}}

2

Попередження: ReactDOMComponent: не отримуйте доступу до .getDOMNode () вузла DOM; натомість використовуйте вузол безпосередньо. Цей вузол DOM був наданий користувачем App.

Має бути

componentDidMount: function () {
  this.refs.nameInput.focus();
}

2

Найпростіша відповідь - додати ref = "деяке ім'я" у вхідний текстовий елемент та зателефонувати нижче.

componentDidMount(){
   this.refs.field_name.focus();
}
// here field_name is ref name.

<input type="text" ref="field_name" />

2

Щоб перемістити фокус на новостворений елемент, ви можете зберігати ідентифікатор елемента у стані та використовувати його для встановлення autoFocus. напр

export default class DefaultRolesPage extends React.Component {

    addRole = ev => {
        ev.preventDefault();
        const roleKey = this.roleKey++;
        this::updateState({
            focus: {$set: roleKey},
            formData: {
                roles: {
                    $push: [{
                        id: null,
                        name: '',
                        permissions: new Set(),
                        key: roleKey,
                    }]
                }
            }
        })
    }

    render() {
        const {formData} = this.state;

        return (
            <GridForm onSubmit={this.submit}>
                {formData.roles.map((role, idx) => (
                    <GridSection key={role.key}>
                        <GridRow>
                            <GridCol>
                                <label>Role</label>
                                <TextBox value={role.name} onChange={this.roleName(idx)} autoFocus={role.key === this.state.focus}/>
                            </GridCol>
                        </GridRow>
                    </GridSection>
                ))}
            </GridForm>
        )
    }
}

Таким чином, жодне з текстових полів не фокусується на завантаженні сторінки (як я хочу), але коли ви натискаєте кнопку "Додати", щоб створити нову запис, то ця нова запис набуває фокус.

Оскільки autoFocusвін не «запускається» знову, якщо компонент не буде перероблений, мені не доведеться турбуватись про знеструмлення this.state.focus(тобто він не буде продовжувати красти фокус назад, коли я оновлюю інші стани).


1

Прочитайте майже всю відповідь, але не бачите getRenderedComponent().props.input

Встановіть відповіді для введення тексту

this.refs.username.getRenderedComponent().props.input.onChange('');


Будь ласка, додатково роз’ясніть свою відповідь у контексті їх коду.
Джиммі Сміт

1

Спробувавши безліч варіантів вище, не маючи успіху, я виявив, що це було так, як я був, disablingі тоді enablingвхід, який призвів до втрати уваги.

У мене була опора, sendingAnswerяка б відключила Input під час опитування бекенду.

<Input
  autoFocus={question}
  placeholder={
    gettingQuestion ? 'Loading...' : 'Type your answer here...'
  }
  value={answer}
  onChange={event => dispatch(updateAnswer(event.target.value))}
  type="text"
  autocomplete="off"
  name="answer"
  // disabled={sendingAnswer} <-- Causing focus to be lost.
/>

Як тільки я вийняв відключений опору, все почало працювати знову.


0

Оновлену версію ви можете перевірити тут

componentDidMount() {

    // Focus to the input as html5 autofocus
    this.inputRef.focus();

}
render() {
    return <input type="text" ref={(input) => { this.inputRef = input }} />
})

0

Оскільки причин цієї помилки дуже багато, я думав, що також опублікую проблему, з якою зіткнувся. Для мене проблема полягала в тому, що я видавав свої дані як вміст іншого компонента.

export default ({ Content }) => {
  return (
  <div className="container-fluid main_container">
    <div className="row">
      <div className="col-sm-12 h-100">
        <Content />                                 // I rendered my inputs here
      </div>
    </div>
  </div>
  );
}

Ось як я назвав вищевказаний компонент:

<Component Content={() => {
  return (
    <input type="text"/>
  );
}} />

0

Відповідно до оновленого синтаксису, ви можете використовувати this.myRref.current.focus()

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.