The most successful I’ve been at this is clever uses of position: sticky. You have to fall back to absolute positioning and scrolling the fixed headers in JS on browsers that aren’t as advanced in their position: sticky support though.
I figured it out without using JavaScript. The trick is to use a <th> on the first child of each tr, which becomes the sticky left column. Position sticky works on th’s.
It’s not ideal, but this works on about 95% of all browsers.