18

I have a few different tables that I want to display vertically down a page. The trouble is, the different tables have different column headings.

In the picture, the dark border represents the viewport. The dotted red border is where the red header belongs but note that it is "frozen" to the top of the viewport. This is true until, in the second image, the green header begins to replace it. http://imagebin.org/172108Concept Picture

Does anyone know how this could be accomplished using very lightweight scripting (In other words, I don't want JQuery or something) and CSS. I have the advantage that I only need it to render on Webkit (which means some css3 is also an option). I don't care if the headers are not actually part of the html <table> - they must obviously just line up properly though.

jcuenod
  • 55,835
  • 14
  • 65
  • 102
  • I don't know what iOS does but considering this is (in my opinion) an intuitive design it wouldn't surprise me if they copied my idea in advance :) – jcuenod Sep 12 '11 at 08:30
  • have a higher z-index on the green bar than the red bar? Or Collision detection? – mplungjan Sep 12 '11 at 08:34
  • @mplungjan Okay but that doesn't really help fix the headers to the top of the page once they're scrolled past... (it also won't look as nice since green will clearly just overlap red) – jcuenod Sep 12 '11 at 08:37
  • I'm thinking that right now you are at a desert looking for water. I don't see this happening without javascript. – Joonas Sep 12 '11 at 08:41
  • He said he doesn't want to have to import a js library. I think js is still in the question though – mowwwalker Sep 12 '11 at 09:45
  • @user828584 I didnt want to rule _javascript_ out but I was more or less talking about js in general and I was thinking that jquery might be the way to go. – Joonas Sep 12 '11 at 10:13
  • I just want lightweight - that's why no library... – jcuenod Sep 12 '11 at 11:50
  • This might be a duplicate of http://stackoverflow.com/questions/7370472/howto-create-the-iphone-contacts-header-scroll-effect-for-html – chris5marsh Sep 12 '11 at 11:57

4 Answers4

18

edit: http://jsfiddle.net/BCtP8/3/

There, I fixed the hiccuping text. When the header becomes fixed, I change the placholder's height. I also changed it to work with any header height, not just the one I had given them.

OLD POST Here you go man:

http://jsfiddle.net/BCtP8/

Sorry for any problems that might occur, I'm quite new to the whole web developing scene and I don't have the experience to predict what might happen.

I doubt this is the best or most efficient way to do it, so if you find better, please post it here so I can learn. Thanks.

mowwwalker
  • 16,634
  • 25
  • 104
  • 157
  • 1
    Dude - that's awesome. That's exactly the effect I want. I'll get back to you about how you accomplished it... – jcuenod Sep 12 '11 at 12:08
  • Thanks, I'm going to bed now, but I'll check back tomorrow to make sure it's working. – mowwwalker Sep 12 '11 at 12:25
  • It does seem to have some hiccups with some offsetting of the text under the title when title becomes fixed ( not like i see it as a big obstable anyhow.. ) That is quite awesome.. (+1) – Joonas Sep 12 '11 at 12:57
  • Ja, that hiccup is because when you change the way that the header is positioned (between static and fixed) it changes whether the header floats over text or not. Know how to fix that? – jcuenod Sep 12 '11 at 13:34
  • Okay - The solution is to position the fixedheaders 'absolute' rather than 'static' and then put the text in p tags with padding-top of 40px. I also removed the .placeholder{display: none} which seems to have had an effect. – jcuenod Sep 12 '11 at 13:43
  • Curiously, what is this effect called? – Chad Levy Sep 12 '11 at 19:18
  • Woah, you removed the placeholder? Did you edit the javascript? The point of the placeholder was to know where the header is supposed to be. If you calculate the header's position while it is fixed, it will always stay fixed because it will always be lower than the scroll position. I'll work on something now for the hiccup. – mowwwalker Sep 12 '11 at 19:44
  • Well, that should solve the problem. I guess that's what I was seeing in FF and Opera, but it was less noticeable in Chrome. If you need help using it, let me know. – mowwwalker Sep 12 '11 at 20:01
  • 1
    Maybe I missed something, isn't the solution supposed to work on tables? – Hemlock Sep 12 '11 at 22:47
  • @Hemlock, that's true, but I can't stand tables. There's no point in using them – mowwwalker Sep 13 '11 at 01:43
  • I did say I wasn't concerned whether the headers were part of the `table` - it's easy enough to put the headers into the div. @Walkerneo I didn't remove the placeholder I just removed the styling - I think on Opera with display:none; it was failing to figure out positioning. This is beautiful thanks dude. – jcuenod Sep 13 '11 at 07:24
  • How would you do such a thing in an AngularJS directive? – Matic Krajnc Jan 14 '15 at 15:29
  • Thanks so much for your solution! I'm still not able to understand what is the logic of this, even though I am looking at this code since 2 days. :-( – MeV Aug 14 '15 at 23:57
3

This will only be possible with Javascript - I can't think of a way of doing it simply with CSS.

The basis of the answer is to use position:fixed on the element. I would suggest cloning the thead of your table to attach in a fixed position at the top of the table, and then add an event listener for the scroll event and check the position of each table against the amount that has been scrolled.

I've put up an example on JSFiddle.

chris5marsh
  • 468
  • 3
  • 13
  • 1
    I find it somewhat weird that you answered 2 hours later with an answer that wasnt the same as the one that the OP had commented on with: `That's exactly the effect I want.` ..I'm gonna give you mental minus point for that. None the less your answer is quite interesting even though its not what the OP wants. – Joonas Sep 12 '11 at 20:00
  • I don't think there's anything wrong with that. If anything, they could have said that their solution was an alternative. Regardless, they did contribute something very useful to this question, this site and its users, so `+1`. – Chad Levy Sep 12 '11 at 23:58
  • I don't have a problem with it. I have a few crits though; I don't like the transition between the headers because you see the wrong one until it covers the right one (Walkerneo's solution looks good in that respect). I also need something lightweight and so didn't want a library like JQuery. I will keep your solution in mind though if I work on something I can use JQuery in. Also, I told the guy whose question I duplicated about this thread, thanks. – jcuenod Sep 13 '11 at 07:31
  • @Lollero - I started answering the question when it was unanswered, got sidetracked, came back to it and finished it off a bit later. I only noticed afterwards that there was another answer! – chris5marsh Sep 14 '11 at 08:19
1

For what it's worth:

There are a couple of answers here but I don't think they actually answer the question. They don't operate on table headers (thead elements) or they use 3rd party libraries. There are some subtle issues with lifting a thead out of the table - the largest of which is that the cells will collapse down if the header text is wider or narrower than the data in the tbody.

Here is my solution that solves the problem without any libraries and working on table headers. It makes no assumptions about the styling of the table, or the size of the headers; everything is calculated. Only tested in Chrome per the requirements of the OP.

Script:

function initFloatingHeaders() {
  var tables = document.querySelectorAll('table.float-header');
  var i = tables.length;

  while (i--) {
    var table = tables[i];
    var wrapper = document.createElement('div');
    wrapper.className = 'floating-header';
    var clone = table.cloneNode(true);
    wrapper.appendChild(clone);
    table.parentNode.insertBefore(wrapper, table);

    var thead = table.querySelector('thead');
    wrapper.style.width = thead.scrollWidth + 'px';  
    wrapper.style.height = thead.scrollHeight + 'px';
    wrapper.style.left = table.offsetLeft + 'px';     
  }

  window.addEventListener('scroll', function() {
    var headers = document.querySelectorAll('div.floating-header');
    var bodyHeight = document.body.offsetHeight;
    var i = headers.length;
    while (i--) {
      var header = headers[i];
      var tableBounds = header.nextSibling.getBoundingClientRect();
      if (tableBounds.top < 0 && tableBounds.bottom > 0) {
        header.style.display = 'block';
      } else {
        header.style.display = null;
      }
    }
  }, false);
}

Tables should have the class float-header applied and initFloatingHeaders should be called on load or documentReady. Example: http://jsbin.com/ulusit/2 (Old example with bad transitions: http://jsbin.com/ulusit/)

Hemlock
  • 6,130
  • 1
  • 27
  • 37
  • I wasn't overly concerned by the use of thead floating but I am impressed by your effort and I appreciate it. Your solution suffers the same problem as chris5marsh's though - I just don't like the header transition. – jcuenod Sep 13 '11 at 07:34
  • 1
    @chris5marsh You're right. Sorry to lump you in there. What yours does, though, is use jQuery which the OP did not want. – Hemlock Sep 14 '11 at 12:03
  • Holy smokes! This is terrific. I have been searching for a solution that did not require fixed column widths, and this is it! Thank you very much. – wrecks Mar 31 '16 at 15:46
0

It's possible to achieve the result in pure CSS with position:sticky

body, dl, dt, dd {
  margin: 0;
}
dl {
  position: relative;
}
dt, dd {
  padding: 1rem;
}
dt {
  background-color: #ddd;
  position: sticky;
  top: 0;
  box-shadow: 0 0 0.25rem rgb(0 0 0 /10%);
}

I got a pen 4 years ago. You can jump in to see how it works: https://codepen.io/patrickliu/full/MGPNgJ