Search code examples
javascripthtmlscrolldrag-and-drop

How to achieve scroll with drag options inside table with sticky header and scroll for tbody


I have a table with sticky header and vertical scroll. Below is the image for that.enter image description here

Inside td i have div containing data(objects), Div's are drag-able and they are working fine. The issue is that there could be some time 50+ rows in the table, and user is allowed to move data from 1st row to the last row by dragging. if we drag data to bottom,

 $('table td').on("dragover", function (event) { 
           event.preventDefault();                   
          event.target.classList.add("highlight");
          $('html, body').animate({scrollTop:event.target.offsetTop},50);  /* i am trying to auto scroll up and down, it just some time scroll down, but never up*/
          
       });

table rows scroll down too quick with a jerk. Also if you want to move some rows towards upside it never goes up. enter image description here

After you drop one, if you try to go up, Page keep scrolling down and never let you go up. So i want a smooth scroll up and down with jQuery(not using jQuery UI dragable and dropable as that does not work for me) or vanilla script. Here is my script sample

var index = 0;
var dragSrcEl = null;
var row = 1;
var lastDragOver = null;

$(document).ready(function() {
  $('.event').on("dragstart", function(event) {
    var dt = event.originalEvent.dataTransfer;
    dt.setData('Text', $(this).attr('id'));
  });
  $('table td').on("drop", function(event) {
    event.preventDefault();
    if (event.type === 'drop') {

      $('.highlight').removeClass('highlight');
      var data = event.originalEvent.dataTransfer.getData('Text', $(this).attr('id'));
      if ($(this).find('span').length === 0) {
        de = $('#' + data).detach();
        de.appendTo($(this));
      }
    }
  });

  $('table td').on("dragover", function(event) {
    event.preventDefault();
    event.target.classList.add("highlight");
    $('html, body').animate({
      scrollTop: event.target.offsetTop
    }, 50); /* i am trying to auto scroll up and down, it just some time scroll down, but never up*/

  });
  $('table td').on("dragleave", function(event) {
    event.preventDefault();
    event.target.classList.remove("highlight");

  });
});
th {
  border: 1px solid #acadb0;
  font-weight: bold;
  color: black;
  font-size: 14px;
}

td {
  border: 1px solid #ccc;
  height: 45px;
  /*min-width:150px;*/
}

.event:hover {
  border: 1px solid #ccc;
  border-style: dashed;
}

.highlight {
  border: 1px solid yellow;
  background-color: yellow;
}

.container .row:first {
  left: -27px !important;
}

.col-md-12 {
  padding-left: 0px !important;
}

#secondHeader {
  padding: 10px 16px 0 16px;
  background: #FFFFFF;
  z-index: 999;
  width: 100%;
  border-top: 1px solid #ccc;
  border-bottom: 1px solid #000;
}

#myTableDiv {
  position: relative;
  top: 26px;
}

#myTable thead th {
  position: sticky;
  top: 0px;
  z-index: 200;
  background: #fff;
  border-top: 1px solid #000;
  border-bottom: 1px solid #000;
  padding-left: 0px;
}
<script src="https://www.dev.enterpriseplus.tools/sw/assets/jqtree/jquery.min.js"></script>
<div data-ng-app="LeadApp" class="row" style="">
  <div id="secondHeader" style="padding-left:10px;top: 0px;position: fixed;">
    Rearrange Objects Childs
  </div>
  <div id="myTableDiv" style="margin-right: -15px;">
    <table id="myTable" class="table table-bordered" style="border-collapse: separate;">
      <thead style="background-color: #f5f6f7; position: sticky; top: 0;">
        <tr>
          <th scope="col">Class Type (Level 1)</th>
          <th scope="col">Stereotype (Level 2)</th>
          <th scope="col">Type (Level 3)</th>
          <th scope="col">Subtype (Level 4)</th>
          <th scope="col">Sub-subtype (Level 5)</th>
          <th scope="col">Sub-sub-subtype (Level 6)</th>
        </tr>
      </thead>
      <tbody id="tablebody">
        <tr>
          <td row="1" column="1" class="dropable" ondrop="drop(event)" ondragover="allowDrop(event)">
            <div draggable="true" class="event" id="33494" style="cursor: pointer;">mr 1</div>
          </td>
          <td row="1" column="2" class="dropable"></td>
          <td row="1" column="3" class="dropable"></td>
          <td row="1" column="4" class="dropable"></td>
          <td row="1" column="5" class="dropable"></td>
          <td row="1" column="6" class="dropable"></td>
        </tr>
        <tr>
          <td row="2" column="1" class="nodrop disable"></td>
          <td row="2" column="2" class="dropable" ondrop="drop(this, event)" ondragover="allowDrop(this, event)">
            <div draggable="true" class="event" id="33496" style="cursor: move;">mr 2</div>
          </td>
          <td row="2" column="3" class="dropable"></td>
          <td row="2" column="4" class="dropable"></td>
          <td row="2" column="5" class="dropable"></td>
          <td row="2" column="6" class="dropable"></td>
        </tr>
        <tr>
          <td row="3" column="1" class="nodrop disable"></td>
          <td row="3" column="2" class="dropable"></td>
          <td row="3" column="3" class="dropable" ondrop="drop(this, event)" ondragover="allowDrop(this, event)">
            <div draggable="true" class="event" id="33495" style="cursor: move;">mr 3</div>
          </td>
          <td row="3" column="4" class="dropable"></td>
          <td row="3" column="5" class="dropable"></td>
          <td row="3" column="6" class="dropable"></td>
        </tr>
        <tr>
          <td row="4" column="1" class="nodrop disable"></td>
          <td row="4" column="2" class="dropable"></td>
          <td row="4" column="3" class="dropable"></td>
          <td row="4" column="4" class="dropable" ondrop="drop(this, event)" ondragover="allowDrop(this, event)">
            <div draggable="true" class="event" id="33498" style="cursor: move;">mr 4</div>
          </td>
          <td row="4" column="5" class="dropable">
          </td>
          <td row="4" column="6" class="dropable"></td>
        </tr>
        <tr>
          <td row="5" column="1" class="nodrop disable"></td>
          <td row="5" column="2" class="dropable">
          </td>
          <td row="5" column="3" class="dropable"></td>
          <td row="5" column="4" class="dropable"></td>
          <td row="5" column="5" class="dropable" ondrop="drop(this, event)" ondragover="allowDrop(this, event)">
            <div draggable="true" class="event" id="33499" style="cursor: move;">mr 5</div>
          </td>
          <td row="5" column="6" class="dropable"></td>
        </tr>
        <tr>
          <td row="6" column="1" class="nodrop disable"></td>
          <td row="6" column="2" class="dropable"></td>
          <td row="6" column="3" class="dropable">
            <div draggable="true" class="event" id="33495" style="cursor: move;">mr 3</div>
          </td>
          <td row="6" column="4" class="dropable"></td>
          <td row="6" column="5" class="dropable"></td>
          <td row="6" column="6" class="dropable" ondrop="drop(this, event)" ondragover="allowDrop(this, event)">
            <div draggable="true" class="event" id="33500" style="cursor: move;">mr 6</div>
          </td>
        </tr>
        <tr>
          <td row="7" column="1" class="nodrop disable"></td>
          <td row="7" column="2" class="dropable"></td>
          <td row="7" column="3" class="dropable"></td>
          <td row="7" column="4" class="dropable"></td>
          <td row="7" column="5" class="dropable"></td>
          <td row="7" column="6" class="dropable" ondrop="drop(this, event)" ondragover="allowDrop(this, event)">
            <div draggable="true" class="event" id="33497" style="cursor: move;">mr 2 (M yousaf)</div>
          </td>
        </tr>
        <tr>
          <td row="8" column="1" class="nodrop disable"></td>
          <td row="8" column="2" class="dropable"></td>
          <td row="8" column="3" class="dropable" ondrop="drop(this, event)" ondragover="allowDrop(this, event)">
            <div draggable="true" class="event" id="33501" style="cursor: move;">mr 3.1</div>
          </td>
          <td row="8" column="4" class="dropable"></td>
          <td row="8" column="5" class="dropable"></td>
          <td row="8" column="6" class="dropable"></td>
        </tr>
        <tr>
          <td row="9" column="1" class="nodrop disable"></td>
          <td row="9" column="2" class="dropable"></td>
          <td row="9" column="3" class="dropable"></td>
          <td row="9" column="4" class="dropable" ondrop="drop(this, event)" ondragover="allowDrop(this, event)">
            <div draggable="true" class="event" id="33502" style="cursor: move;">mr 4.1</div>
          </td>
          <td row="9" column="5" class="dropable"></td>
          <td row="9" column="6" class="dropable"></td>
        </tr>
        <tr>
          <td row="10" column="1" class="nodrop disable"></td>
          <td row="10" column="2" class="dropable"></td>
          <td row="10" column="3" class="dropable"></td>
          <td row="10" column="4" class="dropable"></td>
          <td row="10" column="5" class="dropable" ondrop="drop(this, event)" ondragover="allowDrop(this, event)">
            <div draggable="true" class="event" id="33503" style="cursor: move;">mr 5.1</div>
          </td>
          <td row="10" column="6" class="dropable"></td>
        </tr>
        <tr>
          <td row="11" column="1" class="nodrop disable"></td>
          <td row="11" column="2" class="dropable"></td>
          <td row="11" column="3" class="dropable"></td>
          <td row="11" column="4" class="dropable"></td>
          <td row="11" column="5" class="dropable"></td>
          <td row="11" column="6" class="dropable" ondrop="drop(this, event)" ondragover="allowDrop(this, event)">
            <div draggable="true" class="event" id="33504" style="cursor: move;">mr 6.1</div>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
  <div style="padding-left:10px;padding-top: 20px;">
    <button class="btn btn-primary" onClick="saveObjects()" tabindex="0">
                <i class="fa fa-save"></i> Save Order
            </button>

    <span id="message" style="display:none">&nbsp;&nbsp;&nbsp;<img src="https://www.dev.enterpriseplus.tools/sw/../../images/ajax-loader.gif" style="width:20px;" alt="loading..."/></span>
  </div>
</div>

I have tried jQuery Ui Dragable and dropable, but that was not keeping the data inside the td tag of table, i can move it any where in the page, which is not current requirement, also that was not working with scroll:true for drageable. I have tried many other options and solution but nothing worked for me. I need vanilla js script. I am not an expert in fronend


Solution

  • You can use CSS grid to control the layout of the header, table and footer, and give the table 1fr of the height. Now, the <thead> can be sticky and with overflow: scroll the table will scroll when you start dragging elements up or down the table.

    var index = 0;
    var dragSrcEl = null;
    var row = 1;
    var lastDragOver = null;
    
    $(document).ready(function() {
      $('.event').on("dragstart", function(event) {
        var dt = event.originalEvent.dataTransfer;
        dt.setData('Text', $(this).attr('id'));
      });
      $('table td').on("drop", function(event) {
        event.preventDefault();
        if (event.type === 'drop') {
    
          $('.highlight').removeClass('highlight');
          var data = event.originalEvent.dataTransfer.getData('Text', $(this).attr('id'));
          if ($(this).find('span').length === 0) {
            de = $('#' + data).detach();
            de.appendTo($(this));
          }
        }
      });
    
      $('table td').on("dragover", function(event) {
        event.preventDefault();
        event.target.classList.add("highlight");
        $('html, body').animate({
          scrollTop: event.target.offsetTop
        }, 50); /* i am trying to auto scroll up and down, it just some time scroll down, but never up*/
    
      });
      $('table td').on("dragleave", function(event) {
        event.preventDefault();
        event.target.classList.remove("highlight");
    
      });
    });
    body {
      padding: 0;
      margin: 0;
    }
    
    div.row {
      display: grid;
      grid-template-columns: auto;
      grid-template-rows: auto 1fr auto;
      height: 100vh;
    }
    
    #secondHeader {
      grid-column: 1;
      grid-row: 1;
    }
    
    #myTableDiv {
      grid-column: 1;
      grid-row: 2;
      overflow: scroll;
    }
    
    footer {
      grid-column: 1;
      grid-row: 3;
      padding: .5em;
    }
    
    th {
      border: 1px solid #acadb0;
      font-weight: bold;
      color: black;
      font-size: 14px;
    }
    
    td {
      border: 1px solid #ccc;
      height: 45px;
      /*min-width:150px;*/
    }
    
    .event:hover {
      border: 1px solid #ccc;
      border-style: dashed;
    }
    
    .highlight {
      border: 1px solid yellow;
      background-color: yellow;
    }
    
    .container .row:first {
      left: -27px !important;
    }
    
    .col-md-12 {
      padding-left: 0px !important;
    }
    
    #secondHeader {
      padding: 10px 16px 0 16px;
      border-top: 1px solid #ccc;
      border-bottom: 1px solid #000;
    }
    
    #myTable thead th {
      position: sticky;
      top: 0px;
      z-index: 200;
      background: #fff;
      border-top: 1px solid #000;
      border-bottom: 1px solid #000;
      padding-left: 0px;
    }
    <script src="https://www.dev.enterpriseplus.tools/sw/assets/jqtree/jquery.min.js"></script>
    <div data-ng-app="LeadApp" class="row">
      <div id="secondHeader">
        Rearrange Objects Childs
      </div>
      <div id="myTableDiv">
        <table id="myTable" class="table table-bordered" style="border-collapse: separate;">
          <thead style="background-color: #f5f6f7; position: sticky;">
            <tr>
              <th scope="col">Class Type (Level 1)</th>
              <th scope="col">Stereotype (Level 2)</th>
              <th scope="col">Type (Level 3)</th>
              <th scope="col">Subtype (Level 4)</th>
              <th scope="col">Sub-subtype (Level 5)</th>
              <th scope="col">Sub-sub-subtype (Level 6)</th>
            </tr>
          </thead>
          <tbody id="tablebody">
            <tr>
              <td row="1" column="1" class="dropable" ondrop="drop(event)" ondragover="allowDrop(event)">
                <div draggable="true" class="event" id="33494" style="cursor: pointer;">mr 1</div>
              </td>
              <td row="1" column="2" class="dropable"></td>
              <td row="1" column="3" class="dropable"></td>
              <td row="1" column="4" class="dropable"></td>
              <td row="1" column="5" class="dropable"></td>
              <td row="1" column="6" class="dropable"></td>
            </tr>
            <tr>
              <td row="2" column="1" class="nodrop disable"></td>
              <td row="2" column="2" class="dropable" ondrop="drop(this, event)" ondragover="allowDrop(this, event)">
                <div draggable="true" class="event" id="33496" style="cursor: move;">mr 2</div>
              </td>
              <td row="2" column="3" class="dropable"></td>
              <td row="2" column="4" class="dropable"></td>
              <td row="2" column="5" class="dropable"></td>
              <td row="2" column="6" class="dropable"></td>
            </tr>
            <tr>
              <td row="3" column="1" class="nodrop disable"></td>
              <td row="3" column="2" class="dropable"></td>
              <td row="3" column="3" class="dropable" ondrop="drop(this, event)" ondragover="allowDrop(this, event)">
                <div draggable="true" class="event" id="33495" style="cursor: move;">mr 3</div>
              </td>
              <td row="3" column="4" class="dropable"></td>
              <td row="3" column="5" class="dropable"></td>
              <td row="3" column="6" class="dropable"></td>
            </tr>
            <tr>
              <td row="4" column="1" class="nodrop disable"></td>
              <td row="4" column="2" class="dropable"></td>
              <td row="4" column="3" class="dropable"></td>
              <td row="4" column="4" class="dropable" ondrop="drop(this, event)" ondragover="allowDrop(this, event)">
                <div draggable="true" class="event" id="33498" style="cursor: move;">mr 4</div>
              </td>
              <td row="4" column="5" class="dropable">
              </td>
              <td row="4" column="6" class="dropable"></td>
            </tr>
            <tr>
              <td row="5" column="1" class="nodrop disable"></td>
              <td row="5" column="2" class="dropable">
              </td>
              <td row="5" column="3" class="dropable"></td>
              <td row="5" column="4" class="dropable"></td>
              <td row="5" column="5" class="dropable" ondrop="drop(this, event)" ondragover="allowDrop(this, event)">
                <div draggable="true" class="event" id="33499" style="cursor: move;">mr 5</div>
              </td>
              <td row="5" column="6" class="dropable"></td>
            </tr>
            <tr>
              <td row="6" column="1" class="nodrop disable"></td>
              <td row="6" column="2" class="dropable"></td>
              <td row="6" column="3" class="dropable">
                <div draggable="true" class="event" id="33495" style="cursor: move;">mr 3</div>
              </td>
              <td row="6" column="4" class="dropable"></td>
              <td row="6" column="5" class="dropable"></td>
              <td row="6" column="6" class="dropable" ondrop="drop(this, event)" ondragover="allowDrop(this, event)">
                <div draggable="true" class="event" id="33500" style="cursor: move;">mr 6</div>
              </td>
            </tr>
            <tr>
              <td row="7" column="1" class="nodrop disable"></td>
              <td row="7" column="2" class="dropable"></td>
              <td row="7" column="3" class="dropable"></td>
              <td row="7" column="4" class="dropable"></td>
              <td row="7" column="5" class="dropable"></td>
              <td row="7" column="6" class="dropable" ondrop="drop(this, event)" ondragover="allowDrop(this, event)">
                <div draggable="true" class="event" id="33497" style="cursor: move;">mr 2 (M yousaf)</div>
              </td>
            </tr>
            <tr>
              <td row="8" column="1" class="nodrop disable"></td>
              <td row="8" column="2" class="dropable"></td>
              <td row="8" column="3" class="dropable" ondrop="drop(this, event)" ondragover="allowDrop(this, event)">
                <div draggable="true" class="event" id="33501" style="cursor: move;">mr 3.1</div>
              </td>
              <td row="8" column="4" class="dropable"></td>
              <td row="8" column="5" class="dropable"></td>
              <td row="8" column="6" class="dropable"></td>
            </tr>
            <tr>
              <td row="9" column="1" class="nodrop disable"></td>
              <td row="9" column="2" class="dropable"></td>
              <td row="9" column="3" class="dropable"></td>
              <td row="9" column="4" class="dropable" ondrop="drop(this, event)" ondragover="allowDrop(this, event)">
                <div draggable="true" class="event" id="33502" style="cursor: move;">mr 4.1</div>
              </td>
              <td row="9" column="5" class="dropable"></td>
              <td row="9" column="6" class="dropable"></td>
            </tr>
            <tr>
              <td row="10" column="1" class="nodrop disable"></td>
              <td row="10" column="2" class="dropable"></td>
              <td row="10" column="3" class="dropable"></td>
              <td row="10" column="4" class="dropable"></td>
              <td row="10" column="5" class="dropable" ondrop="drop(this, event)" ondragover="allowDrop(this, event)">
                <div draggable="true" class="event" id="33503" style="cursor: move;">mr 5.1</div>
              </td>
              <td row="10" column="6" class="dropable"></td>
            </tr>
            <tr>
              <td row="11" column="1" class="nodrop disable"></td>
              <td row="11" column="2" class="dropable"></td>
              <td row="11" column="3" class="dropable"></td>
              <td row="11" column="4" class="dropable"></td>
              <td row="11" column="5" class="dropable"></td>
              <td row="11" column="6" class="dropable" ondrop="drop(this, event)" ondragover="allowDrop(this, event)">
                <div draggable="true" class="event" id="33504" style="cursor: move;">mr 6.1</div>
              </td>
            </tr>
          </tbody>
        </table>
      </div>
      <footer>
        <button class="btn btn-primary" onClick="saveObjects()" tabindex="0">
          <i class="fa fa-save"></i> Save Order
        </button>
        <span id="message" style="display: none;">&nbsp;&nbsp;&nbsp;<img src="https://www.dev.enterpriseplus.tools/sw/../../images/ajax-loader.gif" style="width:20px;" alt="loading..."/></span>
      </footer>
    </div>