Hi, working on some code for a client and I’ve seem to hit a wall. Last minute the guy wants me implement drag and drop functionality and I’ve been reading the docs about it on MDN but don’t really have much of a clue.
Here’s the file I’m working on.
app.ts
interface Task {
text: string;
priority: string;
}
let taskList:Task[] = new Array();
// app root
let App = document.getElementById("app-root");
// forms
let low_priority_form = App.querySelectorAll("#form")[0];
let medium_priority_form = App.querySelectorAll("#form")[1];
let high_priority_form = App.querySelectorAll("#form")[2];
let critical_priority_form = App.querySelectorAll("#form")[3];
// lists
let low_priority_list = App.querySelectorAll("#list")[0];
let medium_priority_list = App.querySelectorAll("#list")[1];
let high_priority_list = App.querySelectorAll("#list")[2];
let critical_priority_list = App.querySelectorAll("#list")[3];
///////////////////////////////////////////////////////////////////////////////
// Event Listeners
///////////////////////////////////////////////////////////////////////////////
// Dynamically remove list elements
// https://stackoverflow.com/questions/23835150/javascript-event-listener-for-multiple-buttons-with-same-class-name
App.addEventListener("click", handleClick, false);
// https://stackoverflow.com/questions/11563638/how-do-i-get-the-value-of-text-input-field-using-javascript
low_priority_form.addEventListener("submit", function(e){
e.preventDefault();
let new_text:string = (<HTMLInputElement>low_priority_form.querySelector("#form_input")).value;
let new_priority:string = "low";
let new_task:Task = { text: new_text, priority: new_priority };
taskList.push(new_task);
(<HTMLFormElement>low_priority_form).reset();
draw();
});
medium_priority_form.addEventListener("submit", function(e){
e.preventDefault();
let new_text:string = (<HTMLInputElement>medium_priority_form.querySelector("#form_input")).value;
let new_priority:string = "medium";
let new_task:Task = { text: new_text, priority: new_priority };
taskList.push(new_task);
(<HTMLFormElement>medium_priority_form).reset();
draw();
});
high_priority_form.addEventListener("submit", function(e){
e.preventDefault();
let new_text:string = (<HTMLInputElement>high_priority_form.querySelector("#form_input")).value;
let new_priority:string = "high";
let new_task:Task = { text: new_text, priority: new_priority };
taskList.push(new_task);
(<HTMLFormElement>high_priority_form).reset();
draw();
});
critical_priority_form.addEventListener("submit", function(e){
e.preventDefault();
let new_text:string = (<HTMLInputElement>critical_priority_form.querySelector("#form_input")).value;
let new_priority:string = "critical";
let new_task:Task = { text: new_text, priority: new_priority };
taskList.push(new_task);
(<HTMLFormElement>critical_priority_form).reset();
draw();
});
///////////////////////////////////////////////////////////////////////////////
// Drag and Drop Functions
///////////////////////////////////////////////////////////////////////////////
// https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API
function dragstart_handler(event) {
// Add the target element's id to the data transfer object
event.dataTransfer.setData("text/html", event.target.dataset.id);
console.log("" + event.target.dataset.id);
event.dropEffect = "move";
}
function dragover_handler(event) {
event.preventDefault();
// Set the dropEffect to move
event.dataTransfer.dropEffect = "move";
}
function drop_handler(event) {
event.preventDefault();
// Get the id of the target and add the moved element to the target's DOM
// let data = event.dataTransfer.getData("text");
let data = event.dataTransfer.getData("text/html");
event.target.appendChild(document.getElementById(data));
}
///////////////////////////////////////////////////////////////////////////////
// Utility Functions
///////////////////////////////////////////////////////////////////////////////
function handleClick(event) {
event = event || window.event;
event.target = event.target || event.srcElement;
let element = event.target;
// Climb up the document tree from the target of the event
while (element) {
if (element.nodeName === "BUTTON" && /remove/.test(element.className)) {
// The user clicked on a <button> or clicked on an element inside a <button>
// with a class name called "remove"
let id:number = parseInt(element.dataset.id);
taskList.splice(id, 1);
// then redraw our lists
draw();
break;
}
else if (element.nodeName === "BUTTON" && /move/.test(element.className)) {
let id:number = parseInt(element.dataset.id);
let direction:string = element.dataset.move;
if ( direction.localeCompare("up") ){
changePriority(id, "up");
} else {
changePriority(id, "down");
}
// then redraw our lists
draw();
break;
}
element = element.parentNode;
}
}
function clearAll(){
// https://stackoverflow.com/questions/3955229/remove-all-child-elements-of-a-dom-node-in-javascript
function clear(el){
while ((<HTMLElement>el.firstChild)){
el.removeChild(el.firstChild);
}
}
clear(low_priority_list);
clear(medium_priority_list);
clear(high_priority_list);
clear(critical_priority_list);
}
function changePriority(index: number, direction: string){
let currentPriority = taskList[index].priority;
if (direction.localeCompare("up")){
switch(currentPriority){
case "low" :
taskList[index].priority = "medium";
break;
case "medium" :
taskList[index].priority = "high";
break;
case "high" :
taskList[index].priority = "critical";
break;
case "critical" :
alert("Already at highest possible priority!");
break;
default: alert("I couldn't move anything :(");
}
}
else {
switch(currentPriority){
case "low" :
alert("Already at lowest possible priority!");
break;
case "medium" :
taskList[index].priority = "low";
break;
case "high" :
taskList[index].priority = "medium";
break;
case "critical" :
taskList[index].priority = "high";
break;
default: alert("I couldn't move anything :(");
}
}
}
function listItemTemplate(content: string, Id: number){
return `<li draggable="true" ondragstart="dragstart_handler(event);" data-id="` + Id + `">
<span>`+ content +`</span>
<span class="text-right">
<button type="button" class="move pure-button" title="lower priority" data-move="down" data-id="` + Id + `"><</button>
<button type="button" class="move pure-button" title="raise priority" data-move="up" data-id="` + Id + `">></button>
<button type="button" class="remove pure-button" title="remove this task" aria-label="Close" data-id="` + Id + `">
<span aria-hidden="true">×</span>
</button>
</span>
</li>`
}
function draw(){
let size:number = taskList.length;
let node = document.getElementById("list");
// clear existing lists
clearAll();
// then rebuild
for (let index:number = size - 1; index >= 0; --index){
switch(taskList[index].priority){
case "low":
low_priority_list.innerHTML += listItemTemplate(taskList[index].text, index);
break;
case "medium":
medium_priority_list.innerHTML += listItemTemplate(taskList[index].text, index);
break;
case "high":
high_priority_list.innerHTML += listItemTemplate(taskList[index].text, index);
break;
case "critical":
critical_priority_list.innerHTML += listItemTemplate(taskList[index].text, index);
break;
}
}
}
app.html
<!doctype html>
<html class="no-js" lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Threat Levels</title>
<meta name="description" content="SPA to monitor threat levels">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Roboto:300,300italic,700,700italic">
<link rel="stylesheet" href="//cdn.rawgit.com/necolas/normalize.css/master/normalize.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/milligram/1.3.0/milligram.min.css">
<link rel="stylesheet" href="https://unpkg.com/[email protected]/build/base-min.css">
<link rel="stylesheet" href="https://unpkg.com/[email protected]/build/forms-min.css">
<link rel="stylesheet" href="https://unpkg.com/[email protected]/build/buttons-min.css">
<link rel="stylesheet" href="app.css">
</head>
<body>
<!--[if lte IE 9]>
<div id="browserupgrade" style="text-align:center;">
<p>You are using an <strong>outdated</strong> browser. Please
<a href="https://browsehappy.com/">upgrade your browser</a>
to improve your experience and security.</p>
</div>
<![endif]-->
<div id="app-root" class="app-grid">
<!-- Low Threat List -->
<div id="low" ondrop="drop_handler(event);" ondragover="dragover_handler(event);">
<form id="form" class="pure-form">
<fieldset>
<legend class="">Low</legend>
<input id="form_input" type="text" class="" placeholder="..." aria-label="New Item" required>
<span>
<button type="submit" value="submit" class="button-success pure-button">+</button>
</span>
</fieldset>
</form>
<ol id="list"></ol>
</div>
<!-- Medium Threat List -->
<div id="medium" ondrop="drop_handler(event);" ondragover="dragover_handler(event);">
<form id="form" class="pure-form">
<fieldset>
<legend class="secondary">Medium</legend>
<input id="form_input" type="text" class="" placeholder="..." aria-label="New Item" required>
<span>
<button type="submit" value="submit" class="button-success pure-button">+</button>
</span>
</fieldset>
</form>
<ol id="list"></ol>
</div>
<!-- High Threat List -->
<div id="high" ondrop="drop_handler(event);" ondragover="dragover_handler(event);">
<form id="form" class="pure-form">
<fieldset>
<legend class="warning">High</legend>
<input id="form_input" type="text" class="" placeholder="..." aria-label="New Item" required>
<span>
<button type="submit" value="submit" class="button-success pure-button">+</button>
</span>
</fieldset>
</form>
<ol id="list"></ol>
</div>
<!-- Critical Threat List -->
<div id="critical" ondrop="drop_handler(event);" ondragover="dragover_handler(event);">
<form id="form" class="pure-form">
<fieldset>
<legend class="error">Critical</legend>
<input id="form_input" type="text" class="" placeholder="..." aria-label="New Item" required>
<span>
<button type="submit" value="submit" class="button-success pure-button">+</button>
</span>
</fieldset>
</form>
<ol id="list"></ol>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
Basically the way I’ve designed it each list item is an interface with two properties: text and priority.
What I need to do is, when a list item is dragged into a div with other list items, on drop it needs its priority changed adn then I can just call my draw()
function as it rebuilds each priority area .
When I click and drag a list item now I get this error:
TypeError: Argument 1 of Node.appendChild is not an object.
Maybe I just need to sit down and think about this some more.
Any thoughts are welcome.