I föregående laboration implementerade vi det allra minsta nödvändiga för drag and drop. Nu ska vi göra det mer användarvänligt genom att ge visuell feedback både under och efter drag-processen.
0 - Förberedelser
Vi fortsätter i samma mapp som förra laborationen (lab-6).
- Skapa en ny fil
c.js. - Kopiera koden från
b.jstillc.jssom utgångspunkt. - Ändra i
index.htmlså att den pekar påc.jsistället förb.js.<script type="module" src="c.js"></script>
2 - Visuell feedback på dropzoner
Användaren behöver veta var det går att släppa elementet. Vi kan använda dragenter och dragleave för att markera drop-zonen när man håller musen över den.
- Skapa en css-klass
.accepti din CSS som ger t.ex. en annan bakgrundsfärg..drop-zone.accept { background-color: rgb(192, 238, 192); } - Skapa en
dragenter-lyssnare på drop-zonerna som lägger till klassenaccept. - Skapa en
dragleave-lyssnare på drop-zonerna som tar bort klassenaccept.
Förslag på lösning
for (const zone of dropZones) {
// ... dragover, drop ...
zone.addEventListener('dragenter', function(event) {
event.target.classList.add('accept');
});
zone.addEventListener('dragleave', function(event) {
event.target.classList.remove('accept');
});
}
Testa
- Testa nu att dra element över dropzonerna, och se att de blir gröna när ett element är över en dropzon.
- Prova även att släppa element i en dropzon. Vad händer?
Rensa feedback efter släpp
Som du märker stannar feedbacken kvar efter att man har släppt elementet. Det beror på att dragleave aldrig körs när man släpper elementet. Vi behöver bort feedbacken i samband med drop-eventet också.
I c.js, uppdatera funtionen onDrop så att den tar bort klassen accept från drop-zonen.
Förslag på lösning
function onDrop(event) {
event.preventDefault();
event.target.classList.remove("accept");
event.target.textContent = draggedElement.textContent;
}
3 - Uppdatera drag-elementet efter släpp
Ibland vill vi bara låta användaren dra ett visst element en gång. När det väl har blivit släppt vill vi inte att det skall kunna dras igen.
För att lösa detta på ett sätt som också är tydligt för användaren behöver vi göra två saker.
-
För det första behöver vi göra så att elementet inte går att dras något mer, det gör vi genom att sätta
draggabletillfalse. -
För det andra behöver vi visuellt indikera för användaren att detta elementet är “förbrukat”. Det går såklart att göra på flera sätt, i det här exemplet skall vi ta bort innehållet i elementet, samt skapa en css-regel som stilsätter tomma
drag-item-element med en lägre opacitet och återställa muspekaren till en som inte antyder att elementet kan dras. -
I
c.js, uppdatera funtionenonDropså att den sätterdraggedElement.draggabletillfalseoch ta bort innehållet i elementet. -
I
style.cssskapa en css-regel för.drag-item:emptysom sätteropacitytill0.3ochcursortillauto.
Förslag på lösning
javascript:
function onDrop(event) {
event.preventDefault();
event.target.classList.remove("accept");
event.target.textContent = draggedElement.textContent;
}
CSS:
.drag-item:empty {
opacity: 0.3;
cursor: auto;
}
Testa att dra bokstäverna och släppa dem i en drop-zon. Efterhand som du släpper dem bör de också “förbrukas”.
4 - Villkor för släpp
Slutligen skall vi förhindra släpp i vissa zoner baserat på någon form av vilkor. I det här exemplet skulle det kunna vara rimligt att inte tillåta släpp i zoner som redan har fått en bokstav.
Vi vill alltså förhindra släpp om dropzonen redan innehåller något.
Kom ihåg att det är i dragover som vi bestämmer om det går att släppa eller inte. Se om du kan uppdatera dragover-hanteraren så att den bara tillåter släpp om dropzonen är tom.
Förslag på lösning
I loopen som körs for varje dropzon:
zone.addEventListener('dragover', function(event) {
if(event.target.textContent.length === 0) {
event.preventDefault();
}
});
Testa att släppa flera bokstäver i samma zon. Detta bör nu inte tillåtas. Men nu fungerar inte vår visuella feedback riktigt som den skall.
Fixad visuell feedback
I dragenter lägger vi fortfarande helt ovillkorat in klassen accept i drop-zonen. Den behöver också uppdateras med samma vilkor.
Förslag på lösning
zone.addEventListener('dragenter', function(event) {
if(event.target.textContent.length === 0) {
event.target.classList.add('accept');
}
});
Vi kan göra feedbacken ännu tydligare genom att lägga till en annan klass ifall släpp inte tillåts. Glöm inte att också ta bort den klassen i dragleave.
Förslag på lösning
javascript:
zone.addEventListener('dragenter', function(event) {
if(event.target.textContent.length === 0) {
event.target.classList.add('accept');
}
else {
event.target.classList.add('reject');
}
});
zone.addEventListener("dragleave", function (event) {
event.target.classList.remove("accept", "reject");
});
CSS:
.drop-zone.reject {
background-color: rgb(243, 191, 191);
}