Drag and Drop HTML components with jQuery

Asked

Viewed 1,067 times

3

I am developing a project, for my academic course and I have the following code:

// Esse é um json que vem do banco de dados
var Componentes = {"input": {"Label": "Textbox","Tag": "input",
"Attributes": {"type": "text"},"Class": "form-control"},"btn": 
{"Label": "Button","Tag": "button","Attributes": {"type": "button"},
"Class": "btn","Childrens":[{"Type": "text","Value": "Botão"}]},
"label": {"Label": "Label","Tag": "label","Class": "","Childrens":[
{"Type": "text","Value": "Label:"}]},"form-help":{"Label": "Form help",
"Tag": "span","Class": "help-block","Childrens":[{"Type": "text",
"Value": "I'm a help text."}]},"textbox": {"Label": "Textbox Group",
"Tag": "div","Class": "form-group","Siblings":[],"Childrens":[
{"Type": "component","Value":"label"}, {"Type": "component",
"Value":"input"},{"Type": "component","Value": "form-help"}],
"Rules": {"AllowChilds": false}}};


// Evento dos botões, tem que implementar um evento de Drag n Drop
$(document).on('click', '#componentes .add', function(event){
   event.preventDefault();
   AddComponent( $(this).data('componente') );
});

// Método que adiciona o componente
function AddComponent(componente, retorna){
   // Parâmetro para recursividade (adicionar elementos filhos)
   retorna = retorna == undefined ? false : retorna;

   // Componente escolhido foi armazenado na variável c
   var c = Componentes[componente];

   // Objeto para registro dos componentes, pode ignorar
   var cmp = {
      'CID': Date.now(),
      'Type': componente
   };

   // Elemento criado 
   var $element = $('<'+c.Tag+' />');
   $element.addClass(c.Class+" component "+c.EditClass);

   // Adiciona todos os atributos padrões no elemento
   if (c.Attributes != undefined && c.Attributes.length > 0)
      for(attr in c.Attributes)
         $element.attr(attr, c.Attributes[attr]);

   // Atributo de controle de edição, pode ignorar
   $element.attr('data-component', cmp.Type)
         .attr('data-cid', cmp.CID);

   // Adiciona todos os elementos filhos
   if (c.Childrens != undefined && c.Childrens.length > 0){
      for(children in c.Childrens){

         switch(c.Childrens[children].Type){
            case "component":
               $element.append( AddComponent(c.Childrens[children].Value, true) );
               break;
            case "text":
               $element.text( c.Childrens[children].Value );
               break;
         }

      }
   }

   // Verifica se é pra adicionar a área de design ou retornar o elemento
   // para ser adicionado a um elemento pai
   if (retorna)
      return $element;
   else
      $('#edit-area .active').append($element);
}
/* CSS meramente para demostração*/
#edit-area {
  display: block;
  margin-bottom:20px;
  box-sizing: content-box;
  min-height: 200px;
  position: relative;
  overflow: auto;
  background: #E0E0E0;
}

.row{
    background:white;
    box-shadow: 2px 2px 5px rgba(0,0,0,0.2);
    margin:10px;
    padding:5px;
    min-height:30px;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="row">
   <div class="col-lg-9 col-md-9">
      <div id="edit-area">
          <div class="row">
              <div class="col-lg-12 col-md-12 active"></div>
          </div>
      </div>
   </div>
   <div class="col-lg-3 col-md-3" id="componentes">
      <div class="list-group">
        <button class="add list-group-item" data-componente="textbox">Textbox Group</button>
        <button class="add list-group-item" data-componente="input">Input</button>
        <button class="add list-group-item" data-componente="btn">Botão</button>
      </div>
   </div>
</div>

The code is merely for demonstration, testing and implementations, the actual code is a "little bit" larger. Here only has the essential for the creation of the element(s) (s).

With this code I can create a component by clicking on a button in the list. My goal is to use the style Drag n Drop, click on a button and drag to the area I want.

Kind of like that.

How could I do this using only jQuery (without using plugins or other libraries). If possible have option to reposition them after being created.

Fiddler

There is no problem to create new elements for component handling, because the components are stored in objects (with ids, types, properties and etc.) and then saved in the database the generated HTML elements are only for the designer’s look.

  • Can you use jQuery-UI? or do you really want nothing but jQuery.js?

  • Just the same jQuery @Sergio.

  • @Sergio has no problem creating tags extras to involve the component. I’m leaving the service now, when I get home I’ll give you a comment in the code if you need.

1 answer

3

This question is a little broad because this functionality is relatively complex. Still, it is very useful and I leave here a suggestion on the drag & drop part.

To drag an element you need to know several things:

  • mouse position
  • the element that is dragging
  • the position of the destination element

When dropping the element we need to know if it is inside the destination element to confirm or cancel the drag.

So I created functions for what I mentioned above and global variables that keep "the state of things".

$(document).ready(function () {
    var targetEl = document.getElementById('edit-area'); // elemento destino
    var target = targetEl.getBoundingClientRect(); // dimensões 
    var startPosition; // para guardar a posição inicial do elemento caso queiramos cancelar
    var dragging = null; // flag para saber se há drag ativo
    var offset; // diferença entre canto do elemento e onde o mouse tocou

    function offsetDiff(el, ev) { // calcular diferença entre canto do elemento e onde o mouse tocou
        var elPos = el.getBoundingClientRect();
        return {
            x: ev.pageX - elPos.left,
            y: ev.pageY - elPos.top
        }
    }

    function isInsideTarget(el) { // saber se elemento arrastado está dentro do elemento destino
        var elPos = el.getBoundingClientRect();
        var alignedX = target.left <= elPos.left && target.right >= elPos.right;
        var alignedY = target.top <= elPos.top && target.bottom >= elPos.bottom;
        if (alignedX && alignedY) return true;
        return false;
    }

    function drop(el, cancel) { // largar ou cancelar
        dragging = null;
        el.style.position = 'relative';
        el.style.left = 'auto';
        el.style.top = 'auto';
        if (cancel) return;
        el.classList.remove('add');
        $(targetEl).append(el);
    }

    $(document.body).on("mousemove", function (e) {
        if (!dragging) return;
        $(dragging).offset({
            top: e.pageY - offset.y,
            left: e.pageX - offset.x
        });

    });


    $(document.body).on('mousedown', '.add', function (e) {
        dragging = e.target;
        offset = offsetDiff(dragging, e);
        startPosition = dragging.getBoundingClientRect();

    });

    $(document.body).on("mouseup", function (e) {
        if (!dragging) return;
        var isDroppable = isInsideTarget(dragging);
        drop(dragging, !isDroppable);
    });
});

jsFiddle: http://jsfiddle.net/anuuascq/

  • Sergio, it is not wide, my doubt is how to drag the element that is created by my function instead of the button. If you were just dragging the button it would be much simpler.

  • @Kaduamaral the function you refer to is AddComponent? With my code taking the class .add new elements are draggable. My answer helps or failed at what you need?

  • I haven’t had time to test, maybe Monday I’ll give you a feedback

  • I made a update on your Jsfiddle to more or less what I need. I was able to implement to create the element and drag it, but I’m having to click 2x has as already in the first click do this? Another thing, I have several drop areas, but when I play in one falls in all. Another problem is a discrepancy of the position of the element with the cursor.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.