Hoe kom ik erachter wat het daadwerkelijke event.target van de touchmove javascript-gebeurtenis is?

=Ik probeer een eenvoudige gebruikersinterface voor slepen en neerzetten te ontwikkelen in mijn webtoepassing. Een item kan met een muis of een vinger worden gesleept en vervolgens in een van de verschillende neerzetzones worden neergezet. Wanneer een item over een dropzone wordt gesleept (maar nog niet is vrijgegeven), wordt die zone gemarkeerd, wat een veilige landingslocatie aangeeft. Dat werkt prima met muisgebeurtenissen, maar ik zit vast met touchstart/touchmove/touchend-familie op de iPhone/iPad.

Het probleem is dat wanneer de ontouchmove-gebeurtenishandler van een item wordt aangeroepen, de event.touches[0].targetervan altijd verwijst naar het oorspronkelijke HTML-element (het item) en niet naar het element dat momenteel onder de vinger. Bovendien, wanneer een item met een vinger over een dropzone wordt gesleept, worden de eigen touchmove-handlers van die dropzone helemaal niet aangeroepen. Dat betekent in wezen dat ik niet kan bepalen wanneer een vinger zich boven een van de neerzetzones bevindt, en daarom niet kan markeren als dat nodig is. Tegelijkertijd wordt bij gebruik van een muis mousedowncorrect geactiveerd voor alle HTML-elementen onder de cursor.

Sommige mensen bevestigen dat het zo hoort te werken, bijvoorbeeld http://www.sitepen.com/blog/2008/07/10/touching-and-gesturing-on-the-iphone/:
Voor degenen onder u die uit de normale webdesignwereld komen, in een normale mousemove-gebeurtenis, is het knooppunt dat wordt doorgegeven in het doelattribuut meestal wat de muis momenteel is. Maar in alle iPhone-aanraakgebeurtenissen is het doel een verwijzing naar het oorspronkelijke knooppunt.

Vraag:is er een manier om het daadwerkelijke element onder een vinger te bepalen (NIET het aanvankelijk aangeraakte element dat in veel omstandigheden anders kan zijn)?


Antwoord 1, autoriteit 100%

Dat is zeker niet hoe gebeurtenisdoelen zouden moeten werken. Nog een andere DOM-inconsistentie waar we nu waarschijnlijk allemaal voor altijd aan vast zitten, omdat een leverancier achter gesloten deuren extensies bedenkt zonder enige beoordeling.

Gebruik document.elementFromPointom dit te omzeilen.

document.elementFromPoint(event.clientX, event.clientY);

Antwoord 2, autoriteit 91%

Het geaccepteerde antwoord uit 2010 werkt niet meer: ​​touchmoveheeft geen clientXof clientYattribuut. (Ik gok dat dat vroeger zo was, aangezien het antwoord een aantal upvotes heeft, maar momenteel niet.)

De huidige oplossing is:

var myLocation = event.originalEvent.changedTouches[0];
var realTarget = document.elementFromPoint(myLocation.clientX, myLocation.clientY);

Getest en werkt op:

  • Safari op iOS
  • Chrome op iOS
  • Chrome op Android
  • Chrome op Windows-bureaublad met aanraakfunctie
  • FF op Windows-bureaublad met aanraakfunctie

Werkt NIET op:

  • IE op Windows-bureaublad met aanraakfunctie

Niet getest op:

  • Windows Phone

Antwoord 3, autoriteit 11%

Ik ben hetzelfde probleem tegengekomen op Android (WebView + Phonegap). Ik wil elementen kunnen slepen en detecteren wanneer ze over een bepaald ander element worden gesleept.
Om de een of andere reden lijken touch-events de attribuutwaarde pointer-eventste negeren.

Muis:

  • als pointer-events="visiblePainted"is ingesteld, wijst event.targetnaar het versleepte element.
  • als pointer-events="none"is ingesteld, zal event.targetverwijzen naar het element onder het versleepte element (mijn sleepzone)

Dit is hoe dingen zouden moeten werken en waarom we in de eerste plaats het kenmerk pointer-eventshebben.

Tik op:

  • event.targetverwijst altijd naar het versleepte element, ongeacht de waarde van pointer-eventsdie IMHO verkeerd is.

Mijn oplossing is om mijn eigen sleepgebeurtenisobject te maken (een gemeenschappelijke interface voor zowel muis- als aanraakgebeurtenissen) dat de gebeurteniscoördinaten en het doel bevat:

  • voor muisgebeurtenissen hergebruik ik gewoon de muisgebeurtenis zoals deze is
  • voor touch-evenement gebruik ik:

    DragAndDrop.prototype.getDragEventFromTouch = function (event) {
        var touch = event.touches.item(0);
        return {
            screenX: touch.screenX,
            screenY: touch.screenY,
            clientX: touch.clientX,
            clientY: touch.clientY,
            pageX: touch.pageX,
            pageY: touch.pageY,
            target: document.elementFromPoint(touch.screenX, touch.screenY)
        };
    };
    

En gebruik dat dan voor verwerking (controleren of het gesleepte object zich in mijn sleepgebied bevindt). Om de een of andere reden lijkt document.elementFromPoint()de waarde pointer-eventszelfs op Android te respecteren.


Antwoord 4, autoriteit 9%

Dus aanraakgebeurtenissen hebben een andere ‘filosofie’ als het gaat om hoe ze met elkaar omgaan:

  • Muis beweegt = “hover”zoals gedrag
  • Aanraakbewegingen = ‘sleept’zoals gedrag

Dit verschil komt voort uit het feit dat er geen touchmovekan zijn zonder touchstart-gebeurtenis die eraan voorafgaat, aangezien een gebruiker het scherm moet aanraken om deze interactie te starten. Met de muis kan een gebruiker natuurlijk over het hele scherm bewegen zonder ooit op de knop te drukken (mousedown-gebeurtenis)

Dit is de reden waarom we eigenlijk niet kunnen hopen dingen zoals zweefeffecten met aanraking te gebruiken:

element:hover { 
    background-color: yellow;
}

En dit is de reden waarom wanneer de gebruiker het scherm met 1 vinger aanraakt, de eerste gebeurtenis (touchstart) het doelelement verwerft en de daaropvolgende gebeurtenissen (touchmove) de verwijzing naar het originele element waar de aanraking begon, behouden. Het voelt verkeerd, maar er is een logica dat je misschien ook originele doelinformatie nodig hebt. Dus idealiter zouden er in de toekomst beide (brondoel en huidig ​​doel) beschikbaar moeten zijn.

Dus de gangbare praktijk van vandaag (2018) waar schermen met muis EN aanraking tegelijkertijd kunnen zijn, is nog steeds om beide luisteraars (muis en aanraking) toe te voegen en vervolgens de gebeurteniscoördinaten te “normaliseren” en de bovengenoemde browser-API te gebruiken om elementen te vinden in die coördinaten:

 // get coordinates depending on pointer type:
  var xcoord = event.touches? event.touches[0].pageX : event.pageX;
  var ycoord = event.touches? event.touches[0].pageY : event.pageY;
  // get element in coordinates:
  var targetElement = document.elementFromPoint(xcoord, ycoord);
  // validate if this is a valid element for our case:
  if (targetElement && targetElement.classList.contains("dropZone")) {
  }

Antwoord 5

Het antwoord van JSP64 werkte niet volledig omdat event.originalEventaltijd undefinedretourneerde. Een kleine wijziging als volgt werkt nu.

var myLocation = event.touches[0];
var realTarget = document.elementFromPoint(myLocation.clientX, myLocation.clientY);

Other episodes