Can I "freeze" an element inside a scrollable DIV with just CSS (or something that looks good on mobile)?

11,657

Solution 1

Ok, I haven't tested this but it should be along the right track. Basically this gives you the ability to create multiple "Sticker" items with the HTML5 data attribute I created for you data-special="sticker". The jQuery looks for these, then copies the data and appends it to another <div> element that is positioned where the original was, then it hides the original.

#container {
    position: fixed;
    left: 20px;
    width: 250px;
    height: 250px;
    top: 20px;
    overflow: scroll;
}

#original-element {
    position: absolute;
    top: 30px;
    width: 300px;
    background-color: #CEC;
}

.sticker {
    position:absolute;
}    

<div id="wrapper">
    <div id="container">
        <div id="background">
           Content
        </div>
        <div id="original-element" data-special="sticker">
           I want to stay put!
        </div>
    </div>
</div>

$("[data-special='sticker']").each(function () {
    $('#wrapper').append(
        $('<div/>').html($(this).html())
                   .addClass("sticker")
                   .css('top', parseInt($('#container').css('top')) + parseInt($(this).css('top')))
                   .css('left', $('#container').css('left'))
                   .css('width', $('#container').css('width'))
                   .css('background-color', $(this).css('background-color'))
    );
    $(this).css('display', "none");
});    

Let me know how it works for you, also one downside to this is once the original element is hidden, the space it used to take up is then collapsed... I'll try to brainstorm a solution for that.

Edit:

Changed the JS to get the #container width instead of the original element width as the original element is larger that the container.

Edit:

Tested: jsfiddle

Some issues would be that the element will then also overlap the scroll bar, if you knew the width of that you could then subtract if from the value.

Also check the updated code above. There were some errors...

Solution 2

You might want to have a look at the following post:

As explained in this answer:

A script-free alternative is position: sticky, which is supported in Chrome, Firefox, and Safari. See the article on HTML5Rocks and demo, and Mozilla docs.

As of today, the demo linked works for me in Firefox but not in Chrome.

Share:
11,657
mrdecemberist
Author by

mrdecemberist

Updated on June 04, 2022

Comments

  • mrdecemberist
    mrdecemberist almost 2 years

    I'm going to guess the answer to this question will be "no," but it would be so nice, I'm going to ask anyways.

    What I'm trying to do is freeze an element inside a scrollable DIV such that it stays in place vertically. This is to implement a frozen row feature in a table.

    It's pretty easy to do with JavaScript and absolute positioning. Here's the HTML for a container and three inner DIVs (see here for the live version):

    <div id="container">
      <div id="background">
        Content
      </div>
      <div id="absolutediv">
        Absolute stays inside
      </div>
      <div id="fixeddiv">
        Fixed escapes!
      </div>
      <div id="absolutediv2">
        Stays put!
      </div>
    </div>
    

    The relevant CSS:

    #container {
      position: fixed;
      left: 20px;
      width: 250px;
      height: 250px;
      top: 20px;
      overflow: scroll;
    }
    
    #absolutediv {
      position: absolute;
      top: 30px;
      width: 300px;
      background-color: #CEC;
    }
    
    #fixeddiv {
      position: fixed;
      top: 100px;
      width: 300px;
      background-color: #ECC;
    }
    
    #absolutediv2 {
      position: absolute;
      width: 300px;
      top: 120px;
      background-color: #ECC;
    }
    

    And JavaScript that will hold #absolutediv2 in place:

    var div = document.getElementById('absolutediv2');
    var container = document.getElementById('container');
    
    container.addEventListener('scroll', function() {
      div.style.top = container.scrollTop + 120 + 'px';
    });
    

    So #absolutediv2 is behaving the way I want. But look at #fixeddiv. This gets close to what I'm after, and I suspect it looks nicer on mobile devices because the browser can hold it in place without waiting to run the script. Except that it (a) runs right over the borders, and (b) doesn't scroll horizontally.

    Is there any way to get what I'm after with pure CSS, something that would run well on a mobile browser?

    (In my page, one way to do this would be to place the frozen row above the container DIV, but the number of frozen rows changes depending on where the user has scrolled to, meaning that the container DIV would have to move around.)

    Edit:

    To sum up, I want a div that:

    1. Scrolls horizontally with its container
    2. Stays put when its container scrolls vertically
    3. Looks like it belongs to its container
    4. Looks nice on a mobile browser

    The last one is the tricky bit. I can achieve #1, #2, and #3 with an absolute-position div and JavaScript, but it looks ugly on a mobile browser because it lags. Using a fixed-position div, I can get #2 and #4, and I can achieve #1 with JavaScript (the lag doesn't bother me so much horizontally), but not #3, because a fixed-position div suddenly sits on top of its container.

    Google has a suggestion for this kind of thing, but it's a pretty extreme solution: https://developers.google.com/mobile/articles/webapp_fixed_ui

  • mrdecemberist
    mrdecemberist almost 11 years
    I patched up your answer into a JSFiddle: jsfiddle.net/U7Ayt. The element very much stays put, but it doesn't look like it belongs to the container, and it doesn't scroll horizontally with it.
  • SnareChops
    SnareChops almost 11 years
    @fluggo Yeah, you beat me to it. My jsfiddle is above as well. I worked out a few of the kinks, but the not scrolling with the object and the overlap of the scroll bar are some setbacks... You might be able to put the new element in a scrollable div and then programmatically scroll both together... but at that point it would probably be worth it to use the other method... I wonder if there would be a way to speed up the original calculation you had so that it wouldn't lag.
  • J0ANMM
    J0ANMM over 7 years
    According to this video, for safari you need position: -webkit-sticky