82

I have a positioned div whose content can be too long so scrollbars appear (overflow:auto set). It functions as a dialog box in an ajax app. I want to fix a close button on it's right top corner so when the user scrolls the div it won't scroll away.

I tryed it with position:fixed; right:0; top:0 but it placed the button on the right top of the page not in the div (in firefox).

Is it possible to do this button placement using CSS only without hacking with the offsetWidth/Height in js on every scroll event?

ps: the div's height and width is not a fixed value it depends on the content's size and the browser window's size. User can also resize it if he want.

Calmarius
  • 18,570
  • 18
  • 110
  • 157
  • 1
    It might be worthwhile to consider using a div with a fixed-position background image for the button. – Nick Merrill Nov 10 '13 at 19:48
  • 1
    @NickM seems like a good idea but how would you click the button then? – ithil Nov 07 '14 at 08:51
  • @ithil So long as the height and width of the div are set to match that of the background image, it should be clickable. As far as *handling* the click, that depends on whether you're using plain HTML or JS. If it's just HTML, you could wrap the div in an `a` tag with an `href` (or even just use an `a` tag instead of a `div` and set it as `display: inline-block`). If it's JS, you can listen for a click on the `div`. Did I understand your question correctly? – Nick Merrill Nov 07 '14 at 19:10
  • @NickM yes I believe you did thanks, but the height and width will be "set to match" using JS too I guess? as in the OP question "the div's height and width is not a fixed value", that's why I was wondering – ithil Nov 07 '14 at 20:18
  • @ithil I see. I missed that part. In that case, I think you're right that some JS would have to handle the sizing of the div. – Nick Merrill Nov 07 '14 at 22:21

10 Answers10

88

You can use the position:fixed;, but without set left and top. Then you will push it to the right using margin-left, to position it in the right position you wish.

Check a demo here: http://jsbin.com/icili5

Sotiris
  • 38,986
  • 11
  • 53
  • 85
  • 6
    @Sotiris can you provide a reference that explains why this should work? Why must browsers position a `fixed` element in this way when `top` and `left` are not specified? http://stackoverflow.com/questions/8712047/positionfixed-when-left-top-right-bottom-arent-specified-desired-results-in – Flash Aug 30 '12 at 05:07
  • I wrote the most convoluted JS till I stumbled on this. Nice. – collin Jun 19 '13 at 14:34
  • 1
    this is not the desired behavior – vsync Aug 21 '13 at 14:06
  • I agree this is not the answer. it doesn't handle dynamic width containers as stated in the question. – dano Jan 30 '14 at 20:57
  • @Evildonald what do you mean by "dynamic width containers"? Could you provide an example? – Sotiris Jan 31 '14 at 10:11
  • @Sotiris - What I meant was that if the green parent container in your jsFiddle grows 100px wider due to dynamic width content, the fixed button doesn't stay right justified. Calmarius is quite specific about having variable height and width content... as did I when I hit this problem. – dano Feb 04 '14 at 22:53
  • 9
    Well it's the first time I've seen so many people totally misunderstand a question on this site. – Ryan Bayne Mar 03 '14 at 13:25
15

The current selected solution appears to have misunderstood the problem.

The trick is to neither use absolute nor fixed positioning. Instead, have the close button outside of the div with its position set to relative and a left float so that it is immediately right of the div. Next, set a negative left margin and a positive z index so that it appears above the div.

Here's an example:

#close
    {
        position: relative;
        float: left;
        margin-top: 50vh;
        margin-left: -100px;
        z-index: 2;
    }

#dialog
    {
        height: 100vh;
        width: 100vw;
        position: relative;
        overflow: scroll;
        float: left;
    }

<body> 
    <div id="dialog">
    ****
    </div>

    <div id="close"> </div>
</body>
Joseph
  • 466
  • 1
  • 6
  • 15
  • Awesome answer, got me on the path to a better solution for having an element fixed inside of a scrollable div. – RebelFist Oct 21 '15 at 18:19
6

I know this is an old post but I had the same question but didn't find an answer that set the element fixed relative to a parent div. The scroll bar on medium.com is a great pure CSS solution for setting something position: fixed; relative to a parent element instead of the viewport (kinda*). It is achieved by setting the parent div to position: relative; and having a button wrapper with position: absolute; and the button of course is position: fixed; as follows:

<div class="wrapper">
  <div class="content">
    Your long content here
  </div>

  <div class="button-wrapper">
    <button class="button">This is your button</button>
  </div>
</div>

<style>
  .wrapper {
    position: relative;
  }

  .button-wrapper {
    position: absolute;
    right: 0;
    top: 0;
    width: 50px;
  }

  .button {
    position: fixed;
    top: 0;
    width: 50px;
  }
</style>

working example

*Since fixed elements don't scroll with the page the vertical position will still be relative to the viewport but the horizontal position is relative to the parent with this solution.

Lifehack
  • 1,981
  • 15
  • 12
5

Position:fixed gives an absolute position regarding the BROWSER window. so of course it goes there.

While position:absolute refers to the parent element, so if you place your <div> button inside the <div> of the container, it should position where you meant it to be. Something like

EDIT: thanks to @Sotiris, who has a point, solution can be achieved using a position:fixed and a margin-left. Like this: http://jsfiddle.net/NeK4k/

Damien Pirsy
  • 25,319
  • 8
  • 70
  • 77
  • User can scroll away the close button if I use absolute (that was my first try before tried fixed.) – Calmarius Feb 10 '11 at 20:48
  • Looks like everyone misread want to keep it on the screen. In that case, you must use javascript to correctly position it in relation the div in question, or use javascript to scroll it properly while using position: absolute – Dominic Feb 10 '11 at 20:56
2

Try position:sticky on parent div of the element you want to be fixed.

More info here: http://html5-demos.appspot.com/static/css/sticky.html. Caution: Check for browser version compatibility.

Lohit Bisen
  • 147
  • 1
  • 4
2

Seems, css transforms can be used

"‘transform’ property establishes a new local coordinate system at the element",

but ... this is not cross-browser, seems only Opera works correctly

4esn0k
  • 9,789
  • 7
  • 33
  • 40
1

I achieved to have an element with a fixed position (wiewport) but relative to the width of its parent.

I just had to wrap my fixed element and give the parent a width 100%. At the same time, the wrapped fixed element and the parent are in a div which width changes depending on the page, containing the content of the website. With this approach I can have the fixed element always at the same distance of the content, depending on the width of this one. In my case this was a 'to top' button, always showing at 15px from the bottom and 15px right from the content.

https://codepen.io/rafaqf/pen/MNqWKB

<div class="body">
  <div class="content">
    <p>Some content...</p>
    <div class="top-wrapper">
      <a class="top">Top</a>
    </div>
  </div>
</div>

.content {
  width: 600px; /*change this width to play with the top element*/
  background-color: wheat;
  height: 9999px;
  margin: auto;
  padding: 20px;
}
.top-wrapper {
  width: 100%;
  display: flex;
  justify-content: flex-end;
  z-index: 9;
  .top {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 60px;
    height: 60px;
    border-radius: 100%;
    background-color: yellowgreen;
    position: fixed;
    bottom: 20px;
    margin-left: 100px;
    cursor: pointer;
    &:hover {
      opacity: .6;
    }
  }
}
agapitocandemor
  • 696
  • 11
  • 25
0

If your close button is going to be text, this works very well for me:

#close {
  position: fixed;
  width: 70%; /* the width of the parent */
  text-align: right;
}
#close span {
  cursor: pointer;
}

Then your HTML can just be:

<div id="close"><span id="x">X</span></div>
pizza247
  • 3,869
  • 7
  • 33
  • 47
0

position fixed but relative to parent-div

#parent-div {
  position: *either static or relative, doesn't matter*
  width: 1024px;  // for example
  height: 300vh;  // for example
}

#sticky-div {  // should be div
  position: sticky;
  bottom: 40px;
  width: 30px;  // your-element's width or larger
  height: 30px;  // your-element's height or larger
  margin-top: -40px;  // to compensate "bottom"
  margin-left: auto;
  margin-right: 20px;  // substitution for "right" property
  z-index: 10;
}

#your-element {
  width: 30px;
  height: 30px;
  background-color: red;
}

<div id="parent-div">
  <div id="sticky-div">
    <whatever id="your-element" />
  </div>
</div>
Psychopomp
  • 293
  • 4
  • 9
0

Not sure if it's different from the other solutions, but this is how I understand the fixed position without any top, left, etc:

You place your element wherever you want using a wrapper, and then on the element itself, you just use "fixed" position. This will "freeze" the element.

This is only working if you make sure you don't have a global scrollbar on the body.

So this should work for the use case of the question.

<body style="height :80vh; display:grid;place-items:center">
  <div style="height:150px; width:150px;background:grey;overflow-y:auto">
    <div style="height:2000px;position:relative">
      <div style="position:absolute;top:0;right:25px;">
        <button style="position:fixed">
               x
        </button>
      </div>
      <div>stuf</div>
      <div>stuf</div>
      <div>stuf</div>
      <div>stuf</div>
      <div>stuf</div>
    </div>
  </div>
</body>
Stéphane Gerber
  • 1,388
  • 1
  • 17
  • 30