Et vanlig klikk er uproblematisk og vi trenger ikke ta spesielle
forholdsregler for at dette skal fungere. En touch-skjerm simulerer, eller emulerer, en "tap" som et museklikk.
Det vi imidlertid må være klar over er at
det er mange begivenheter involverte nå vi klikker på en touch-skjerm. I rekkefølge er det slik:
- touchstart
- touchmove
- touchend
- mouseover
- mousemove
- mousedown
- mouseup
- click
Det betyr f.eks. at dersom vi er interesserte i å starte en drag-operasjon på en touch-skjerm
så må vi plukke opp touchstart, og ikke de andre begivenhetene. Nøkkelen til dette er
event-funksjonen preventDefault(). Det er en god leveregel å kalle denne i
behandlingen av touch-events.
Enkel drag
Med mus
Dersom vi skal dragge et element vil kanskje gjøre noe slikt:
mousedrag1.html
https://borres.hiof.no/respons/events1/mousedrag1.html
// the dragging functionality
var mover;
var initialMouseX;
var initialMouseY;
var startX;
var startY;
// select and adjust dragger functionality
function drop(){
// we want it to stay in absolute pos
mover.classList.add("stayAbsolute");
// we want to see where it is
var L=parseInt(mover.style.left);
var T=parseInt(mover.style.top);
mover.innerHTML=""+L+","+T+"<br/>Dragme";
}
function releaseElement(evt){
mover.classList.remove("dragged");
drop();
document.removeEventListener("mousemove",dragMouse,false);
document.removeEventListener("mouseup",releaseElement,false);
}
function dragMouse(evt){
var dx = evt.clientX - initialMouseX;
var dy = evt.clientY - initialMouseY;
mover.style.left = startX + dx + 'px';
mover.style.top = startY + dy + 'px';
}
function startDragMouse(evt){
startX = mover.offsetLeft;
startY = mover.offsetTop;
mover.classList.add("dragged");
initialMouseX = evt.clientX;
initialMouseY = evt.clientY;
document.addEventListener("mousemove",dragMouse,false);
document.addEventListener("mouseup",releaseElement,false);
}
function setUp(){
mover=document.querySelector(".dragme");
mover.addEventListener("mousedown",startDragMouse,false);
}
Merk at vi kopler mousemove og mouseup til document,
ikke til det elementet vi dragger.
Med fingeren
Dersom vi skal dragge et element vil kanskje gjøre noe slikt:
fingerdrag1.html
https://borres.hiof.no/respons/events1/fingerdrag1.html
// the dragging functionality
var mover;
var initialMouseX;
var initialMouseY;
var startX;
var startY;
// select and adjust dragger functionality
function drop(){
// we want it to stay in absolute pos
mover.classList.add("stayAbsolute");
// we want to see where it is
var L=parseInt(mover.style.left);
var T=parseInt(mover.style.top);
mover.innerHTML=""+L+","+T+"<br/>Dragme";
}
function releaseElement(evt){
mover.classList.remove("dragged");
drop();
mover.removeEventListener("touchmove",dragFinger,false);
mover.removeEventListener("touchend",releaseElement,false);
}
function dragFinger(evt){
var dx = evt.changedTouches[0].clientX - initialMouseX;
var dy = evt.changedTouches[0].clientY - initialMouseY;
mover.style.left = startX + dx + 'px';
mover.style.top = startY + dy + 'px';
evt.preventDefault();
}
function startDragFinger(evt){
startX = mover.offsetLeft;
startY = mover.offsetTop;
mover.classList.add("dragged");
initialMouseX = evt.changedTouches[0].clientX;
initialMouseY = evt.changedTouches[0].clientY;
mover.addEventListener("touchmove",dragFinger,false);
mover.addEventListener("touchend",releaseElement,false);
evt.preventDefault();
}
function setUp(){
mover=document.querySelector(".dragme");
mover.addEventListener("touchstart",startDragFinger,false);
}
Merk at vi kopler mousemove og mouseup til det elementet vi dragger,
og vi kaller preventDefault() når vi har behandlet et touch-event.
Merk dessuten at vi bruker changedTouches[o] når vi skal ha tak i en bestemt begivenhet.
Det betyr at vi skal ha tak i den første finger-touch begivenheten. Vi kan ha flere, bruke begge fingrene.
Dette gjelder både touchstart og andre touch-events.
Kodestrategi
Eksemplene over er svært enkle. Dragging innebærer oftest mer en å flytte på
et enkelt absolutt-posisjonert element. Vi vil kanskje kunne flytte på mange elementer,
vi vil kontrollere hvor de havner og vi vil kanskje justere posisjonen i forhold til andre elementer
i nærheten. Tenk deg at du vil flytte spillkort fra en kortsokk og legge dem pent
ved siden av hverandre hos ulike spillere.
Jeg har god erfaring med å lage et objekt (ikk en klasse) som tar seg av dragging. For
musehandtering kan en slik klasse se omtrent slik ut;
// when we are mousebased
mouseDrag = {
initialMouseX: undefined,
initialMouseY: undefined,
startX: undefined,
startY: undefined,
draggedObject:undefined,
MAX_Z_INDEX:1,
initElement: function (elt) {
elt.addEventListener("mousedown",mouseDrag.startDragMouse,false);
if (elt.style.zIndex)
{
thisIndex=element.style.zIndex;
if (thisIndex > mouseDrag.MAX_Z_INDEX)
mouseDrag.MAX_Z_INDEX=thisIndex;
}
},
clearElement: function (obj){
obj.removeEventListener("mousedown",mouseDrag.startDragMouse,false);
document.removeEventListener("mousemove",mouseDrag.dragMouse,false);
document.removeEventListener("mouseup",mouseDrag.releaseElement,false);
},
resetElement:function(obj){
mouseDrag.setPosition(0,0);
document.removeEventListener("mousemove",mouseDrag.dragMouse,false);
document.removeEventListener("mouseup",mouseDrag.releaseElement,false);
}
,
startDragMouse: function (evt) {
mouseDrag.startDrag(evt.target);
mouseDrag.initialMouseX = evt.clientX;
mouseDrag.initialMouseY = evt.clientY;
document.addEventListener("mousemove",mouseDrag.dragMouse,false);
document.addEventListener("mouseup",mouseDrag.releaseElement,false);
evt.stopPropagation();
return false;
},
startDrag: function (obj) {
if (mouseDrag.draggedObject)
mouseDrag.clearElement(mouseDrag.draggedObject);
mouseDrag.startX = obj.offsetLeft;
mouseDrag.startY = obj.offsetTop;
mouseDrag.draggedObject = obj;
obj.classList.add("dragged");
mouseDrag.draggedObject.style.zIndex=mouseDrag.MAX_Z_INDEX+1;
mouseDrag.MAX_Z_INDEX=mouseDrag.MAX_Z_INDEX+1;
mouseDrag.mark();
},
dragMouse: function (evt) {
if(!mouseDrag.draggedObject)
return false;
var dX = evt.clientX - mouseDrag.initialMouseX;
var dY = evt.clientY - mouseDrag.initialMouseY;
mouseDrag.setPosition(dX,dY);
return false;
},
setPosition: function (dx,dy) {
mouseDrag.draggedObject.style.left = mouseDrag.startX + dx + 'px';
mouseDrag.draggedObject.style.top = mouseDrag.startY + dy + 'px';
},
releaseElement: function() {
if(mouseDrag.draggedObject){
mouseDrag.draggedObject.classList.remove("dragged")
// where did it drop
mouseDrag.drop(mouseDrag.draggedObject);
}
mouseDrag.draggedObject = null;
},
// default dropper
drop:function(){
mouseDrag.resetElement(mouseDrag.draggedObject);
},
// default marker
mark:function(){
}
}
Jeg påstår ikke at det er iddellt, men "it works for me".
Nå kan jeg lage et tilsvarende objekt for touch-handtering
// when we are touch oriented
touchDrag = {
initialMouseX: undefined,
initialMouseY: undefined,
startX: undefined,
startY: undefined,
draggedObject:undefined,
MAX_Z_INDEX:1,
initElement: function (elt) {
elt.addEventListener("touchstart",touchDrag.startDragFinger,false);
if (elt.style.zIndex)
{
thisIndex=element.style.zIndex;
if (thisIndex > touchDrag.MAX_Z_INDEX)
touchDrag.MAX_Z_INDEX=thisIndex;
}
},
clearElement: function (obj){
obj.removeEventListener("touchstart",touchDrag.startDragFinger,false);
obj.removeEventListener("touchmove",touchDrag.dragFinger,false);
obj.removeEventListener("touchend",touchDrag.releaseElement,false);
},
resetElement:function(obj){
touchDrag.setPosition(0,0);
obj.removeEventListener("touchmove",touchDrag.dragFinger,false);
obj.removeEventListener("touchend",touchDrag.releaseElement,false);
}
,
startDragFinger: function (evt) {
var obj=evt.changedTouches[0].target;
touchDrag.startDrag(obj);
touchDrag.initialMouseX = evt.changedTouches[0].clientX;
touchDrag.initialMouseY = evt.changedTouches[0].clientY;
obj.addEventListener("touchmove",touchDrag.dragFinger,false);
obj.addEventListener("touchend",touchDrag.releaseElement,false);
evt.preventDefault();
evt.stopPropagation();
touchDrag.mark();
return false;
},
startDrag: function (obj) {
if (touchDrag.draggedObject)
touchDrag.clearElement(touchDrag.draggedObject);
touchDrag.startX = obj.offsetLeft;
touchDrag.startY = obj.offsetTop;
touchDrag.draggedObject = obj;
obj.classList.add("dragged");
touchDrag.draggedObject.style.zIndex=touchDrag.MAX_Z_INDEX+1;
touchDrag.MAX_Z_INDEX=touchDrag.MAX_Z_INDEX+1;
},
dragFinger: function (evt) {
if(!touchDrag.draggedObject)
return false;
var dX = evt.changedTouches[0].clientX - touchDrag.initialMouseX;
var dY = evt.changedTouches[0].clientY - touchDrag.initialMouseY;
touchDrag.setPosition(dX,dY);
evt.preventDefault();
//return false;
},
setPosition: function (dx,dy) {
touchDrag.draggedObject.style.left = touchDrag.startX + dx + 'px';
touchDrag.draggedObject.style.top = touchDrag.startY + dy + 'px';
},
releaseElement: function(evt) {
if(touchDrag.draggedObject){
touchDrag.draggedObject.classList.remove("dragged");
// where did it drop
touchDrag.drop(touchDrag.draggedObject);
}
touchDrag.draggedObject = null;
evt.preventDefault();
},
// default dropper, should redefined by using code
drop:function(){
touchDrag.resetElement(touchDrag.draggedObject);
},
// default marker
mark:function(){
}
}
Det er et problem:
Jeg har ingen enkel og sikker måte å avgjøre om jeg skal forholde meg til en touch-skjerm.,
altså vilken av de to objektene jeg skal kople til.
Det er i hvert fall tre måter å angripe dette problemet på:
- Størrelsen på skjermmen kan hjelpe meg til å velge vilken dragstrategi (objekt) jeg
skal bruke. Det er rimelig greitt å identifisere
mobiler og små pad'er, men det er en foranderlig verden og det er vanskelig å ta en sikker
beslutning.
-
Jeg kan lage en test på den måten at når brukeren åpner siden er hen tvunget til å klikke på
et eller annet for å komme videre. Dette, et eller annet, kan settes til å reagere på både
touchstart og mousedown. Siden rekkefølgen events behandles i er gitt,
kan jeg lett finne ut om det er touch-skjerm og
jeg kan justere koden (velge dragobjekt) deretter.
-
Jeg kan lage et objekt som er mottagelig både for musebegivenheter og touch.
Den første strategien vil etterlate en viss usikkerhet om vi har fått med oss alle
aktuelle dimensjonene,
mens de to andre vil fungere. Den andre strategien, der vi så og si indirekte
tvinger brukeren til å identifisere skjermen sin, kan av og til være litt kunstig
og det greieste er trolig å vedlikeholde og tilpasse et drag-obekt som tar seg av alle aktuelle
begivenheter. Du finner et eksemple på bruk i
eksempel1
,
og du kan jo se på Likhet.