Search code examples
datatables

How to make the correct page selection in datatables when selecting a row?


The code I'm working with will highlight the corresponding row in the data tables after clicking on the map marker.

The problem is that when sorting is applied, the selection of the page with the selected row is incorrect or does not occur at all.

If I cancel the pagination by displaying all the lines, then the line selection works perfectly fine. How to make correct page selection when selecting row after sorting?

 // Add markers and event listeners
  for (var i = 0; i < dataArray.length; i++) {
    var marker = L.marker([dataArray[i][5], dataArray[i][6]]).addTo(map);
    marker.bindPopup("Book: " + dataArray[i][2] + "<br>" + "Pages: " + dataArray[i][3]);

var currentMarker = null;
var currentTableRow = null;

marker.on("click", function(e) {
   var popup = e.target.getPopup();
   var content = popup.getContent();
   var table = $('#data-table').DataTable();
   var rowData = table.rows().data().toArray();

   // Find the row that matches the content of the popup
   var tr = null;
   for (var i = 0; i < rowData.length; i++) {
     if (rowData[i][2] == content.split("<br>")[0].split(": ")[1]) {
       // Use the "index" option to get the index of the row in the original loading order
       var index = table.column(2, {order:'index'}).data().indexOf(rowData[i][2]);
       tr = table.row(index).node();
       break;
     }
   }

   if (tr !== null) {
     // Add a "highlight" class to the new table row
     $(tr).addClass("highlight");

     // Remove the "highlight" class from the previous table row (if any)
     if (currentTableRow !== null) {
       $(currentTableRow).removeClass("highlight");
     }

     // Update the currently selected marker and its corresponding table row
     currentMarker = marker;
     currentTableRow = tr;

     // Get the page index of the selected row
     var pageInfo = table.page.info();
     var rowPage = Math.floor(table.row(tr).index() / pageInfo.length);

     // Set the page index as the active page
     table.page(rowPage).draw(false);
   }
});


  }

Solution

  • Solution:

    Instead of this:

    tr = table.row(index).node();
    

    use this:

    tr = table.row(index, {order:'index'}).node();
    

    Explanation:

    Your code is using the following:

    var index = table.column(2, {order:'index'})
    

    This retrieves the index value assigned to the row in DataTables. This value is assigned when the DataTable is first initialized and represents the original order in which data was loaded into your DataTable. This assigned value does not change, regardless of what filtering or sorting you apply.

    You therefore need to use this same index value with the same selector-modifier when you select the node to be highlighted.

    If you only use tr = table.row(index).node();, then this defaults to {order:'current'} - which is why you get the wrong (or no) row highlighted.


    Demo:

    I added lengthMenu: [ 2, 10 ], to my demo, just to show multiple pages for my test data.

    var dataArray = [
      ['111', '1111', 'AAA', '752', 'Hardcover', '51.55', '-0.11'],
      ['222', '5555', 'BBB', ' 1040', 'Hardcover', '51.55', '-0.10'],
      ['333', '777', 'CCC', '846', 'Hardcover', '51.565', '-0.11'],
      ['444', '888', 'DDD', '258', 'Paperback', '51.56', '-0.12'],
      ['555', '555', 'FFF', ' 789', 'Hardcover', '51.55', '-0.13']
    ];
    
    var map = L.map('viewmap').setView([51.56, -0.12], 13);
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
      attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
    }).addTo(map);
    
    for (var i = 0; i < dataArray.length; i++) {
      var marker = L.marker([dataArray[i][5], dataArray[i][6]]).addTo(map);
      marker.bindPopup("Book: " + dataArray[i][2] + "<br>" + "Pages: " + dataArray[i][3]);
    
      var currentMarker = null;
      var currentTableRow = null;
    
      marker.on("click", function(e) {
        var popup = e.target.getPopup();
        var content = popup.getContent();
        var table = $('#data-table').DataTable();
        var rowData = table.rows().data().toArray();
    
        // Find the row that matches the content of the popup
        var tr = null;
        for (var i = 0; i < rowData.length; i++) {
          if (rowData[i][2] == content.split("<br>")[0].split(": ")[1]) {
            // Use the "index" option to get the index of the row in the original loading order
            var index = table.column(2, {order: 'index'}).data().indexOf(rowData[i][2]);
            tr = table.row(index, {order: 'index'}).node();
            break;
          }
        }
    
        if (tr !== null) {
          // Add a "highlight" class to the new table row
          $(tr).addClass("highlight");
    
          // Remove the "highlight" class from the previous table row (if any)
          if (currentTableRow !== null) {
            $(currentTableRow).removeClass("highlight");
          }
    
          // Update the currently selected marker and its corresponding table row
          currentMarker = marker;
          currentTableRow = tr;
    
          // Get the page index of the selected row
          var pageInfo = table.page.info();
          var rowPage = Math.floor(table.row(tr).index() / pageInfo.length);
    
          // Set the page index as the active page
          table.page(rowPage).draw(false);
        }
      });
    }
    
    $(document).ready(function() {
      $('#data-table').DataTable({
        data: dataArray,
        lengthMenu: [ 2, 10 ],
        columns: [{
            "title": "Rating"
          },
          {
            "title": "Reviews"
          },
          {
            "title": "Book"
          },
          {
            "title": "Pages"
          },
          {
            "title": "Type"
          },
          {
            "title": "Longitude"
          },
          {
            "title": "Latitude"
          },
        ]
      });
    });
    <!DOCTYPE html>
    <html>
    
    <head>
      <base target="_top">
      <script src="https://code.jquery.com/jquery-3.5.1.js"></script>
      <script src="https://cdn.datatables.net/1.10.23/js/jquery.dataTables.min.js"></script>
      <script src="https://cdn.datatables.net/1.10.23/js/dataTables.bootstrap4.min.js"></script>
    
      <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.2/css/bootstrap.css">
      <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.23/css/dataTables.bootstrap4.min.css">
    
      <?!= include('Scriptdata'); ?>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>Document</title>
    
        <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin="" />
        <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script>
        <style>
          body {
            margin: 0;
            padding: 0;
          }
          
          #viewmap {
            width: 60%;
            height: 60vh;
            left: 0%;
            margin: auto;
          }
          
          #text {
            font-family: Georgia, 'Times New Roman', Times, serif;
          }
          
          .highlight {
            background-color: yellow !important;
            ;
          }
        </style>
    </head>
    
    <body>
      <div class="container">
        <br>
        <div class="row">
          <table id="data-table" class="table table-striped table-sm table-hover table-bordered">
          </table>
        </div>
      </div>
    
      <div id="viewmap"></div>
    
    </body>
    
    
    
    </html>


    Update

    Regarding your comment:

    I click the "Pages" filter ... does not go to the page with the highlighted line.

    You can use the DataTables length event to know when the user changes the DataTable "rows per page" drop-down. You can then use similar code to what you already have in your demo (and in my demo) to ensure the required row is still visible after a page length change:

    $('#data-table').on( 'length.dt', function ( e, settings, len ) {
      if (tr !== null) {
        var table = $('#data-table').DataTable();
        var pageInfo = table.page.info();
        var rowPage = Math.floor(table.row(tr).index() / pageInfo.length);
        table.page(rowPage).draw(false);
      }
    } );
    

    For example:

    1. I show all 10 pages in my demo.

    2. I highlight row DDD.

    3. I change the drop-down from 10 pages to 2 pages.

    4. Row DDD is still visible.

    Some additional re-organization is needed from my original demo. Here is the new <script> code:

      var tr = null;
    
      var dataArray = [
        ['111', '1111', 'AAA', '752', 'Hardcover', '51.55', '-0.11'], 
        ['222', '5555', 'BBB',' 1040', 'Hardcover', '51.55', '-0.10'], 
        ['333', '777','CCC', '846', 'Hardcover', '51.565', '-0.11'], 
        ['444', '888', 'DDD','258','Paperback', '51.56', '-0.12'], 
        ['555', '555', 'FFF',' 789','Hardcover', '51.55', '-0.13']
      ];
    
      var map = L.map('viewmap').setView([51.56, -0.12], 13);
      L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
      }).addTo(map);
        
      for (var i = 0; i < dataArray.length; i++) {
        var marker = L.marker([dataArray[i][5], dataArray[i][6]]).addTo(map);
        marker.bindPopup("Book: " + dataArray[i][2] + "<br>" + "Pages: " + dataArray[i][3]);
    
        var currentMarker = null;
        var currentTableRow = null;
    
        marker.on("click", function(e) {
          var popup = e.target.getPopup();
          var content = popup.getContent();
          var table = $('#data-table').DataTable();
          var rowData = table.rows().data().toArray();
    
          // Find the row that matches the content of the popup
          //var tr = null;
          for (var i = 0; i < rowData.length; i++) {
            if (rowData[i][2] == content.split("<br>")[0].split(": ")[1]) {
              // Use the "index" option to get the index of the row in the original loading order
              var index = table.column(2, {order:'index'}).data().indexOf(rowData[i][2]);
              tr = table.row(index, {order:'index'}).node();
              break;
            }
          }
    
          if (tr !== null) {
            // Add a "highlight" class to the new table row
            $(tr).addClass("highlight");
    
            // Remove the "highlight" class from the previous table row (if any)
            if (currentTableRow !== null) {
              $(currentTableRow).removeClass("highlight");
            }
    
            // Update the currently selected marker and its corresponding table row
            currentMarker = marker;
            currentTableRow = tr;
    
            // Get the page index of the selected row
            var pageInfo = table.page.info();
            var rowPage = Math.floor(table.row(tr).index() / pageInfo.length);
    
            // Set the page index as the active page
            table.page(rowPage).draw(false);
          }
        });
      }
    
      $(document).ready(function(){
        $('#data-table').DataTable({
          data: dataArray,
          "lengthMenu": [ 2, 10 ],
          columns: [
            {"title":"Rating"},
            {"title":"Reviews"},
            {"title":"Book"},
            {"title":"Pages"},
            {"title":"Type"},
            {"title":"Longitude"},
            {"title":"Latitude"},
          ]
        });
      });
    
      $('#data-table').on( 'length.dt', function ( e, settings, len ) {
        if (tr !== null) {
          var table = $('#data-table').DataTable();
          var pageInfo = table.page.info();
          var rowPage = Math.floor(table.row(tr).index() / pageInfo.length);
          table.page(rowPage).draw(false);
        }
      } );
    

    I am sure you could refactor this to improve it (e.g. by moving common code into a function). But this should give you a starting point.