Leaflet: Le menu contextuel n'est pas déclenché sur iOS13

Créé le 23 sept. 2019  ·  47Commentaires  ·  Source: Leaflet/Leaflet

Après la mise à jour d'iOS vers la version 13, un appui long ne fonctionne pas et l'événement de menu contextuel n'est pas déclenché.

Commentaire le plus utile

Ce problème est référencé à partir du bogue WebKit #202143 . L'équipe WebKit (dans ce cas, moi) enquête.

Tous les 47 commentaires

appuyer deux fois pour déclencher doubleClickZoom ne fonctionnera pas non plus dans iOS 13. Un correctif pour le menu contextuel est beaucoup plus important à résoudre.

Je confirme le même problème. Après un examen initial, il semble que iOS 13 Safari ne déclenche pas correctement les événements tactiles.

Un appui long dans iOS Safari n'envoie aucun événement à la bibliothèque Leaflet et est géré par iOS lui-même.

Le double clic émet les événements suivants : mouseover, mousemove, mousedown, mouseup, click, ce qui entraîne l'événement click du dépliant.

Cela me semble être un problème sérieux dans Safari mobile iOS 13 et je suppose que cela a quelque chose à faire pour rendre Safari mobile très similaire à Safari de bureau. À moins que les événements tactiles ne soient envoyés correctement à Leaflet, je crains qu'il n'y ait pas grand-chose à corriger.

Y a-t-il quelqu'un qui signalera cela à Apple ?

La semaine dernière, j'ai envoyé un rapport au support Apple. Il n'y a toujours pas de réponse.

J'ai un peu plus d'informations. Si l'ajout d'un gestionnaire de clic sur la division de la carte et le contrôle du zoom sont activés, l'appui long a commencé à fonctionner en tant que menu contextuel copier/coller.
comme ça

<div id="mapid" onclick="javascript:void(0)"/>

 -webkit-user-select: none;
 -webkit-touch-callout: none;

n'aide pas non plus, ne désactivez que le menu copier/coller

Peut-être que ce serait utile pour quelqu'un.

En tant que solution temporaire pour les appuis longs :
ajouter cette bibliothèque
https://www.cssscript.com/handle-long-press-tap-event-in-javascript-long-press-js/
et en css
-webkit-user-select: none; -webkit-touch-callout: none;

que d'ajouter un écouteur pour un événement à appui long. Et ici, dans le gestionnaire d'événements, convertissez les coordonnées en coordonnées de dépliant.

La bibliothèque fonctionne-t-elle ? Jusqu'à présent, j'ai vérifié le code source et la bibliothèque écoute les événements tactiles qui ne sont de toute façon pas envoyés à Safari. Juste curieux.

Oui cela fonctionne. Pour un appui long au moins.
Je l'ai vérifié sur iOS13 et iPadOS13

super conseil merci

C'est la solution finale telle que nous l'utilisons. Il détecte les appuis longs et émule le MenuEvent « contextmenu ». Placez-le simplement dans vos codes JS et vous devriez être bon.

Remarque : il est judicieux de limiter l'exécution uniquement pour les utilisateurs d'iOS 13, afin de ne pas avoir trop d'auditeurs

// Copied and modified from https://github.com/john-doherty/long-press-event/

import rs from 'rootScope'
import $ from '$'

if( rs.platform === 'ios' && / OS 13_/.test( navigator.userAgent ) ) {

    // This is our main map el
    const mapEl = $('#map-container')

    // This class adds  -webkit-user-select: none; -webkit-touch-callout: none;
    // to the BODY el
    document.body.classList.add('ios13')

    let timer = null;

    const fireLongPressEvent = originalEvent => {

        clearLongPressTimer();

        const el = originalEvent.target
        , x = originalEvent.touches[0].clientX
        , y = originalEvent.touches[0].clientY

        // This will emulate contextmenu mouse event
        const event = new MouseEvent('contextmenu', {
                                    bubbles: true,
                                    cancelable: true,
                                    clientX: x,
                                    clientY: y
                                })


        // fire the long-press event
        const suppressClickEvent = el.dispatchEvent.call(el,event);

        if (suppressClickEvent) {

            // temporarily intercept and clear the next click
            mapEl.addEventListener('touchend', function clearMouseUp(e) {
                mapEl.removeEventListener('touchend', clearMouseUp, true);
                cancelEvent(e);
            }, true);
        }
    }

    const startLongPressTimer = e => {
        clearLongPressTimer(e);
        timer = setTimeout( fireLongPressEvent.bind(null,e), 1000 );
    }

    const clearLongPressTimer = () => {
        if(timer) {
            clearTimeout(timer);
            timer = null;
        }
    }

    const cancelEvent = e => {
        e.stopImmediatePropagation();
        e.preventDefault();
        e.stopPropagation();
    }

    // hook events that clear a pending long press event
    mapEl.addEventListener('touchcancel', clearLongPressTimer, true);
    mapEl.addEventListener('touchend', clearLongPressTimer, true);
    mapEl.addEventListener('touchmove', clearLongPressTimer, true);

    // hook events that can trigger a long press event
    mapEl.addEventListener('touchstart', startLongPressTimer, true); // <- start

}

Je suis tombé sur ce fil en examinant un problème similaire. J'ai trouvé qu'appeler preventDefault sur touchstart vous permettra de recevoir des événements tactiles comme d'habitude et donc de vérifier le double tap comme avant.

Je pense que cela se résume au fait que Safari mobile a changé la façon dont il simule les événements de survol (pour faire fonctionner les sites Web qui reposent uniquement sur les événements de survol pour ouvrir les menus, etc.)

Je suis tombé sur ce fil en examinant un problème similaire. J'ai trouvé qu'appeler preventDefault sur touchstart vous permettra de recevoir des événements tactiles comme d'habitude et donc de vérifier le double tap comme avant.

Je pense que cela se résume au fait que Safari mobile a changé la façon dont il simule les événements de survol (pour faire fonctionner les sites Web qui reposent uniquement sur les événements de survol pour ouvrir les menus, etc.)

Jusqu'à présent, cela fonctionne à l'opposé pour moi, supprimez simplement tous les clics, à l'exception d'un appui long. Pouvez-vous partager un morceau de code qui fonctionne pour vous ?

désolé, je supposais que vous utilisiez des événements tactiles bruts pour détecter les doubles appuis. Si vous comptez sur les événements clic/souris, alors preventDefault sur touchstart les empêchera.

Quoi qu'il en soit, il semble qu'il s'agisse d'un bogue dans iOS qui a été corrigé dans la version 13.1.

@felixhageloh Je viens de tester sur iOS 13.1 en exécutant cette démo :
http://aratcliffe.github.io/Leaflet.contextmenu/examples/index.html

Mais il ne semble pas que le problème soit résolu ! Y a-t-il quelque chose que je rate ?
Problème vraiment critique pour nous puisque nous comptons beaucoup sur l'événement de presse longue pour fonctionner, toute aide sera appréciée.

Ce problème est référencé à partir du bogue WebKit #202143 . L'équipe WebKit (dans ce cas, moi) enquête.

Ce problème est référencé à partir du bogue WebKit #202143 . L'équipe WebKit (dans ce cas, moi) enquête.

Nous sommes très heureux que vous vous penchiez sur ce problème. En tant que développeurs Web, nous serions heureux que Safari sur iOS 13.x se comporte de la même manière que sur iOS 12.x ou Chrome sur Android en cas d'événements tactiles.

Heureux que vous soyez ici.

@ majid701 désolé si j'ai causé une certaine confusion. Le problème que je voyais dans 13.0 était:
double tap (rapidement) -> un seul touchstart et touchend a été tiré.

Depuis la version 13.1, je reçois à nouveau correctement deux événements touchstart et touchnd. L'application que je maintiens utilise des événements tactiles bruts pour détecter les doubles appuis, donc ce double zoom fixe pour nous. Je supposais à tort que ce serait le cas pour vous aussi.

if( rs.platform === 'ios' && / OS 13_/.test( navigator.userAgent ) )

Notez @ilblog que cela ne fonctionnera pas sur iPadOS où la chaîne de l'agent utilisateur est la même que Safari sur le bureau et n'annoncera pas iOS.

_(republier à partir du bogue WebKit afin que les gens qui ont Cc à ce bogue seuls puissent le voir.)_

L'événement Leaflet contextmenu n'est pas déclenché car le code Leaflet utilise les événements pointerdown et touchstart manière interchangeable et la disponibilité du premier exclut l'autre. Dans Map.Tap.js, la méthode _onDown a cette vérification if (!e.touches) { return; } qui conduira toujours à un retour anticipé puisqu'un événement PointerEvent n'est pas la même chose qu'un TouchEvent et n'a pas touches propriété

Ainsi, l'absence d'événement Leaflet contextmenu déclenché est spécifiquement un problème de Leaflet.

En fait, ce code n'est même pas exécuté avec Pointer Events à cause de ce code :

if (Browser.touch && !Browser.pointer) {
    Map.addInitHook('addHandler', 'tap', Tap);
}

Sur iOS 13, Browser.touch et Browser.pointer sont vrais, donc l'ensemble du package n'est pas initialisé.

La semaine dernière, j'ai envoyé un rapport au support Apple. Il n'y a toujours pas de réponse.

@ginger-777 Avez-vous un numéro de bogue/ID de retour ? Généralement, le meilleur moyen d'attirer l'attention des développeurs WebKit est de signaler un bogue directement sur bugs.webkit.org.

@graouts Oui, j'ai remarqué cela aussi après avoir lu votre dernier commentaire. Mais j'ai testé la désactivation des événements de pointeur à partir de Settings -> General -> Experimental Features et il semblait que cela fonctionnait sur ce site de démonstration :
http://aratcliffe.github.io/Leaflet.contextmenu/examples/index.html

Mais cela ne fonctionne pas à l'intérieur de mon application. Je charge un dépliant dans une vue Web.

Quant au double-tap-to-zoom, c'est aussi un problème avec Leaflet. Considérez ce code dans DomEvent.DoubleTap :

function onTouchStart(e) {
    var count;

    if (Browser.pointer) {
        if ((!Browser.edge) || e.pointerType === 'mouse') { return; }
        count = _pointersCount;
    } else {
        count = e.touches.length;
    }

    if (count > 1) { return; }

    var now = Date.now(),
        delta = now - (last || now);

    touch = e.touches ? e.touches[0] : e;
    doubleTap = (delta > 0 && delta <= delay);
    last = now;
}

Si un navigateur prend en charge les événements de pointeur et qu'il ne s'agit

Pour autant que je sache, les deux problèmes signalés ici sont tout simplement pas prêts pour un navigateur qui prend en charge à la fois les événements tactiles et les événements de pointeur (appui long et l'événement contextmenu ) ou prend en charge les événements de pointeur mais n'est pas Microsoft Edge (appuyez deux fois et l'événement dblclick ).

J'ai fermé le bogue WebKit car il n'y a rien à corriger à ce stade.

@graouts Oui, j'ai remarqué cela aussi après avoir lu votre dernier commentaire. Mais j'ai testé la désactivation des événements de pointeur à partir de Settings -> General -> Experimental Features et il semblait que cela fonctionnait sur ce site de démonstration :
http://aratcliffe.github.io/Leaflet.contextmenu/examples/index.html

Mais cela ne fonctionne pas dans mon application. Je charge un dépliant dans une vue Web.

Oui @majid701 , je pense que ce paramètre ne s'applique qu'à Safari, pas à WKWebView ou à tout autre moyen d'intégrer WebKit sur iOS. Je ne sais pas comment désactiver les événements de pointeur en dehors de l'application Safari.

Je pense que la solution sera de corriger la bibliothèque Leaflet qui détecte iOS 13 Safari comme navigateur "pinterType" et ne gère donc pas correctement les événements Touch.

Merci beaucoup @graouts pour vos efforts et beaucoup d'aide dans cette affaire.

Il y a des relations publiques juste en mouvement https://github.com/Leaflet/Leaflet/pull/6815 concernant le double tap sur les appareils mobiles, corrigeant les mêmes lignes de code, je leur ai donc demandé de couvrir également ce problème.

if( rs.platform === 'ios' && / OS 13_/.test( navigator.userAgent ) )

Notez @ilblog que cela ne fonctionnera pas sur iPadOS où la chaîne de l'agent utilisateur est la même que Safari sur le bureau et n'annoncera pas iOS.

Eh bien, c'est intéressant, car cela peut déclencher une autre série de problèmes, lorsque les développeurs sont incapables de distinguer macOS et iPad. Ce n'est pas lié à ce bug de Leaflet mais je pense qu'il devrait également être corrigé par Apple.

https://forums.developer.apple.com/thread/119186

RÉSOLU

Grâce aux précieuses contributions d'Apple, j'ai réussi à résoudre le problème d'iOS 13 au moins sur iPhone. Étant donné que l'iPad sur iOS13 a la même chaîne d'agent utilisateur que macOS, je n'ai pas pu résoudre le problème sur iPad.

Modifiez simplement src/core/Browser.js comme ceci :

// 'false' for all iOS devices, that (as of version iOS13 support both, touch and pointer events)
// Unfortunately as of iOS13 it is not possible to distinguish iPad from OS X by user agent string
export var pointer = !!(window.PointerEvent || msPointer) && !userAgentContains('iphone');

La demande de tirage avec le correctif est ici

https://github.com/Leaflet/Leaflet/pull/6827

if( rs.platform === 'ios' && / OS 13_/.test( navigator.userAgent ) )

Notez @ilblog que cela ne fonctionnera pas sur iPadOS où la chaîne de l'agent utilisateur est la même que Safari sur le bureau et n'annoncera pas iOS.

Eh bien, c'est intéressant, car cela peut déclencher une autre série de problèmes, lorsque les développeurs sont incapables de distinguer macOS et iPad. Ce n'est pas lié à ce bug de Leaflet mais je pense qu'il devrait également être corrigé par Apple.

Généralement, prendre des décisions basées sur la chaîne user-agent n'est pas l'approche optimale. La détection de fonctionnalités au cas par cas convient à la grande majorité des cas d'utilisation. De plus, Safari sur iPadOS peut utiliser une chaîne d'agent utilisateur différente en fonction de la classe d'appareil (iPad mini par rapport aux autres modèles d'iPad à écran plus grand) ou en fonction de la taille de la fenêtre en multitâche où Safari peut utiliser un tiers de la écran, ou même selon la préférence de l'utilisateur (demande site Web mobile/de bureau).

S'il y a des cas où la détection de fonctionnalités ne convient pas, veuillez signaler un bogue sur bugs.webkit.org et l'équipe WebKit sera en mesure de déterminer la meilleure solution.

Des mises à jour sur la façon de résoudre ce problème pour iPadOS 13 ?
J'utilise les événements mousedown/mouseup.

Salut tout le monde,

J'ai essayé une meilleure solution que de simplement renifler l'UA et de le truquer, car il devrait être plus stable.
https://github.com/Leaflet/Leaflet/pull/6855
Il renifle toujours l'UA, mais juste pour que je ne modifie pas le code des plates-formes que je ne peux pas tester. La condition edge semble être là pour le renflouement en raison de possibles événements dblclick déclenchés, ou quelque chose du genre ? Il me semble (mais je ne connais pas bien JS) que le test de fonctionnalité Browser.pointer pour les événements de pointeur devrait être le seul test dont nous avons besoin là-dedans, et les autres tests devraient être étroits et très spécifiques, au lieu de "si ce n'est pas Edge, renflouez-vous" (il manque un "pourquoi?"), il semble que cela devrait être "si Edge, faites quelque chose de différent" (avec une raison dans les commentaires).

Cependant, j'ai toujours un problème avec iOS13 sur un iPad.

@graouts : Re : https://github.com/Leaflet/Leaflet/issues/6817#issuecomment -536581361 et https://github.com/Leaflet/Leaflet/issues/6817#issuecomment -536587962 :
J'essaie de faire fonctionner cela sur iPad (corrigé sur iPhone avec le PR ci-dessus), mais il n'y a pas de déclenchement d'un deuxième pointerdown si je fais un double-clic. J'ai envoyé des points d'arrêt, ajouté des appels log() , et je ne vois aucun déclenchement. Il semble que l'iPad omet le deuxième robinet dans certaines situations.
Je peux le faire zoomer avec un "triple/quadruple clic", mais pas avec un double-clic. les journaux sur les gestionnaires globaux (qui sont une couche de compatibilité pour les choses demandant touchstart / touchend ) ne se déclenchent pas lors des doubles appuis.
Avez-vous une idée de ce que l'iPad (avec iOS13) pourrait faire pour omettre ces secondes pressions ? Le seuil est de 250 ms, mais je ne peux pas obtenir le deuxième appui pour m'inscrire s'il est plus rapide que ~ 320 ms.
J'ai <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> dans mon en-tête, comme mentionné dans la page "Dépliant pour mobile".

@graouts (désolé, je ne vous en veux pas, je vous demande juste parce que vous avez mentionné être de l'équipe WebKit):
Si vous essayez la page de test d'écouteur d'événement suivante :
https://patrickhlauke.github.io/touch/tests/event-listener.html

Vous pouvez voir la différence entre iOS13 sur iPhone et iPad. sur l'iPhone, je peux appuyer deux fois sur le bouton et voir deux séquences de pointerdown+pointerup . Sur l'iPad, je ne peux pas déclencher cette séquence avec un double tap. Il organise des événements pendant un certain temps.

Merci,
Philippe

@filcab Veuillez

@filcab Veuillez

J'ai déposé https://bugs.webkit.org/show_bug.cgi?id=203031

Salut tout le monde, merci beaucoup pour toutes les précieuses contributions - nous examinons cela dès que possible. Je suis en vacances, je ne pourrai donc pas coder, mais j'ai demandé à d'autres responsables de donner la priorité.

En parcourant rapidement la discussion, il semble que la source du problème soit que iOS13 prend en charge la spécification des événements de pointeur, donc Leaflet prend partiellement les chemins de code que nous avons optimisés spécifiquement pour les navigateurs mobiles IE hérités où les événements tactiles habituels n'étaient pas disponibles.

Le reniflage de chaîne d'agent comme dans #6827 n'est pas très fiable (peut se briser à tout moment); réparer les événements de pointeur comme dans #6855 pourrait être mieux mais toujours très risqué étant donné que nous n'avons jamais eu l'intention que les chemins de code de pointeur fonctionnent dans les navigateurs modernes lorsque nous avons écrit ce code ; peut-être devrions-nous essayer de désactiver complètement les événements de pointeur si nous avons détecté plus tôt que les événements tactiles réguliers fonctionnent correctement (donc, quelque chose comme export var pointer = !webkit && !!(window.PointerEvent || msPointer) ) ?

Salut tout le monde, merci beaucoup pour toutes les précieuses contributions - nous examinons cela dès que possible. Je suis en vacances, je ne pourrai donc pas coder, mais j'ai demandé à d'autres responsables de donner la priorité.

En parcourant rapidement la discussion, il semble que la source du problème soit que iOS13 prend en charge la spécification des événements de pointeur, donc Leaflet prend partiellement les chemins de code que nous avons optimisés spécifiquement pour les navigateurs mobiles IE hérités où les événements tactiles habituels n'étaient pas disponibles.

Le reniflage de chaîne d'agent comme dans #6827 n'est pas très fiable (peut se briser à tout moment); réparer les événements de pointeur comme dans #6855 pourrait être mieux mais toujours très risqué étant donné que nous n'avons jamais eu l'intention que les chemins de code de pointeur fonctionnent dans les navigateurs modernes lorsque nous avons écrit ce code ; peut-être devrions-nous essayer de désactiver complètement les événements de pointeur si nous avons détecté plus tôt que les événements tactiles réguliers fonctionnent correctement (donc, quelque chose comme export var pointer = !webkit && !!(window.PointerEvent || msPointer) ) ?

Cela semble bien, surtout si le code des événements de pointeur n'est pas aussi récent que prévu. Je mettrai à jour mon PR avec votre patch proposé (juste testé sur iPad et iPhone avec iOS 13 et cela fonctionne sur les deux).

Merci,
Philippe

D'un point de vue standard, il est préférable d'utiliser des événements de pointeur (le cas échéant) plutôt que des événements tactiles, car il s'agit d'une norme W3C et non d'une technologie spécifique à un fournisseur.

D'un point de vue technique, au moins pour l'implémentation iOS, il y a des implications en termes de performances. Les événements tactiles sont toujours distribués de manière synchrone, de sorte que tout travail JS effectué à la suite de leur traitement sera bloquant et peut entraîner une réactivité inférieure. Les événements de pointeur ont été conçus de manière à pouvoir être implémentés de manière asynchrone, et sur iOS, ils le sont.

Deux choses à considérer, peut-être pas pour ce problème particulier, mais qui méritent d'être gardées à l'esprit pour l'avenir.

se pourrait-il que le correctif #6855 ne fonctionne pas sur les marqueurs ? Je reçois des rapports selon lesquels un appui long sur un marqueur ne déclenche pas le menu contextuel mais le menu partager/copier le système d'exploitation apparaît...

Salut @eliboni ,
Il semble que vous deviez utiliser -webkit-touch-callout: none; (ou similaire... je suis mauvais pour ça 😅) sur les éléments. Je ne sais pas sur quoi, cependant. L'ajout de * { -webkit-touch-callout: none; } fonctionné sur mon test (événement contextmenu déclenché sur un marqueur, à l'aide d'un iPhone).
Ajustez selon vos besoins.

Salut,
Je ne peux pas utiliser le zoom double tap avec Leaflet.js.
Je viens de lire ce post, mais je n'ai pas trouvé la solution.
Quelqu'un peut-il m'aider s'il-vous-plaît?

Meilleures salutations Kandy.

J'ai enfin trouvé la solution :

Nous devons mettre à jour la brochure 1.5 et activer de Tap sur :

new L.map("mapid",{ attributionControl: false, zoomControl: false, tap: true });

et fonctionne bien.

Meilleures salutations

@bitxenio ,

J'ai peur que cela ne fonctionne pas pour moi en utilisant 1.5.1

Je suis de retour à l'écriture de mon propre gestionnaire pour le moment car IOS 13.2 ne semble pas non plus fonctionner non plus :(

@rwillett , @Bitxenio : Quel code utilisez-vous ? Vous mentionnez tous les deux des versions spécifiques du dépliant.
Le correctif pour PR#6855, validé en octobre, n'est pas dans la dernière version, qui date de mai.

Utilisez-vous master ?

Merci,
Philippe

Ah ! Nous avons utilisé la version officielle. Nous n'avions pas réalisé que PR#6855 n'était pas inclus. Notre faute.

Nous avons téléchargé la branche dev et nous allons essayer cela,

Merci

Rob

Nous avons téléchargé 1.6-dev et cela fonctionne bien. Le double tap et le menu contextuel sur ios 13.2.

Merci pour l'aide, merci.

Rob

Sorti en 1.6.0

peut-être devrions-nous essayer de désactiver complètement les événements de pointeur si nous avons détecté plus tôt que les événements tactiles réguliers fonctionnent correctement (donc, quelque chose comme export var pointer = !webkit && !!(window.PointerEvent || msPointer) ) ?

@pleureuse
C'était effectivement risqué et causait des problèmes comme #6896.
Veuillez vous joindre à la discussion sur https://github.com/Leaflet/Leaflet/issues/6977#issuecomment -577638632

Cette page vous a été utile?
0 / 5 - 0 notes