Think this was all introduced in the rewrite: #1574738: Rewrite tableheader.js.

See screenshots:

  1. Original header is visible under sticky header (setting a z-index fixes this, but that might open a whole new can of worms
  2. If for some reason you load the page halfway a table, the sticky table will only show up upon the first scroll. Maybe trigger scroll once on pageload if the table is in the viewport?
  3. There should be a detach behavior to unregister tables that have been removed/replaced.
  4. On scroll/resize handlers should be debounced. The inline docs currently state they are debounced to execute every 250ms, but they are actually executed every possible scroll/resize.
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Kiphaas7’s picture

So I talked a little bit with nod_ about this, and he said the sticky table creation upon scroll is on purpose, to delay execution as much as possible.

A middleground might be to execute tableHeaderInitHandler unconditionally on attach. Move the 'on scroll' bind to createSticky, after which we can test for checkStickyVisible: if it returns true, trigger scroll on the table.

This way the actual creation and insertion of the sticky table, which I think is the most costly operation, is still delayed if the table is not visible.

If that sounds good, I'll try to whip up a patch.

Kiphaas7’s picture

Issue summary: View changes

typos

nod_’s picture

Category: task » bug
Kiphaas7’s picture

made it a task for the last line in the OP, (no detach or events), but if it should be a bug report, fine by me :).

Kiphaas7’s picture

Aha!

table th a {
    display: block;
    position: relative;
}

Seems to be the culprit for the bleed-through. It only happens with links, and the position: relative; is responsible

Removing it from the original table (not the sticky table) seems to fix it.

nod_’s picture

Status: Active » Needs review
FileSize
1.83 KB
Kiphaas7’s picture

Looks like a very good and simple solution. Though I would prefer to set offsetTop to null instead of undefined. Semantically makes more sense to me (we set the variable, we just don't know what value it should have).

Should the bleed through bug be deferred to the theme/css folks?

nod_’s picture

Yeah I wasn't sure, I went for undefined so that it wouldn't break so horribly if someone tries to use it as a number before it's computed.

On the other hand, it's new code, it can be fixed right now :)

Kiphaas7’s picture

If they want to use it, and it's null at init, they should check if it's not null :). But that's the same if undefined was used... Not checking would be sloppy on their end :P. And yea, the code is still brand new, I doubt anyone is already depending on it :).

nod_’s picture

tag

Kiphaas7’s picture

Just as a reminder for myself (or others), might want to add this to the patch as well by means of feature testing?

http://drupal.org/node/1440628#comment-6455104

var nativeSticky = window.getComputedStyle(document.querySelector('.sticky')).position.match('sticky') ? true : false;
if (!nativeSticky) {
  // execute regular js code.
}
.sticky {
  position: -o-sticky;
  position: -moz-sticky;
  position: -ms-sticky;
  position: -webkit-sticky;
  position: sticky;
}
Kiphaas7’s picture

Made a new issue for the bleed-through issue: #1780852: Sticky tableheaders bleed-through.

seutje’s picture

@#10: makes sense, we don't want to end up breaking natively supported implementations.

seutje’s picture

after doing some more analysis, I've come to the conclusion that this won't break native implementations as it'll just swap it to fixed once we're past a certain point.

so if we have consensus on this undefined vs null thing, this is good to go, yes?

seutje’s picture

Issue summary: View changes

detach

quicksketch’s picture

Issue summary: View changes
quicksketch’s picture

Issue summary: View changes

Hi guys, just reviewing the tableheader.js changes from 2012 and thought I'd provide additional input on the changes that I discovered:

- The onScroll behavior says that "This function is throttled to once every 250ms to avoid unnecessary calls." But it is not. Both the onScroll and onResize methods are fired repeatedly with no throttle/debouncing.
- The onScroll call to this.stickyPosition(null, scrollValue('scrollLeft')); is having unexpected results. Because in stickyPosition !isNaN(offsetTop) returns true for a "null" value (see these Mozilla examples). The code seems to expect that null will not be considered a number, but oddly isNaN actually *does* consider null to be a number. Very weird.

I'd suggest changing the conditionals to add "&& offsetTop !== null" to prevent the weirdness:

      if (!isNaN(offsetTop) && offsetTop !== null) {
        css.top = offsetTop + 'px';
      }
      if (!isNaN(offsetLeft) && offsetTop !== null) {
        css.left = (this.tableOffset.left - offsetLeft) + 'px';
      }

Though maybe there is a better way of checking if a number is not, this StackExchange post seems pretty definitive, but I'm not sure comprehensive checking is really necessary in the first place.

EDIT: Sorry I've noticed that all the problems with isNaN are fixed by using ECMA Script 5's "use strict". Which means we don't have any problem on any browsers except IE9, which doesn't support use strict. http://caniuse.com/use-strict

quicksketch’s picture

Another regression: The tableheader behavior will no longer be applied to tables loaded via AJAX, since the use of $(window).one() won't refire when new content is attached. Although you'd have to be replacing a really big table for this issue to occur. Most of the time you'd want to add/replace rows within an existing table rather than replace it, but it could still be an issue in some situations.

nod_’s picture

For #1 I don't know what I had in mind at the time. Was supposed to be.

For #2 there is already #2269129: Permissions sticky header is not place correctly.

(edit) and for the init are you sure? because to me any new ajax call will bind the tableinit function and it's just that it's executed on the first scroll event that happens. Can you reproduce your bug?

jhedstrom’s picture

Status: Needs review » Needs work
Issue tags: +Needs reroll
jain_deepak’s picture

Re Rolled patch

jain_deepak’s picture

Status: Needs work » Needs review
pwieck’s picture

Issue tags: -Needs reroll

Status: Needs review » Needs work

The last submitted patch, 19: core-js-tableheader-fix-1764912-19.patch, failed testing.

nod_’s picture

Issue tags: +Needs reroll
Manjit.Singh’s picture

rerolling a patch :)

Manjit.Singh’s picture

Status: Needs work » Needs review
googletorp’s picture

Issue tags: -Needs reroll
mgifford’s picture

Status: Needs review » Needs work

Needs re-roll.

droplet’s picture

Original header is visible under sticky header (setting a z-index fixes this, but that might open a whole new can of worms

Not sure if it's still exist.

If for some reason you load the page halfway a table, the sticky table will only show up upon the first scroll. Maybe trigger scroll once on pageload if the table is in the viewport?

Can't reproduce (also @see #17)

There should be a detach behavior to unregister tables that have been removed/replaced.

this is make sense.

On scroll/resize handlers should be debounced. The inline docs currently state they are debounced to execute every 250ms, but they are actually executed every possible scroll/resize.

For page with many sticky tables, it helps a bit. If only one or two, it's slightly bad for UX IMO.

I think we should open new issues for each problem.

Note that: No re-roll is needed. I reviewed the patch and that's not right approach now.

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

maximpodorov’s picture

Version: 8.6.x-dev » 8.8.x-dev
Status: Needs work » Needs review
FileSize
716 bytes

What about the invalidation of all tables on each DOM change?

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

joseph.olstad’s picture

I'm going to hijack this issue, I'm proposing some improvements in the name of helper functions.

Imagine your sticky table header works great on first page load, then you grab some table from ajax, replace the table in the DOM with the new table from ajax, well, that just broke the references in the tables collection in Drupal.TableHeader.tables

So, to solve this, I've added three helper functions, one is called Drupal.TableHeader.validateReferences()
what this function does is remove the unreferenced tables in the collection of Drupal.TableHeader.tables (tables that are no longer found in the DOM)

This explains how , if you call recalculateSticky with broken references (tables no longer present in the DOM) the table cell widths will all be returned 0 , and the whole recalculateSticky will be broken.

So to solve this, you call Drupal.TableHeader.validateReferences() and then re-initialise by calling Drupal.TableHeader.addTable(tableSelectorParam);

see patch

Also, if you have multiple sticky headers on a page, I found it useful to add another function called Drupal.TableHeader.recalculateStickys(); rather than recalculateSticky on one item , this does all of them in a for loop.

So three functions added altogether.

This is 100% backwards compatible.

Works very well now that I finally figured out how to use this library.

I'd like to provide some example code at some point as well, however the above instructions should be helpful, would probably be good to add comments to the tableheader.js file with this explanation , or add a link to documentation with an example or two.

a quick example

make a global scope variable , set it to false
like

var checkTargetStickyInit = false;
var jqWrapper = '.some-class-for-your-block';
var jqSticky = '.sticky-header'; // This is the 'copy' created by TableHeader, the sticky one, thead only.
var jqTableEvents = '.sticky-table'; // This is the table with both the header thead and rows of table data.

then as an example, some js functions I use:

  function resetStickyHeader() {
    // Since the table may have been replaced (if this was a sort operation for example.
    // Then we have to call validateReferences() because we replaced the table when we called ajax.
    var invalidTableRef = Drupal.TableHeader.validateReferences();  // the new function from the patch I just added.
    if (invalidTableRef || !$(jqWrapper + ' ' + jqSticky).length) {
      prepareForNewStickyHeader(); // validateReferences() removed the previous table, assign this event.
      Drupal.TableHeader.addTable(jqWrapper + ' ' + jqTableEvents);
      $(jqWrapper + ' ' + jqTableEvents).trigger('columnschange');
      $(window).trigger('scroll'); // This actually sets off the re-init of the sticky header.
    }
  }

  function prepareForNewStickyHeader() {
    checkTargetStickyInit = false;
    $(window).on('scroll.TableHeaderInit', {context: document}, function(e) {
      if (checkTargetStickyInit) {
        $(this).off(e); // Turn off this event;
      }
      else {
        // If you have special setup to do to initialise events on the sticky-header to it here.
        if (debuggingEnabled)
        console.log('call initialiseStickyTableHeader();');
        initialiseStickyTableHeader(); // some function that is going to add click events to the header or whatever you need post setup.
      }
    });
  }

in the function initlialiseStickyTableHeader() you do something like this:

  function initialiseStickyTableHeader() {
    // Initialise all event search results sticky-header table headers.
    $('div.some-css-class .sticky-table').each(function(index) {
      // You're in, this means the sticky table was found.
      checkTargetStickyInit = true; // Ding ding ding! See .off().
      $(this).addClass('target-sticky-header');
      // Make sure that the column for actions is added if it isn't already.
      Drupal.TableHeader.recalculateStickys(); // Now that it's initialised, recalculate.
      // Call some function here for setting up click events on your table header, or 
      // whatever post processing you need initialised on the table header
    });
  }
joseph.olstad’s picture

see comment # 38 for a verbose explanation.
#1764912-38: Fix regressions and further improve tableheader.js

interdiff shows minor change, removing setTimeout (not needed).

attaching updated patch

joseph.olstad’s picture

see comment # 38 for a verbose explanation.
#1764912-38: Fix regressions and further improve tableheader.js

interdiff shows minor change, putting comment before code.

attaching updated patch

joseph.olstad’s picture

@Quicksketch, these helper functions and detailed explanation in comment #38 (with examples) explain and provide a strategy to deal with and explain what happens when doing ajax calls (replacing a table for example) what you describe in #16.

see comment #38 and patch 39

#1764912-38: Fix regressions and further improve tableheader.js

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
FileSize
176 bytes

The Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.