Мобільне сафарі: метод Javascript focus () у полі введення працює лише за допомогою клацання?


75

У мене є таке просте поле введення, як це.

<div class="search">
   <input type="text" value="y u no work"/>
</div>​

І я намагаюся зробити focus()це всередині функції. Отже, всередині випадкової функції (неважливо, яка це функція) у мене є цей рядок ...

$('.search').find('input').focus();

Це чудово працює на будь-якому робочому столі.

Однак це не працює на моєму iPhone. Поле не фокусується, а клавіатура не відображається на моєму iPhone.

Для тестування та для того, щоб показати вам проблему, я зробив короткий зразок:

$('#some-test-element').click(function() {
  $('.search').find('input').focus(); // works well on my iPhone - Keyboard slides in
});

setTimeout(function() {
  //alert('test'); //works
  $('.search').find('input').focus(); // doesn't work on my iPhone - works on Desktop
}, 5000);​

Будь-яка ідея, чому це focus()не працює з функцією очікування на моєму iPhone.

Щоб побачити живий приклад, протестуйте цю скрипку на своєму iPhone. http://jsfiddle.net/Hc4sT/

Оновлення:

Я створив точно такий же випадок, з яким я зараз стикаюся у своєму поточному проекті.

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


може бути , це цілеспрямований, але клавіатура просто не показує (це так на моєму Nokia X6).
11684

1
працює для мене. jsfiddle, який ви вставили, просто сфокусував поле та відкрив клавіатуру на моєму iphone. який iOS ви використовуєте? Я перевірив 5.1.
Krizz

Гаразд, ти маєш рацію, як здається. Я працюю на iOS 5.1.1. Здається, фокус () працює, оскільки я знаю, встановлений input:focus { background:green; }через css на вході. Однак клавіатура для мене не розсувається. Дивіться цей робочий випадок, над яким я зараз працюю. cl.ly/1d210x1W3Y3W Перевірте це на своєму iPhone. Будь-який шанс ковзати по клавіатурі, вибираючи "Пошук" у полі вибору?
matt

1
Я намагаюся багато речей. Я спробував triggerHandler (), focusin (), trigger ("focus"). Я навіть намагався спочатку розмити його, а потім знову додати фокус. Я думаю, що це може бути помилка в webkit. Можливо, вам не пощастило з цим ... Але я спробую ще кілька речей ...
Алекс

1
Я здаюся. Я все пробував. Це просто не те, що ви можете робити у веб-браузері iOS Safari. Сподіваємось, Apple вносить зміни, щоб дозволити це, оскільки я, звичайно, не вважаю це "особливістю"! ха-ха, я не знаю, чому ви можете так легко розмити () поле, але не можете встановити на нього фокус. Для мене це не має сенсу.
Алекс

Відповіді:


77

Насправді, хлопці, є спосіб. Я потужно намагався це зрозуміти для [ПОСИЛАННЯ ВИДАЛЕНО] (спробуйте на iPhone або iPad).

В основному, Safari на сенсорних пристроях скупий, коли справа стосується focus()введення текстових полів. Навіть деякі браузери для настільних ПК справляються краще, якщо ви це робите click().focus(). Але дизайнери Safari на сенсорних пристроях зрозуміли, що це дратує користувачів, коли клавіатура постійно піднімається, тому вони зробили фокус лише на таких умовах:

1) Користувач десь клацнув і focus()був викликаний під час виконання клікової події. Якщо ви робите виклик AJAX, то ви повинні робити це синхронно, наприклад, із застарілою (але все ще доступною) $.ajax({async:false})опцією в jQuery.

2) Більше того - і цей мене деякий час зайняв - focus()все ще, здається, не працює, якщо на той момент зосереджено якесь інше текстове поле. У мене була кнопка "Перейти", яка робила AJAX, тому я спробував розмити текстове поле у touchstartвипадку кнопки "Перейти", але це просто змусило клавіатуру зникнути і перемістило область перегляду, перш ніж я мав можливість завершити натискання кнопки "Перейти" . Нарешті, я спробував розмити текстове поле на touchendподії кнопки Go, і це спрацювало як шарм!

Коли ви поєднуєте №1 та №2 разом, ви отримуєте чарівний результат, який відрізнятиме ваші форми входу від усіх безглуздих веб-форм входу, розміщуючи фокус у полях для паролів, і зробить їх більш рідними. Насолоджуйтесь! :)


2
Це звучить дуже розумно, але мені важко "скласти №1 і №2". У моєму додатку, використовуючи jQueryMobile, коли користувач натискає елемент списку на сторінці A, відкривається нова сторінка B, яка має лише один видимий вхід. Я хочу, щоб клавіатура автоматично відкривалася для цього введення. Ви хочете сказати, що я повинен розмістити виклик .focus () десь у обробнику кліків на сторінці A?
Wytze

1
Дякуємо, що заощадили мій час. Це було так неочевидно знайти.
vogdb

4
Я зміг це отримати! Замість того , щоб розмитість і фокусування елемента, я просто додав event.preventDefault()на touchstart. Зверніть увагу, що це заважає виконувати клацання ng, тому мені довелося додати свою чітку логіку тексту всередину обробника подій та$scope.$apply()
ErikAGriffin

2
Я виявив, що просто запуск input.focus()від обробника клацання кнопки спрацював. Safari на iPhone 5 та iPad. Якби у мене був focus()setTimeout, це не спрацювало. Тож, можливо, саме це означає "синхронно", @Michael.
Дан Даскалеску,

1
Дякуємо за розміщення фактичного рішення. Розмиття попереднього елемента в touchend + налаштування фокусу на подію кліку все ще працює в iOS10.
Кирило Е.

18

Вбудована реалізація відповіді WunderBart у javascript.

function onClick() {

  // create invisible dummy input to receive the focus first
  const fakeInput = document.createElement('input')
  fakeInput.setAttribute('type', 'text')
  fakeInput.style.position = 'absolute'
  fakeInput.style.opacity = 0
  fakeInput.style.height = 0
  fakeInput.style.fontSize = '16px' // disable auto zoom

  // you may need to append to another element depending on the browser's auto 
  // zoom/scroll behavior
  document.body.prepend(fakeInput)

  // focus so that subsequent async focus will work
  fakeInput.focus()

  setTimeout(() => {

    // now we can focus on the target input
    targetInput.focus()

    // cleanup
    fakeInput.remove()
    
  }, 1000)

}

Інші посилання: Вимкніть автоматичне масштабування введення тегу "Текст" - Safari на iPhone


1
Це спрацювало для мене. Якщо ви хочете, щоб клавіатура не відображалася на фокусі fakeInput, додайте до неї readonly = "true".
Браніслав Куліга,

Єдине рішення, яке працює. requestAnimationFrame мені достатньо і виключає затримку.
Томас

Цю відповідь слід розподілити серед інших подібних питань, що стосуються створення setTimeout + фокусу на браузерах Safari. Це єдине, що справді працює!
gojun

Дякую! Це єдине рішення, яке спрацювало для мене.
Offek

5

Нещодавно я стикався з тим самим питанням. Я знайшов рішення, яке, мабуть, працює для всіх пристроїв. Ви не можете виконати асинхронний фокус програмно, але ви можете переключити фокус на цільовий вхід, коли якийсь інший вхід вже сфокусований. Отже, що вам потрібно зробити, це створити, приховати, додати до DOM і зосередити фальшивий вхід на події тригера і, коли дія асинхронізації завершиться, просто знову викликати фокус на цільовий вхід. Ось приклад фрагмента - запустіть його на своєму мобільному телефоні.

редагувати:

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

var $triggerCheckbox = $("#trigger-checkbox");
var $targetInput = $("#target-input");

// Create fake & invisible input
var $fakeInput = $("<input type='text' />")
  .css({
    position: "absolute",
    width: $targetInput.outerWidth(), // zoom properly (iOS)
    height: 0, // hide cursor (font-size: 0 will zoom to quarks level) (iOS)
    opacity: 0, // make input transparent :]
  });

var delay = 2000; // That's crazy long, but good as an example

$triggerCheckbox.on("change", function(event) {
  // Disable input when unchecking trigger checkbox (presentational purpose)
  if (!event.target.checked) {
    return $targetInput
      .attr("disabled", true)
      .attr("placeholder", "I'm disabled");
  }

  // Prepend to target input container and focus fake input
  $fakeInput.prependTo("#container").focus();

  // Update placeholder (presentational purpose)
  $targetInput.attr("placeholder", "Wait for it...");

  // setTimeout, fetch or any async action will work
  setTimeout(function() {

    // Shift focus to target input
    $targetInput
      .attr("disabled", false)
      .attr("placeholder", "I'm alive!")
      .focus();

    // Remove fake input - no need to keep it in DOM
    $fakeInput.remove();
  }, delay);
});
label {
  display: block;
  margin-top: 20px;
}

input {
  box-sizing: border-box;
  font-size: inherit;
}

#container {
  position: relative;
}

#target-input {
  width: 250px;
  padding: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div id="container">
  <input type="text" id="target-input" placeholder="I'm disabled" />

  <label>
    <input type="checkbox" id="trigger-checkbox" />
    focus with setTimetout
   </label>
</div>


Це найкраще рішення, яке я знайшов на даний момент.
anwerso

Ви можете fontSize: '16px'вимкнути автоматичне масштабування. Також див. Мою відповідь нижче щодо реалізації власного js.
Рейн Ревере

3

У мене є форма пошуку з піктограмою, яка очищає текст при натисканні. Однак проблема (на мобільних пристроях та планшетах) полягала в тому, що клавіатура згортається / приховується, оскільки clickвидалена подія focusбула видалена з input.

введення тексту з піктограмою закриття

Мета : після очищення форми пошуку (клацання / натискання на піктограму x) залишайте клавіатуру видимою !

Для цього подайте заявку stopPropagation()на подію приблизно так:

function clear ($event) {
    $event.preventDefault();
    $event.stopPropagation();
    self.query = '';
    $timeout(function () {
        document.getElementById('sidebar-search').focus();
    }, 1);
}

І форма HTML:

<form ng-controller="SearchController as search"
    ng-submit="search.submit($event)">
        <input type="search" id="sidebar-search" 
            ng-model="search.query">
                <span class="glyphicon glyphicon-remove-circle"
                    ng-click="search.clear($event)">
                </span>
</form>

3

Це рішення працює добре, я протестував на своєму телефоні:

document.body.ontouchend = function() { document.querySelector('[name="name"]').focus(); };

насолоджуватися


Це ПОЧТИ працює. Але це не масштабується до двох полів дати, і це робить сторінку трохи хитромудрою
Шейн

Дякую! Перепробував всілякі речі, і це нарешті зробило це для мене, де з якихось причин document.getElementById цього не зробив
daamsie

2

Мені вдалося змусити його працювати з таким кодом:

event.preventDefault();
timeout(function () {
    $inputToFocus.focus();
}, 500);

Я використовую AngularJS, тому я створив директиву, яка вирішила мою проблему:

Директива:

angular.module('directivesModule').directive('focusOnClear', [
    '$timeout',
    function (timeout) {
        return {
            restrict: 'A',
            link: function (scope, element, attrs) {
                var id = attrs.focusOnClear;
                var $inputSearchElement = $(element).parent().find('#' + id);
                element.on('click', function (event) {
                    event.preventDefault();
                    timeout(function () {
                        $inputSearchElement.focus();
                    }, 500);
                });
            }
        };
    }
]);

Як користуватися директивою:

<div>
    <input type="search" id="search">
    <i class="icon-clear" ng-click="clearSearchTerm()" focus-on-clear="search"></i>
</div>

Схоже, ви використовуєте jQuery, тому я не знаю, чи директива допомагає.


2
Відомо, що це не працює на ios-сафарі. Safari спеціально розмежовує події, породжені взаємодією користувача, та згенеровані суто з самого js, і не доставлятиме фокус / вибір подій таким чином.
Ben Wheeler

Ну, це неправда, і я використовую це в своїх додатках з Кордовою / Фонегапом ... І чому голосувати проти? Чим моя відповідь відрізняється від інших? За винятком того, що я створив директиву Angular, яку ви можете використовувати.
martinmose

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

Привіт @Maarten дякую за ваш коментар, ви маєте на увазі 500, які я встановив на тайм-аут? :)
martinmose

Гаразд, я перевірю це. Дякую :).
martinmose

1

ОНОВЛЕННЯ

Я теж спробував це, але безрезультатно:

$(document).ready(function() {
$('body :not(.wr-dropdown)').bind("click", function(e) {
    $('.test').focus();
})
$('.wr-dropdown').on('change', function(e) {
    if ($(".wr-dropdow option[value='/search']")) {
        setTimeout(function(e) {
            $('body :not(.wr-dropdown)').trigger("click");
        },3000)         
    } 
}); 

});

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

Спробуйте цей рядок коду у вашій функції SetTimeOut для вашої події кліку:

document.myInput.focus();

myInput корелює з атрибутом name вхідного тегу.

<input name="myInput">

І використовуйте цей код, щоб розмити поле:

document.activeElement.blur();

Дякую, див. Моє оновлення у моєму запитанні. Здається, я не можу змусити це працювати з вашою пропозицією.
matt


-3

Будь ласка, спробуйте використовувати on-tap замість ng-click події. У мене було це питання. Я вирішив це, зробивши свою кнопку очищення вікна пошуку всередині мітки форми пошуку і замінив натискання кнопки очищення на натискання. Зараз це чудово працює.

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