Search code examples
javascripthtmlcssdrag-and-drop

Drag and Drop Quiz Vanilla Javascript


I'm currently building a simple drag and drop Quiz but I'm allowed only to use html css and vanilla javascript. The idea is there is a div with the answers and a div with the questions. In the questions text there are some blank divs where you can drop the draggable answers.

For example you have the answers "a,b,c" and "x,y,z" and the question is "The 1st three letters of the alphabet are: ___"

I need help on two main things:

  1. I want to have the question's blank divs allow only one element drop per div. (I can stack them atm)
  2. After drop I want to check if the answers in the current questions divs are correct.

How can I do?

P.S. I'm a newbie on html/css/js so maybe tell me just if it's not possible to impement this without external libraries and php.

/* Events fired on the drag target */

document.addEventListener("dragstart", function(event) {
 // The dataTransfer.setData() method sets the data type and the value of the dragged data
  event.dataTransfer.setData("Text", event.target.id);
  
  // Output some text when starting to drag the p element
 document.getElementById("demo").innerHTML = "Started to drag the p element.";
  
  // Change the opacity of the draggable element
  event.target.style.opacity = "0.4";
});

// While dragging the p element, change the color of the output text
document.addEventListener("drag", function(event) {
  document.getElementById("demo").style.color = "red";
});

// Output some text when finished dragging the p element and reset the opacity
document.addEventListener("dragend", function(event) {
 document.getElementById("demo").innerHTML = "Finished dragging the p element.";
  event.target.style.opacity = "1";
});

/* Events fired on the drop target */

// When the draggable p element enters the droptarget, change the DIVS's border style
document.addEventListener("dragenter", function(event) {
if ( event.target.className == "droptarget" ) {
 event.target.style.border = "3px dotted red";
 }
});

// By default, data/elements cannot be dropped in other elements. To allow a drop, we must prevent the default handling of the element
document.addEventListener("dragover", function(event) {
  event.preventDefault();
});

// When the draggable p element leaves the droptarget, reset the DIVS's border style
document.addEventListener("dragleave", function(event) {
  if ( event.target.className == "droptarget" ) {
    event.target.style.border = "";
  }
});

/* On drop - Prevent the browser default handling of the data (default is open as link on drop)
   Reset the color of the output text and DIV's border color
   Get the dragged data with the dataTransfer.getData() method
   The dragged data is the id of the dragged element ("drag1")
   Append the dragged element into the drop element
*/
document.addEventListener("drop", function(event) {
 event.preventDefault();
 if ( event.target.className == "droptarget" ) {
  document.getElementById("demo").style.color = "";
  event.target.style.border = "hidden";
  var data = event.dataTransfer.getData("Text");
  event.target.appendChild(document.getElementById(data));
  }
});
.droptarget {
    display: inline-block;
    min-width: 50px;
    height: 25px;
    border: 1px solid #aaaaaa;
    color: #000;
    text-align: center;
}
.container {
    display: inline-block;
    padding: 15px;
    margin: 10px;
    background: #eee;
    border: 2px solid black;
    border-radius: 5px;
    box-sizing:border-box;
}
.dragtarget {
    background-color: red;
    padding: 5px;
    border-radius: 5px;
    color: #fff;
    font-weight: bold;
    text-align: center;
}
.domande {
    display: inline-block;
    padding: 15px;
    margin: 10px;
    background: #eee;
    border: 2px solid black;
    border-radius: 5px;
    box-sizing:border-box;
}
<p>Trascina la risposta nel quadrato giusto</p>

<div class="container">
    <p draggable="true" class="dragtarget" id="dragtarget">A,B,C</p>
    <p draggable="true" class="dragtarget" id="dragtarget">1,2,3</p>
</div>

<div class="domande">
    <h3>Prime tre lettere dell'alfabeto<div class="droptarget"></div></h3>
    <h3>Primi tre numeri<div class="droptarget"></div></h3>
</div>
<p id="demo"></p>


Solution

  • Using the same id is really bad, it can only get the first element encounter with getElementById. Instead, I'll capture dragging DOM with dragstart and use it later on drop. In drop you just need to check if is there any child element inside of it. If it does, append that child back to .container.

    You didn't include any details about how you will check so it's hard to help, I can only help you get the question and answer out.

    var dragP;
    /* Events fired on the drag target */
    
    document.addEventListener("dragstart", function (event) {
        // The dataTransfer.setData() method sets the data type and the value of the dragged data
        // event.dataTransfer.setData("Text", event.target.id);
        dragP = event.target;
    
        // Output some text when starting to drag the p element
        document.getElementById("demo").innerHTML = "Started to drag the p element.";
    
        // Change the opacity of the draggable element
        event.target.style.opacity = "0.4";
    });
    
    // While dragging the p element, change the color of the output text
    document.addEventListener("drag", function (event) {
        document.getElementById("demo").style.color = "red";
    });
    
    // Output some text when finished dragging the p element and reset the opacity
    document.addEventListener("dragend", function (event) {
        document.getElementById("demo").innerHTML = "Finished dragging the p element.";
        event.target.style.opacity = "1";
    });
    
    /* Events fired on the drop target */
    
    // When the draggable p element enters the droptarget, change the DIVS's border style
    document.addEventListener("dragenter", function (event) {
        if (event.target.className == "droptarget") {
            event.target.style.border = "3px dotted red";
        }
    });
    
    // By default, data/elements cannot be dropped in other elements. To allow a drop, we must prevent the default handling of the element
    document.addEventListener("dragover", function (event) {
        event.preventDefault();
    });
    
    // When the draggable p element leaves the droptarget, reset the DIVS's border style
    document.addEventListener("dragleave", function (event) {
        if (event.target.className == "droptarget") {
            event.target.style.border = "";
        }
    });
    
    /* On drop - Prevent the browser default handling of the data (default is open as link on drop)
       Reset the color of the output text and DIV's border-color
       Get the dragged data with the dataTransfer.getData() method
       The dragged data is the id of the dragged element ("drag1")
       Append the dragged element into the drop element
    */
    document.addEventListener("drop", function (event) {
        event.preventDefault();
        let targetDiv = event.target;
        if (targetDiv.className == "droptarget") {
            document.getElementById("demo").style.color = "";
            targetDiv.style.border = "hidden";
            if (targetDiv.childElementCount != 0){
                let childP = targetDiv.getElementsByTagName("p")[0];
                document.getElementById("answer").appendChild(childP);
            }
            targetDiv.appendChild(dragP);
            dragP = null;
        }
    });
    
    document.getElementById("checkAnswer").addEventListener("click", function () {
        let questions = document.getElementsByClassName("question");
        let resultP = document.getElementById("result");
        resultP.innerHTML = "";
        for (let index = 0; index < questions.length; index++) {
            const element = questions[index];
            let childP = element.getElementsByTagName("p")[0];
            let question = element.childNodes[0].textContent;
            let answer = childP != undefined ? childP.innerText : "no answer";
            resultP.append(`${question} : ${answer} ; `);
        }
    })
    .droptarget {
        display: inline-block;
        min-width: 50px;
        height: 25px;
        border: 1px solid #aaaaaa;
        color: #000;
        text-align: center;
    }
    
    .container {
        display: inline-block;
        padding: 15px;
        margin: 10px;
        background: #eee;
        border: 2px solid black;
        border-radius: 5px;
        box-sizing: border-box;
    }
    
    .dragtarget {
        background-color: red;
        padding: 5px;
        border-radius: 5px;
        color: #fff;
        font-weight: bold;
        text-align: center;
    }
    
    .domande {
        display: inline-block;
        padding: 15px;
        margin: 10px;
        background: #eee;
        border: 2px solid black;
        border-radius: 5px;
        box-sizing: border-box;
    }
    <p>Trascina la risposta nel quadrato giusto</p>
    
    <div class="container" id="answer">
        <p draggable="true" class="dragtarget" id="dragtarget">A,B,C</p>
        <p draggable="true" class="dragtarget" id="dragtarget">1,2,3</p>
    </div>
    
    <div class="domande">
        <h3 class="question">Prime tre lettere dell'alfabeto<div class="droptarget"></div>
        </h3>
        <h3 class="question">Primi tre numeri<div class="droptarget"></div>
        </h3>
    </div>
    <p id="demo"></p>
    <button id="checkAnswer">Check</button>
    <p id="result"></p>