Understanding how drag and drop form builder works in javascript deeply for beginners

Lesson 01 / 06

The fields[] Array — Your Source of Truth

In this modern day, al most all CMS including WordPress and many page builders, form builders etc uses drag and drop interface to build the UI. Have you wondered how it work behind the scene? Today we will explore how drag and drop work in a form builder which is very common. If you understand this, then you can understand how popular form builder plugin works and how Elementor, Gutenberg block editor etc work. Everything in the form builder lives in one JavaScript array. Understanding this is the foundation.

A form builder doesn’t actually move HTML around the page. Instead, it manages a JavaScript array called fields[]. Each item in the array is a plain JavaScript object that describes one form field.

When you drag a field onto the canvas, you add an object to the array. When you delete it, you remove it from the array. The HTML on screen is always just a reflection of what’s in the array.

Key principle: The array is the form. The HTML is just a display. This is called having a single source of truth.
// Each field is just a plain JavaScript object
const field = {
  id: "field_1",        // unique ID
  type: "text",         // what kind of input
  label: "Full Name",   // the visible label
  placeholder: "e.g. John Smith",
  required: true,      // validation
  helpText: ""
};

Click the buttons below to add fields. Watch how the array grows:

// fields[] is empty
let fields = [];
1 of 6
Lesson 02 / 06

The 3 Drag Events You Need

The browser fires events as you drag. You only need to listen to three of them.

dragstart
Fires when the user begins dragging. You record what is being dragged.
dragover
Fires while hovering over a drop target. You must call e.preventDefault() here or the drop won’t work.
drop
Fires when the user releases. You use the recorded info to add a new field to the array.
Drag Source (Palette)
Text Input
draggable=”true”
Drop Zone (Canvas)
Drop here
ondragover + ondrop
// Events will appear here as you drag…
// 1. On the palette item — record what's being dragged
paletteItem.addEventListener('dragstart', function(e) {
  draggedType = 'text'; // save to a variable
});

// 2. On the drop zone — allow the drop
dropZone.addEventListener('dragover', function(e) {
  e.preventDefault(); // THIS IS REQUIRED or drop won't fire
});

// 3. On the drop zone — actually add the field
dropZone.addEventListener('drop', function(e) {
  var newField = { id: 'field_1', type: draggedType };
  fields.push(newField); // add to array
  renderCanvas();         // update the HTML
});
2 of 6
Lesson 03 / 06

The 3-Panel Layout

Every form builder uses the same layout. Once you know it, you can build it with plain CSS Grid.

Left Panel
Field Palette
𝐓 Text
@ Email
▾ Dropdown
☑ Checkbox
⊞ Date
Center — Canvas
Drop Zone
Full Name ✱
Email Address
Drop here…
Right Panel
Settings
Label Full Name
Placeholder Enter…
Required
Help text
Left Panel — The palette of available field types. Each item is draggable.
Center Canvas — The drop zone. Shows a preview of the form being built.
Right Panel — Settings for the selected field. Changes update the array and re-render.
/* The whole thing is just a CSS Grid */
.workspace {
  display: grid;
  grid-template-columns: 240px 1fr 260px;
  height: 100vh;
}

/* Drop zone gets a dashed border when empty */
.drop-zone {
  min-height: 200px;
  border: 2px dashed #ccc;
}

/* Highlight when something is dragged over it */
.drop-zone.drag-over {
  border-color: #4CAF50;
  background: #f0fff4;
}
<div class="workspace">

  <div class="palette">
    <div draggable="true" data-type="text">Text Input</div>
    <div draggable="true" data-type="email">Email</div>
  </div>

  <div class="canvas">
    <div class="drop-zone">
      <!-- field blocks appear here -->
    </div>
  </div>

  <div class="settings-panel">
    <!-- settings for selected field -->
  </div>

</div>
3 of 6
Lesson 04 / 06

renderCanvas() — Turning Data into HTML

One function reads the fields[] array and builds the HTML. You call it every time anything changes.

Canvas (what you see)
// fields[] array (the data)
// empty — add a field
function renderCanvas() {
  var zone = document.getElementById('drop-zone');
  zone.innerHTML = ''; // clear existing HTML

  // Loop through the array and build HTML for each field
  fields.forEach(function(field) {
    var block = document.createElement('div');
    block.className = 'field-block';
    block.innerHTML = `
      <label>${field.label}</label>
      <input type="${field.type}"
             placeholder="${field.placeholder}">
    `;
    zone.appendChild(block);
  });

  // Show placeholder if empty
  if (fields.length === 0) {
    zone.innerHTML = '<p>Drop fields here</p>';
  }
}
Key insight: You never manually update the HTML. You update the fields[] array, then call renderCanvas(). The function rebuilds everything from scratch. This is exactly how React, Vue, and WordPress Gutenberg blocks work.
4 of 6
Lesson 05 / 06

The Settings Panel — Editing a Field’s Data

When you click a field, the settings panel reads that field’s data and shows inputs to edit it.

Settings Panel
// field object in fields[]
{
  “label”: “Full Name”,
  “placeholder”: “e.g. John Smith”,
  “helpText”: “”,
  “required”: false
}
// When a field is clicked — show its settings
function selectField(id) {
  selectedId = id;
  var field = fields.find(f => f.id === id);

  // Populate the settings panel with this field's values
  document.getElementById('input-label').value = field.label;
  document.getElementById('input-placeholder').value = field.placeholder;
  document.getElementById('toggle-required').checked = field.required;
}

// When user types in the settings panel — update the array
document.getElementById('input-label').addEventListener('input', function() {
  var field = fields.find(f => f.id === selectedId);
  field.label = this.value; // update the array
  renderCanvas();          // re-render the canvas
});
The flow: Click field → selectField(id) fills the settings panel → user edits → event listener updates fields[]renderCanvas() redraws the canvas instantly.
5 of 6
Lesson 06 / 06

The Complete Flow — From Drag to Save

Put it all together. Here’s every step from dragging a field to saving the form.

Palette
Item
draggable=”true”
stores type
dragstart
event
draggedType =
‘text’
drop
event
on the canvas
drop zone
fields[]
.push()
new object
added to array
render
Canvas()
array → HTML
on screen
settings
panel
click field
edit its data
JSON.
stringify()
save to DB
or export
<!-- HTML -->
<div class="workspace">
  <div class="palette">
    <div draggable="true" data-type="text">Text</div>
    <div draggable="true" data-type="email">Email</div>
  </div>
  <div id="drop-zone"></div>
  <div id="settings"></div>
</div>

// JavaScript
var fields = [];
var draggedType = null;
var selectedId = null;
var nextId = 1;

// Step 1: Each palette item stores its type on dragstart
document.querySelectorAll('.palette [draggable]').forEach(function(el) {
  el.ondragstart = function() { draggedType = el.dataset.type; };
});

// Step 2: Drop zone accepts the drop
var zone = document.getElementById('drop-zone');
zone.ondragover = function(e) { e.preventDefault(); };
zone.ondrop = function() {
  fields.push({
    id: 'field_' + nextId++,
    type: draggedType,
    label: draggedType + ' field',
    required: false
  });
  renderCanvas();
};

// Step 3: Render the array as HTML
function renderCanvas() {
  zone.innerHTML = fields.map(function(f) {
    return `<div onclick="selectField('${f.id}')">
      <label>${f.label}</label>
      <input type="${f.type}">
    </div>`;
  }).join('');
}

// Step 4: Show settings for the clicked field
function selectField(id) {
  selectedId = id;
  var field = fields.find(f => f.id === id);
  document.getElementById('settings').innerHTML = `
    <input id="s-label" value="${field.label}"
      oninput="updateField('label', this.value)">
  `;
}

// Step 5: Update the field object when settings change
function updateField(key, value) {
  var field = fields.find(f => f.id === selectedId);
  field[key] = value;   // update the array
  renderCanvas();       // re-render
}
fields[] array → stored in post_meta as JSON using update_post_meta()
renderCanvas() → a PHP loop that outputs <input> tags on the front end
Settings panel → a WordPress meta box with standard form inputs, or Gutenberg InspectorControls
JSON.stringify(fields) → submitted via $_POST and saved with update_post_meta()
6 of 6

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *