Im creating a web builder, Im using svelte to facilitate some of the functionality of the UI but the web builder itself is almost pure javascript. I created a tool panel in whith diferent dragable elements that are created proramatically so that they can be droped on the canvas (its a div but i call it canvas for structual purposes). However to create the html elements in the canvas I was thinking of passing the tag name as a text and have it create a new element of that type inside the canvas.
The drop is working and the elements are in deed dragable however the issue is with the ondragstart property. I've also tried adding an event listener to the element and this still doesnt work. There are three main components in this operation. 1st the tool panel which is the following. This is where the user will see his tools and be able to drag them into the canvas
<script lang="ts">
import { onMount } from "svelte";
import {define_default_styles, define_default_content} from "$lib/helpers/default_element_configurations";
let hidden:boolean = true
let element_declaration:string[] = ["div", "h1", "h2", "p", "form", "input", "button"]
let elements: (string | undefined)[] = [];
let element_panel:HTMLElement
onMount(()=>{
for(let element of element_declaration){
let newElement = document.createElement(element)
newElement.className="rounded p-2 border"
newElement.draggable=true
newElement.ondragstart=(event)=>{
console.log("dragstart event fired");
event.dataTransfer?.setData("text/plain", "div");
console.log("Data set: ", event.dataTransfer?.getData("text/plain"));
}
define_default_styles(newElement,element)
define_default_content(newElement,element)
elements.push(newElement.outerHTML)
}
})
</script>
<div class="flex items-center space-x-2 p-2 bg-gray-200">
<h1 class="text-blue-500 font-bold text-xl">Scripter Web-Builder</h1>
<button on:click={()=>{hidden=false}} class="bg-blue-500 text-white rounded text-center px-2 text-xl font-bold py-1 hover:bg-blue-700 active:bg-blue-900">+</button>
</div>
{#if !hidden}
<div bind:this={element_panel} class="absolute border-2 w-1/4 min-h-52 bg-gray-50 rounded shadow-md">
{#each elements as element}
<div class="p-2">{@html element}</div>
{/each}
</div>
{/if}
This is a helper function that adds aditional functionality, this is where i tried to add the event listener. I already deleted the event listenr in the code but ill add it again for ilustrative porpuses `
export function define_default_content(node:HTMLElement, node_type:string){
switch(node_type){
case "div":
node.innerText="div"
node.addEventListener("dragstart" ,(event)=>{
console.log("dragstart event fired");
event.dataTransfer?.setData("text/plain", "div");
console.log("Data set: ", event.dataTransfer?.getData("text/plain"));
})
node.addEventListener("drop", (event)=>{event.stopPropagation()})
break;
finally this is the canvas which does work as advertised
<script lang="ts">
import {onMount} from "svelte"
let canvas:HTMLElement
onMount(()=>{
canvas.addEventListener("dragenter",(e)=>e.preventDefault())
canvas.addEventListener("dragover",(e)=>e.preventDefault())
canvas.addEventListener("drop",(e)=>console.log(e.dataTransfer?.getData("text/plain")))
})
</script>
<div bind:this={canvas}>
</div>
It is best to avoid manual DOM manipulation, among other things this can cause interference with mounting/unmounting of components.
For something like this, arbitrary elements can be added via {#each}
and <svelte:element>
.