maaartinus
maaartinus

Reputation: 46422

How to determine fitting table row count

In my angularjs application, I'm displaying a "main table" on (nearly) every page. This table has thousands of logical rows, but only a few physical rows (so it's fast avoiding huge DOM overhead).

The number of physical rows is data-independent (I display them even when there are no logical rows). I implemented scrolling of the table and I want it to exactly fit in the window, so that no scrolling of the page is necessary.

For this, I need is to know how many rows do fit. The height of every row is fixed (using height: 20px !IMPORTANT and overflow: hidden). The table is the last thing on the page. So in theory, all I need is

var t = $("main-table");
var extraHeight = $(window).height - t.position().top - t.height() - someReserve;
var extraRows = Math.floor(extraHeight) / normalRowHeight);
physicalRows.length += extraRows;

The problem is that I don't know when the values settle down. I start with physicalRows.length = 10 and after executing the above code, I should get the length right. But I don't... so I iterate it using $timeout and it works.

But it doesn't look good with the table growing and shrinking randomly. For example, I get the following lengths:

10 -> 33 -> 31 -> 30 -> 30 (done)

Tuning the number didn't help, even doubling normalRowHeight didn't avoid overshooting. I tried to limit the growth, so that shrinking is avoided. Shrinking looks especially bad as a window scrollbar gets temporarily displayed, making the right table edge jump left and back. But the only thing working was

extraRows = Math.max(extraRows, 1);

which looked terrible with the table growing row by row. Without it, I occasionally get a sequence like

10 -> 33 -> 56 -> 36 -> 31 -> 30 -> 30 (done)

The increase to 56 came from the HTML not being updated between iterations.

What can I do? Is there a way to find out when the sizes are right?

Solution summary

I introduced the iterative computation as the direct one didn't work right. The problems had multiple causes:

My problem was trying a too complicated solution for a simple problem. The iterating hid the original problem and caused others. The answers made me to give up the stupid idea.

Upvotes: 5

Views: 1646

Answers (3)

If you not find any solution then you can initially hide your dataTable div and then when no of rows and height decide, apply no of rows and again show dataTable

Upvotes: 2

LeGEC
LeGEC

Reputation: 51850

If I understand correctly :

  1. you build your main-table from a base array of physicalRows
  2. you compute "how much space is left"
  3. you update physicalRows
  4. you update main-table based on the new array (am I correct ?)
  5. back to 2

If this is indeed the process, you can avoid the looping part, by not using table.height in your formula :

// no extraHeight : get the full height available for your table
var height = $(window).height - t.position().top - someReserve;
// no extraRows : get the full rowCount
var rowCount = Math.floor(extraHeight) / normalRowHeight);

// if you want to display at least 10 rows :
rowCount = Math.max(rowCount, 10);

// no adding to existing size : set the correct size
physicalRows.length = rowCount;

Since this computation does not depend on the table height, you know for sure the value will not change when you update the rows of your table.


The second thing is the height of a row : the height attribute does not take into account border width or padding, so the actual height of a row in your table will be higher than just your cell's height attribute.

See for example : https://jsfiddle.net/1sm3oct1/
even with a height: 20px !important attribute on the table's cells, the actual height looks more like 23px.

Play around with the css and click "Run" to see the actual's table height.

You can try to find through testing the actual size for your rows :

 // testing several pages show that the table's rows are 5px larger than 
 // height in css :
 var normalRowHeight = 20 + 5;

or use the existing table to figure out the height for one of its row :

 var normalRowHeight = t.height() / physicalRows.length;

You can also look at he following SO question : How to fix height of TR ?

Upvotes: 0

Clark Pan
Clark Pan

Reputation: 6027

I think instead of adding/subtracting from the length of the physical rows, you should just try to figure it out on each resize/scroll from scratch.

Something along the lines of this:

<!doctype html>
<html>
  <head>
    <script src="bower_components/jquery/jquery.js"></script>
    <script src="bower_components/angular/angular.js"></script>
    <script>
      angular.module('app', []).directive('calculateFit', function($window, $rootScope){
        return {
          link: function($scope, $element, $attr){
            var height = parseInt($attr.calculateFitHeight, 10);
            $window.addEventListener('resize', function(){
              $scope.$apply(calculateFit);
            }, true);
            $window.addEventListener('scroll', function(){
              $scope.$apply(calculateFit);
            }, true);

            function calculateFit(){
              // This will need to be adjusted to your particular visual use case
              var availableHeight = $(window).height() - $element.position().top;
              $scope[$attr.calculateFit] = Math.ceil(availableHeight / height);
            }
            calculateFit();
          }
        }
      }).run(function($rootScope){
        $rootScope.rows = [];
        for(var i = 0; i < 1000; i++){
          $rootScope.rows.push(i);
        }
      });
    </script>
    <style>
      .spacer {
        height: 100px;
      }
      .main-table {

      }
      .row {
        height: 20px;
      }
      body {
        margin: 0;
        height: 100%;
      }
    </style>
  </head>
  <body ng-app="app">
    <div class="spacer">{{numRows}}</div>
    <div class="main-table" calculate-fit="numRows" calculate-fit-height="20">
      <div ng-repeat="row in rows | limitTo : numRows" class="row">
        {{ $index }}
      </div>
    </div>

  </body>
</html>

You'll probably also want to debounce the resize and scroll events so that the resize/scroll performance is not affected. That might result in some visual delays, but I think its a worthwhile price to pay for smoother scrolling.

Upvotes: 1

Related Questions