5
\$\begingroup\$

I'm working on a table component that is able to scroll through millions of lines of data. Not bad for the front end. The way I'm doing this is to only render a small table with 10 rows and then, when the user scrolls, I change the data in that table.

The table itself is not in a scrollable div because the entire html element fits on the page. This is working OK, but it means that the scroll on the page is not disabled the way it would be if the table were in a scrollable div.

I really don't like how I have to hide the overflow on the body when the mouse enters the component to stop the scroll event from moving the whole page instead of just the data in the component.

import Ember from 'ember';
import layout from '../templates/components/table';

let TableComponent = Ember.Component.extend(
  {
    tagName: 'table',
    layout: layout,
    firstRow: 0,
    lastRow: 10,
    isScrollEnabled: false,


    maxRow: Ember.computed('content',
      function () {
        return Ember.get(this, 'content.length');
      }
    ),

    setup: function () {
      this.$().on('mousewheel', Ember.run.bind(this, this._didScroll));
      this.$(window).on('focus', Ember.run.bind(this, this._focus));
      this.$(window).on('blur', Ember.run.bind(this, this._blur));
    }.on('didInsertElement'),

    tearDown: function () {
      this.$().off('mousewheel', Ember.run.bind(this, this._didScroll));
      this.$(window).off('focus', Ember.run.bind(this, this._focus));
      this.$(window).off('blur', Ember.run.bind(this, this._blur));
    }.on('willDestroyElement'),

    visibleContent: Ember.computed('content', 'firstRow', 'lastRow',
      function () {
        let content = Ember.get(this, 'content');
        let firstRow = Ember.get(this, 'firstRow');
        let lastRow = Ember.get(this, 'lastRow');

        return content.slice(firstRow, lastRow);
      }
    ),

    _didScroll(event) {
      if (!Ember.get(this, 'isScrollEnabled')) {
        return;
      }
      if (event.originalEvent.wheelDelta >= 0) {
        // scroll up but not past first row
        if (Ember.get(this, 'firstRow') > 0) {
          this.decrementProperty('firstRow');
          this.decrementProperty('lastRow');
        }
      } else {
        // scroll down but not past last row
        if (Ember.get(this, 'lastRow') < Ember.get(this, 'maxRow')) {
          this.incrementProperty('firstRow');
          this.incrementProperty('lastRow');
        }
      }
    },

    _focus() {
      Ember.set(this, 'isScrollEnabled', true);
    },

    _blur() {
      Ember.set(this, 'isScrollEnabled', false);
    },

    mouseEnter() {
      Ember.$('body').css('overflow', 'hidden');
    },

    mouseLeave() {
      Ember.$('body').css('overflow', 'auto');
    }
  }
);

export default TableComponent;

The mouseEnter and mouseLeave functions are my big concerns.

\$\endgroup\$
3
1
\$\begingroup\$

hide the overflow on the body when the mouse enters the component to stop the scroll event from moving the whole page instead of just the data in the component

Isn't that already the default behavior of scrollable divs? Well, except the page doesn't set overflow:hidden on itself. Once your cursor is in a scrollable area, scroll will transfer there. When it reaches the end, the page scroll will pick up the rest of your scroll (really apparent when answering in SE with a really tiny textarea).

So I believe you're preventing that "page picking up after the end" thing. If so, you don't need to disable scrolling for the component. Once you hover over it, just disable the page scroll and enable when you move out.

Now for your code, you're inconsistently doing the "egyptian" (collapsed, K&R) brackets. Well, the problem is the inconsistency, not the fact that you're using one or the other. Just choose one style and stick with it.

maxRow: Ember.computed('content', function () {
  return Ember.get(this, 'content.length');
}),

let TableComponent = Ember.Component.extend({
  tagName: 'table',
  layout: layout,
  ...

To make these checks easier, try configuring your IDE to tell you. I believe most IDEs/text editors have plugins for eslint, jshint, and jslint.

\$\endgroup\$
2
  • \$\begingroup\$ The scroll on the table is unusual. The table itself is not "scrollable" and is not in a scrollable div. The entire thing fits onto the page without any overflow. So, I don't even have a real scroll event to override (which is why I'm using mousewheel). When I detect a mousewheel event, I fire _didScroll() which updates firstRow and lastRow. That triggers the visibleContent computed property which changes the data in the table. This update doesn't add or delete nodes in the DOM, it just changes the values on them. \$\endgroup\$ – spinlock Dec 3 '15 at 18:21
  • \$\begingroup\$ We've decided on putting the curly braces on a separate line for object definitions because we've found that, over time, we add mixins. If the curly brace is on the top line, this can run off the end of the page and hide information you want to know when looking at the class definition. But, if you try to refactor at that point and put each mixin on it's own line, you'll create a huge update in git that's all whitespace. So, we've decided to always put the curly brace on it's own line to allow for mixins to easily be added in the future. A bit inconsistent but the juice is worth the squeeze. \$\endgroup\$ – spinlock Dec 3 '15 at 18:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.