Ошибка выбора текста в Mobile Safari с iframes и ontouchstart

В моем веб-приложении у меня есть iframe с различными z-индексами. Я обнаруживаю события touchstart для некоторых элементов в iframe. Однако, если у меня есть поле ввода текста, перекрывающее элемент, захватывающий touchstart, поле ввода начинает вести себя беспорядочно: второе нажатие в поле не фокусирует его, вы не можете выбрать какой-либо текст, но вы можете ввести в текстовом поле.

Похоже, единственным решением может быть остановка захвата событий touchstart на фоновую рамку. Я бы предпочел решение, подобное прозрачному div, для захвата событий в качестве посредника, но я еще не получил этого. Существуют ли другие способы обхода?

Пример страницы jsfiddle, но вот код:

<!DOCTYPE html> 
<html> 
    <head> 
        <style type='text/css'> 
            iframe {
                position:absolute;
            }
            #background {
                border: solid 3px red;
                z-index:1;
                width: 20em;
                height: 20em;
            }
            #foreground, #foreground2 {
                border: solid 2px yellow;
                z-index:2;
                top: 15em;
                height: 5em;
            }
            #foreground2 {
                top: 22em;
            }
        </style> 

        <script type='text/javascript'> 
            window.onload=function(){
                document.getElementById("foreground").contentDocument.write("<input type='text' value='text'/><input type='text'/>");
                document.getElementById("foreground2").contentDocument.write("<input type='text' value='text'/><input type='text'/>");

                document.getElementById("background").addEventListener("touchstart", function() {
                    console.log("touch");
                });
            }
        </script> 
    </head> 
    <body> 
        <iframe id=background></iframe> 
        <iframe id=foreground></iframe> 
        <iframe id=foreground2></iframe> 
    </body>
</html> 

Ответ 1

Пока этот поток довольно старый, я просто хотел подтвердить, что эта ошибка все еще сохраняется в последней версии iOS (8.4) на всех устройствах (iPhone/iPad).

После некоторого тестирования я могу подтвердить, что он всегда появляется при следующих обстоятельствах:

  • При использовании HTML-тега iframe/object/embed для отображения содержимого HTML в сочетании с привязками событий javascript touch touch (touchstart/touchhend и т.д.) к элементам внутри этого содержимого HTML. Когда пользователь коснется входного элемента внутри этого тега iframe/object/embed во второй раз (иногда больше), все, что он пытается ввести с помощью экранной клавиатуры, больше не появляется внутри поля ввода. (интересно, что слова, предлагаемые над клавиатурой, все еще реагируют на все, что пользователь вводит, он больше не появляется в поле ввода). Иногда можно удалять содержимое через backspace внутри поля ввода, но ничего не добавлять.

Существуют следующие варианты:

  • Событие касания привязывается непосредственно к элементу ввода, ведущему к этой ошибке.

  • Событие касания привязывается к родительскому элементу с входным элементом внутри него (не обязательно быть прямым потомком).

  • Элемент ввода визуально сидит поверх элемента с привязкой события касания. Не имеет значения, где этот элемент находится внутри DOM, или если между элементом, связанным с касанием, и поля ввода есть наложения элементов. Элемент, связанный с касанием, также не должен быть видимым для этого (например, когда другие элементы находятся поверх него). Как только пользователь коснется области поля ввода, в которой находится элемент, связанный с касанием, появляется ошибка. Это интересно, потому что если элемент ввода лишь частично перекрывает элемент, связанный касанием, касание части входа, где элемент, связанный с касанием, не позади, не приведет к этой ошибке.

Решения: Во-первых: это не помогает, если вы экспериментируете с preventDefault/stopPropagation и другими вещами, подобными этому на элементе, связанным касанием. Неважно, что происходит с привязкой события/касания к связанному элементу (я его протестировал, он не вызван/вызван, когда появляется ошибка). Даже CSS-решения, такие как "указатели-события: нет" на элементе, связанный с касанием, не помогают. Итак, следующие решения - наша единственная надежда:

  • Динамически связывать события касания, подобные предложенным в этом потоке, чтобы вы только активировали их, когда они действительно нужны, и удалите их, когда нет, чтобы предотвратить все три случая, описанных выше.

  • Перемещайте элементы или контейнеры с привязками событий касания в сторону, когда элемент ввода находится спереди. Достаточно использовать CSS-преобразования и т.д., Так как даже визуально перемещая их от полей ввода работает. (см. вариант ошибки 3 выше). Поэтому в зависимости от вашего макета, например. если у вас есть боковая панель с полем поиска, которое перемещается со стороны вашей страницы и накладывается на основной контент, подумайте о том, чтобы не накладывать основное содержимое, а вместо этого перемещайте его в сторону.

  • Используйте "display: none" для элементов с привязкой события касания, когда они не используются. Это единственное свойство css, которое, кажется, решает вариант ошибки 3. "видимость: скрытая" не имела никакого эффекта в моих тестах. Элементы с "display: none" больше не влияют на элементы ввода с этой ошибкой. В случае, если вы создаете один пейджер, вы должны рассмотреть страницы настроек или "сенсорные связанные элементы", которые не отображаются, чтобы отобразить их. Это предотвратит вариант ошибки 3.

  • Конечно, лучше всего избегать использования тегов iframe/object/embed для отображения другого содержимого до тех пор, пока эта ошибка существует.

Ответ 2

Не было "хорошего" решения. В моей конкретной ситуации, поскольку мне не нужно было отслеживать события касания в "фоновых" iframe, я просто отключил этот обработчик событий в фоновом режиме и добавил их обратно на передний план. К сожалению, Javascript не имеет никакого способа узнать, какие события привязаны к node. В моем случае, я отслеживал это самостоятельно, и это сработало нормально.

Интересно, что это происходит, даже если вы просто попытаетесь захватить события touchstart внутри одного iframe: введите описание ссылки здесь. Я подозреваю, что обходной путь

Ответ 3

Давид прав. Я знаю, что вокруг этой проблемы нет, я потратил много времени, пытаясь ее найти. Единственное, что нужно сделать, это удалитьEventListener для всех событий касания, когда открывается оверлей iframe, а затем повторно добавитьEventListener, когда iframe закрывается. Единственный способ сделать это - использовать публичную именованную функцию в качестве слушателя событий, чтобы вы могли ее удалить. В какой-то день уровень DOM 3 будет реализован, и вы сможете получить функцию "имя" анонимного или частного прослушивателя событий с помощью eventListenerList(), но ни один браузер не использует это еще.

Интересно, что эта проблема возникает только тогда, когда элементы формы в iframe сидят непосредственно над элементами с помощью прослушивателей событий касания. Элементы формы в iframe, которые не входят в список прослушиваемых элементов сенсорного события, будут работать нормально. В некоторых случаях (не мое), а альтернативное решение может изменить высоту фонового элемента на 1px, в то время как iframe открыт и reset его высота до полного размера, когда перекрытие iframe закрывается.