Search code examples
javascripthtmljqueryecmascript-6drag-and-drop

Not able to drop element when creating dynamic divs to drag and drop element in


Following up on my previous question. It works perfectly. But when I dynamically create the divs where the li element is dropped in, I am not able to drop the element in it.

I am using the basic HTML drag and drop feature. Why is the on drop working with dynamically created div?

Here is my code:

    ['harry', 'hermione', 'ron', 'ginny', 'luna'].forEach(met => {
        $(`#char`).append(`
            <div class="form-check py-1">
            <label class="form-check-label">
            <input type="checkbox" class="form-check-input pickTarget" name='checkTarget' value="${met}">${met}
            </label>
            </div>
            `);
    });

    $('.pickTarget').click(function() {
        let unselected = $('.pickTarget:checked').length >= 3;
        $('.pickTarget').not(":checked").attr("disabled", unselected);
    });

    $('.pickTarget').change(function() {
        if ($(this).is(':checked')) $(this).parent().append(`<div style='border: 1px solid red' class='float-right ml-3 text-muted target' id='drop_${this.value}'>Drag Number</div>`);
        else $(`#drop_${this.value}`).remove();
    });

    $('#list').on('dragstart', 'li', function(e) { e.originalEvent.dataTransfer.setData('text', e.target.id); });

    var $target = $('.target');
    var $list = $('#list');

    $target.on('drop', function(e) {
        e.preventDefault();
        let data = e.originalEvent.dataTransfer.getData('text');
        $list.append($(this).html());
        $(this).html($(`#${data}`));
    });

    $target.on('dragover', function(e) { e.preventDefault(); });
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>

<br />
<div id='char' class='w-50 float-left h-50 overflow-auto ml-5'><b>Characters</b> (select 3)</div>

<br />
<ul id="list" class='list-group d-inline-block ml-2'>
    <li id="drag1" class='list-group-item py-1' draggable='true' data-order="1">one</li>
    <li id="drag2" class='list-group-item py-1' draggable='true' data-order="2">two</li>
    <li id="drag3" class='list-group-item py-1' draggable='true' data-order="3">three</li>
</ul>

In my code, once you click on the checkbox next to a Harry Potter character, it creates a div. After selecting the 3 divs (only 3 allowed), divs are created next to the check box. I want to drag each of the one, two & three to each of the divs. Why is it not working?

Here is the fiddle for my code.


Solution

  • Your target divs are dynamically created so you need to bind that with static elements which are already present inside your dom . So , just use $("#char").on('drop', '.target',.. and same for dragover event .

    Also, in your code when user uncheck the checkbox and if there are any li item inside target div they are also get removed . Instead you check if the target div has any class with name list-group-item if yes then just clone that element and then append same to your list and finally remove whole div .

    Demo Code :

    ['harry', 'hermione', 'ron', 'ginny', 'luna'].forEach(met => {
      $(`#char`).append(`
                <div class="form-check py-1">
                <label class="form-check-label">
                <input type="checkbox" class="form-check-input pickTarget" name='checkTarget' value="${met}">${met}
                </label>
                </div>
                `);
    });
    
    $('.pickTarget').click(function() {
      let unselected = $('.pickTarget:checked').length >= 3;
      $('.pickTarget').not(":checked").attr("disabled", unselected);
    });
    
    $('.pickTarget').change(function() {
      if ($(this).is(':checked')) {
        $(this).parent().append(`<div style='border: 1px solid red' class='float-right ml-3 text-muted target' id='drop_${this.value}'><span class='${this.value}'>Drag Number</span></div>`);
      } else {
        //check if the div has li item inside it 
        if ($(`#drop_${this.value}`).find(".list-group-item").length > 0) {
          var cloned = $(`#drop_${this.value}`).find(".list-group-item").clone() //yes clone it
          $("#list ." + this.value).remove(); //remove Drag Number span
          $("#list").append($(cloned)) //append li again in list
        }
        $(`#drop_${this.value}`).remove(); //finally remove div
        //you can write code for sorting lis ..here 
      }
    });
    
    $('#list').on('dragstart', 'li', function(e) {
      e.originalEvent.dataTransfer.setData('text', e.target.id);
    });
    var $list = $('#list');
    //change this
    $("#char").on('drop', '.target', function(e) {
      e.preventDefault();
      let data = e.originalEvent.dataTransfer.getData('text');
      $list.append($(this).html());
      $(this).html($(`#${data}`));
    });
    //change this
    $("#char").on('dragover', '.target', function(e) {
      e.preventDefault();
    });
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
    
    <br />
    <div id='char' class='w-50 float-left h-50 overflow-auto ml-5'><b>Characters</b> (select 3)</div>
    <br />
    <ul id="list" class='list-group d-inline-block ml-2'>
      <li id="drag1" class='list-group-item py-1' draggable='true' data-order="1">one</li>
      <li id="drag2" class='list-group-item py-1' draggable='true' data-order="2">two</li>
      <li id="drag3" class='list-group-item py-1' draggable='true' data-order="3">three</li>
    </ul>