8

I'm trying to make a sticky header + first column table. Works fine on desktop browsers.

However, when I scroll the table's x-axis on a mobile device, the position update is dragging i.e. not fast enough.

I've read various SO-threads that suggest iScroll. I'm not really sure how to use it appropriately in this case. Should intercept the tbody scroll event, perevent the default behaviour and update the position based on iScroll's event values? Please point me in the right direction here :)

$(function() {
  var $tbody = $('tbody');

  $tbody.on('scroll', function(e) { 
    var left = $tbody.scrollLeft();
    $('thead').css('left', -left); 
    $('tbody td:nth-child(1), thead th:nth-child(1)').css('left', left);
  });

  var iScroll = new IScroll($tbody[0], { probeType: 3 });
  iScroll.on('scroll', function(){
    console.log('not fired?');
  });
});

https://jsfiddle.net/97r799gr/

To reproduce the problem, it's probably easiest for you to visit https://jsfiddle.net/97r799gr/show on your mobile. I'm using an SGS7 edge so I think this will be reproducible on pretty much any mobile device.

Johan
  • 35,120
  • 54
  • 178
  • 293
  • I will go on a guessig spree and say that the use of selectors is extremely inneficient. Try storing the results into variables. – Ismael Miguel Jan 30 '17 at 08:58
  • @IsmaelMiguel Could have been the case, but it's not :) – Johan Jan 30 '17 at 09:13
  • You could try replacing every single `td:nth-child(1)` with `td:first-child`. Then, add `var $thead= $('thead');` under the `var $tbody`. `$('thead').css('left', -left);` can be replaced with `$thead.css('left', -left);`. Then, replace `$('tbody td:nth-child(1), thead th:nth-child(1)')` with `$tbody.find('td:first-child').css('left', left); $thead.find('th:first-child').css('left', left);`. This should work and speed up things a bit. You could go a bit further and store those results into a var – Ismael Miguel Jan 30 '17 at 09:53
  • Actually, the simplest answer is: Just use [datatables](https://datatables.net/)! Here's what you want to implement: https://datatables.net/extensions/fixedcolumns/examples/initialisation/left_right_columns.html It is one heavy plugin, but does anything you need with the table. – Ismael Miguel Jan 30 '17 at 11:21
  • @IsmaelMiguel Thanks, but the problem is that the datatables plugin is rather laggy on a mobile device too... – Johan Jan 30 '17 at 11:50
  • Then there's something else burning CPU time – Ismael Miguel Jan 30 '17 at 12:22
  • 2
    I just tested this on my mac laptop in safari 10.0.2 and it is super laggy as well - so not just mobile! – Zze Feb 01 '17 at 20:54
  • A different approach might be to make the table scrollable (instead of the page) and set the names set as position absolute (using js to calculate there position on page load / resize). – Davey Feb 01 '17 at 20:57
  • Check here, this may help you. http://stackoverflow.com/questions/3402295/html-table-with-horizontal-scrolling-first-column-fixed You may also be interested in https://datatables.net/extensions/fixedcolumns/ – Mike Feb 01 '17 at 22:12
  • I have done something similar, but I'm using Zurb's Foundation. Have a look at their responsive table plugin here: http://zurb.com/playground/responsive-tables – Wim Mertens Feb 01 '17 at 22:16
  • @WimMertens Still only one axis though? I need both a sticky header and first column – Johan Feb 02 '17 at 21:13
  • @Johan, yes you're right. Found this post, might be useful: http://stackoverflow.com/questions/21449339/dynamic-html-table-with-fixed-header-and-fixed-first-column check the fiddle in the answer: http://jsfiddle.net/mayekarsaurabh/7w8TC/52/ – Wim Mertens Feb 02 '17 at 21:52
  • @WimMertens Thanks, but it's the same solution more or less :/ The difference beeing using js for the y axis instead of x... – Johan Feb 02 '17 at 22:35
  • @WimMertens a little laggy on anroid. But it works! – shukshin.ivan Feb 03 '17 at 19:53

2 Answers2

2

IScroll uses a wrapper with fixed dimensions with one scrollable content in it. Therefore we can't use tbody as a wrapper. We should wrap the whole table and hold desired elements with CSS when scrolling.

I've created a fiddle with iscroll-probe.js code in it (iscroll.js hasn't got a scroll event). Bad news is that it works only on my iPhone. Scrolling is suddenly stops when scrolling it on my android5.1 device.

// main code
$(document).ready(function(){ 
  $('#pos').text('initialize iscroll');
  try{
    var iScroll = new IScroll('#wrapper', { 
      interactiveScrollbars: true, 
      scrollbars: true, 
      scrollX: true, 
      probeType: 3, 
      useTransition:false, 
      bounce:false
    });
  } catch(e) {
    $('#error').text('Error ' + e.name + ":" + e.message + "\n" + e.stack);
  }

  $('#pos').text('initialized');

  iScroll.on('scroll', function(){
    var pos = $('#scroller').position();
    $('#pos').text('pos.left=' + pos.left + ' pos.top=' + pos.top);

    // code to hold first row and first column
    $('#scroller th:nth-child(1)').css({top: (-pos.top), left: (-pos.left), position:'relative'});
    $('#scroller th:nth-child(n+1)').css({top: (-pos.top), position:'relative'});

    $('#scroller td:nth-child(1)').css({left: (-pos.left), position:'relative'});
  });
});

https://jsfiddle.net/0qv1kjac/11/

shukshin.ivan
  • 11,075
  • 4
  • 53
  • 69
  • 1
    Awesome, this looks promising! Any ideas about the android problem though? I'm experiencing it too – Johan Feb 04 '17 at 13:12
  • I can't improve the answer any more. It is a known issue. https://github.com/cubiq/iscroll/issues/91 and https://github.com/cubiq/iscroll/issues/751 can possibly help you. – shukshin.ivan Feb 04 '17 at 13:16
  • 1
    Ah I see. Thanks a lot for your help, appreciate it! – Johan Feb 04 '17 at 13:25
  • @Johan This does not work on desktop (at least not macOS Sierra, Chrome 55). You're ok with an iPhone-only solution? – tony19 Feb 05 '17 at 05:38
  • @tony19 Yeah, my main concern was the iPad actually. Thanks for asking :) Also, it works fine on latest Chrome on windows 10. – Johan Feb 05 '17 at 22:26
0

I wasn't able to make it work with pure CSS, so I decided to remove iScroll and rewrite the javascript in vanilla js. It works fine on my iphone now. I don't have an android to test with.

New fiddle: https://jsfiddle.net/66a1b2rw/

Everything is the same as your original fiddle except the js:

updated js

var tbody = document.querySelector('tbody');
var thead = document.querySelector('thead');
var firstCol = document.querySelectorAll('tbody td:nth-child(1), thead th:nth-child(1)');
console.log(firstCol);
tbody.addEventListener('scroll', function() {
    var left = tbody.scrollLeft;
  thead.style.left = '-' + left + 'px';
  firstCol.forEach(function(x) {x.style.left = left + 'px'})
})
Community
  • 1
  • 1
Jonah
  • 15,806
  • 22
  • 87
  • 161
  • Thanks for the suggestion. I'm afraid this didn't help much for me though. I think you need to resort to a third party framework like iScroll to intercept the native scrolling event – Johan Feb 02 '17 at 08:12