mkoryak/floatThead

Strategy for handling sticky position from top when that position isn't constant

adam-jones-net opened this issue · 11 comments

floatThead is almost working perfectly for me on a site I am building except for one issue that I hope you can assist with. Very simply, the top position where the table needs to stick changes sometimes. There are 2 reasons why it could change...

  1. The site is responsive so the header height changes at certain page widths and that has a knock-on effect of altering the position of everything in the page below the header.
  2. I have an element on the page that users can optionally show/hide and the sticky table is below that point in the page. So again ,depending on if that element is showing or not the sticky point alters.

Due to this, it seems that setting the 'top' parameter as a single setting won't work and setting it as a function might work but it would be messy to handle where it should show on page. Is this the only option I have to really control where it should stick ?

I have some simple top function setup at the moment but I know it's not flexible enough because it still sets one of only two top positions, which for the reasons I explain above won't work in all situations. Combining that with forcing the 'position' parameter manually to 'absolute' is as good as I can get it without any further suggestions.

var trackTable = $('.trackTable');
trackTable.floatThead({
	top: function() {
		var yPos = $(window).scrollTop();
		if ($('.sometimesVisible').is(":visible")){
			return 180;
		}else{
			return 80;
		}
	},
	position: 'absolute'
});

Any help much appreciated.

Very likely none and it's just me not using it correctly.
But to confirm, in a situation like I describe where the place at which the table starts is going to be dynamic, as standard you'd expect a user to have to explicitly state the top point via a configuration using top values like I am in this example?

Or should it be completely possible for the plugin to work out the position to make it sticky on it's own ?

A jsfiddle example would go a long way here, if you can build one I will be able to give you a definitive answer / solution.

The plugin can work out the floating position on its own, and that position should always be at the top of the thing that is being scrolled, be it the window or a container. You can give it an offset from that location via the top param.

The plugin does not know about anything on your page except for the scrolling container and the table, so it cannot take into account other elements.

You could probably make your top function be smarter and take into account the height of some element/elements to compute the top position instead of hardcoding it. You should also register media query listeners to trigger a reflow on the plugin so that it updates the sticky position when your page layout becomes responsive. Like maybe:

var trackTable = $('.trackTable');
trackTable.floatThead({
	top: function() {
		const rect = $('.sometimesVisible')[0].getBoundingClientRect();
                return rect.height - rect.top; // well probably not this, but something to this effect using `getBoundingClientRect` which gives you position of an element relative to the viewport. 
	},
	position: 'absolute'
});
// Define breakpoints at which your layout changes
window.matchMedia('(max-width: 600px)').addListener((e) => {
   trackTable.trigger('reflow'); // Tell the plugin to re-align the floating header. 
});

Hi thanks for your time. Ok so I've made a fiddle which is here: https://jsfiddle.net/arkid77/b79ca268/9/

It includes a lot of css referenced in an external file. I felt that was important to include as otherwise it won't represent the real problem page I have at all (which I can't share). This fiddle fails in exactly the same way as the real page I am working on.

The main thing that goes wrong can be seen by scrolling down the page say half way and then clicking on any of the show/hide links. That will open a rectangular space near the top of the page using the jquery slideDown() function. Once that has happened off screen above then as soon as you scroll in the page you will see that the sticky TH has been moved down the paeg lower than it should be.

I can solve your problem by using 'fixed' positioning mode: https://jsfiddle.net/2khfbred/4/
I think you alluded in your original post that you need the 'absolute' position for some reason, so based on that, here is what we can do:
https://jsfiddle.net/2khfbred/3/

You shouldnt need to return 2 different top positions because the position of the header relative to the top of the table does not change when you show/hide something above the table.

in the 2nd fiddle where the position if 'absolute', you will notice that there is a lag effect between the click and the header getting into the correct position, this is unfortunate, but we can do a very bad thing and trigger reflow on every step of the animation like this:
https://jsfiddle.net/kL9bvzac/

This wont lead to good performance, but you might not care ;)

The best solution is to just use fixed positioning and not worry about this animation at all.

Hi @mkoryak thank you so much for looking at this.

The first fiddle you give seems the best however it doesn't seem to work in all situations. I believe I saw this at one point myself with my own tests. What seems to happen is that if you scroll slightly down the page and then scroll back to the very top, upon opening the slideDown the table header gets stuck and doesn't slide down the page thereby messing up the formatting.

Any ideas how to get around this ? Otherwise it seems to work nicely.

You want to do this:
https://jsfiddle.net/kL9bvzac/1/

$(document).ready(function() {
  $('.trackTable').floatThead({
    top: function() {
      return $('#headerOuter').height() + 1;
    },
    position: 'fixed'
  });
});


function showHiddenElement() {
  const reflowTableHeader = () => {
    $('.trackTable').trigger('reflow');
  };
  if (!$('.playerWrapperInner').is(':visible')) {
    $('.playerWrapperInner').slideDown({duration:600, progress: reflowTableHeader, complete: reflowTableHeader});
  } else {
    $('.playerWrapperInner').slideUp({duration:200, progress: reflowTableHeader, complete: reflowTableHeader});
  }
}

Amazing thanks so much. So to confirm, the difference is that the animation is handled differently and by not using the slideUp/Down functions it can be worked around?

The animation is exactly the same as you had it, the only difference is that during every frame of the animation we trigger reflow on the table. The plugin listens to that event and realigns the header every time it is triggered.
Essentially, you are animating the header position during the animation.

This is not idea because the reflow event was never meant to be run inside of animation - you are probably triggering it 500 times during the animation.. but computers are fast today and this lib was written for old slow computers.

I still have a few issues implementing this on a real site that is in preview. Can I share a url with you privately in case anything simple ?