Using Javascript Drag and Drop to Create an Interactive "Form"
Published: 2021-02-08 01:03:33

With my fourth project at Flatiron School, my cohort lead told us to make our projects as interactive as possible so we can showcase as much Javascript DOM manipulation as we can. I decided to make an ice cream ordering app (partially so that I could doodle out cute ice cream graphics) because I thought it would be really interactive to have a form that, as the user selected their ice cream ingredients on the form, the ice cream would "build" on the side by the images stacking on top of each other. As in, when someone was prompted to choose their base, either a cup or cone image would pop up and then when they selected their flavor from a drop down in the form, an image on that ice cream would pop up inside the cup or cone. After presenting my idea, I was challenged to ditch the form entirely and come up with a more creative way to build my ice creams....

Challenge accepted! ;) Since I was already planning on using images to represent the ice cream order ingredients, I decided that they should all be rendered on the page at once and the user can choose visually which items they want. No drop downs or input elements! In order to accomplish this, I had to use the Drag and Drop API. 

The first thing to do is identify what the draggable element/s are and what element is acting as the dropzone, ie where the draggable item will be released. Mark the draggable element as such by adding `draggable="true"` to the element's tag. I added this to the img tag of each image upon it's creation: 
```
<img id="${toppingType.name}" data-id="${toppingType.id}" data-class="ToppingType" draggable="true" ondragstart="onDragStart(event);" src="./public/images/${toppingType.name}.png" class="builder-image">
```
Now when my ice cream flavors render to the DOM they are draggable. But nothing happens during the drag or on the drop yet. 
![Screen Shot 2021-02-07 at 6.54.57 PM](https://dev-to-uploads.s3.amazonaws.com/i/5xueuu69cezojcuabkib.png)
 

To handle the actual dragging and dropping, I had to use `onevent` handlers. According to MDN, "HTML drag-and-drop uses the DOM event model and drag events inherited from mouse events." There are 8 drag event handlers but the three I used in my project to make my interactive "form" are - `ondragstart`, `ondragover`, and `ondrop`. These event handlers use information from the `dataTransfer` object to keep track of information about the state of the drag through the `setData` method. 

Following the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API) as well as this [helpful guide](https://www.digitalocean.com/community/tutorials/js-drag-and-drop-vanilla-js), I set up a separate .js file to hold my three drag event handlers:

```
function onDragStart(event) {
    event.dataTransfer.setData('text/plain', event.target.id);
}

function onDragOver(event) {
    event.preventDefault();
}

function onDrop(event) {
    const validDropzone = document.getElementById("grid-viewer")

    if (event.target === validDropzone) {

        const id = event.dataTransfer.getData('text')

        const draggableElement = document.getElementById(id)
        const dropzone = event.target
        dropzone.appendChild(draggableElement.cloneNode(true))

    } else {
        event.preventDefault()
    }

    event.dataTransfer.clearData();
```

Then, mark the html element that is serving as the "dropzone" with the ondragover and ondrop attributes as:

```
<div id="grid-viewer" class="grid viewer" ondragover="onDragOver(event);" ondrop="onDrop(event);" hidden></div>
```

That took care of rendering my draggable ice cream ingredient images and handling dragging and dropping them into the dropzone div but it doesn't quite finish up all the duties required of a typical form - it has to submit the data! In my case, this meant sending separate fetch requests to my Rails API backend to persist all of the different ingredients in that particular order. I accomplished this by grabbing all of the children of my dropzone, iterating over that HTML collection and, through a switch statement, firing off fetch requests to the appropriate table in my database, whether it be for the base, flavors, or toppings. And finally, it's ice cream time 🍦 !


Please check out my [repo](https://github.com/madelinemc/cherrys-front-end) for all of the code and, as I am just learning, feel free to leave any comments and suggestions for me :)