web-dev-qa-db-fra.com

Positionner l'élément fixe verticalement, absolu horizontalement

Voici ce que j'essaie d'accomplir:

J'ai besoin d'un bouton qui est positionné à une certaine distance du côté droit d'un div et qui est toujours à la même distance du côté du div, quelle que soit la taille de la fenêtre, mais qui défilera avec la fenêtre.

Il s'agit donc à tout moment de x pixels du côté droit de la div, mais à tout moment de y pixels du haut du port de vue.

Est-ce possible?

68
Jimmy

Positionner l'élément fixe horizontalement par rapport à un autre élément

( Avis de non-responsabilité : La solution proposée ici n'est pas techniquement " absolu horizontalement " comme question le titre a déclaré, mais a atteint ce que l'OP souhaitait à l'origine, l'élément de position fixe étant à une distance 'X' du bord droit d'un autre mais fixé dans son défilement vertical.)

J'adore ces types de défis CSS. Comme preuve de concept, oui, vous pouvez obtenir ce que vous désirez . Vous devrez peut-être modifier certaines choses pour vos besoins, mais voici quelques exemples html et css qui ont passé FireFox, IE8 et IE7 (IE6, bien sûr, n'a pas position: fixed).

HTML:

<body>
  <div class="inflow">
    <div class="positioner"><!-- may not be needed: see notes below-->
      <div class="fixed"></div>
    </div>
  </div>
</body>

CSS (bordures et toutes les dimensions pour la démonstration):

div.inflow {
  width: 200px; 
  height: 1000px; 
  border: 1px solid blue; 
  float: right; 
  position: relative; 
  margin-right: 100px;
}
div.positioner {position: absolute; right: 0;} /*may not be needed: see below*/
div.fixed {
  width: 80px; 
  border: 1px solid red; 
  height: 100px; 
  position: fixed; 
  top: 60px; 
  margin-left: 15px;
}

La clé est de ne pas définir du tout le left ou right pour l'horizontale sur le div.fixed mais utilisez les deux divs wrapper pour définir la position horizontale. Le div.positioner est pas nécessaire si le div.inflow est une largeur fixe, comme la marge gauche du div.fixed peut être réglé sur la largeur connue du conteneur. Cependant, si vous souhaitez que le conteneur ait un pourcentage de largeur, vous aurez besoin du div.positioner pour pousser le div.fixed à droite du div.inflow première.

Edit: Comme note latérale intéressante, lorsque je mets overflow: hidden (en cas de besoin) sur le div.inflow avait aucun effet sur la position fixe div étant en dehors des limites de l'autre. Apparemment, la position fixe est suffisante pour la retirer du contexte de div contenant pour overflow.

44
ScottS

Après avoir beaucoup creusé (y compris ce post), je n'ai pas trouvé de solution que j'aimais. La réponse acceptée ici ne fait pas ce que le titre du PO a lu, et les meilleures solutions que j'ai pu trouver ont entraîné des éléments nerveux. Ensuite, cela m'a frappé: demandez à l'élément d'être "fixe", détectez quand un défilement horizontal se produit et basculez-le pour qu'il soit absolument positionné. Voici le code résultant:

Affichez-le comme un stylo code.

[~ # ~] html [~ # ~]

  <div class="blue">
    <div class="red">
    </div>
  </div>

[~ # ~] css [~ # ~]

/* Styling */
.blue, .red {
  border-style: dashed;
  border-width: 2px;
  padding: 2px;
  margin-bottom: 2px;
}

/* This will be out "vertical-fixed" element */
.red {
  border-color: red;
  height: 120px;
  position: fixed;
  width: 500px;
}

/* Container so we can see when we scroll */
.blue {
  border-color: blue;
  width: 50%;
  display: inline-block;
  height: 800px;
}

JavaScript

$(function () {
  var $document = $(document),
      left = 0,
      scrollTimer = 0;

  // Detect horizontal scroll start and stop.
  $document.on("scroll", function () {
    var docLeft = $document.scrollLeft();
    if(left !== docLeft) {
      var self = this, args = arguments;
      if(!scrollTimer) {
        // We've not yet (re)started the timer: It's the beginning of scrolling.
        startHScroll.apply(self, args);
      }
      window.clearTimeout(scrollTimer);
      scrollTimer = window.setTimeout(function () {
        scrollTimer = 0;
        // Our timer was never stopped: We've finished scrolling.
        stopHScroll.apply(self, args);
      }, 100);
      left = docLeft;
    }
  });

  // Horizontal scroll started - Make div's absolutely positioned.
  function startHScroll () {
    console.log("Scroll Start");
    $(".red")
    // Clear out any left-positioning set by stopHScroll.
    .css("left","")
    .each(function () {
      var $this = $(this),
          pos = $this.offset();
      // Preserve our current vertical position...
      $this.css("top", pos.top)
    })
    // ...before making it absolutely positioned.
    .css("position", "absolute");
  }

  // Horizontal scroll stopped - Make div's float again.
  function stopHScroll () {
    var leftScroll = $(window).scrollLeft();
    console.log("Scroll Stop");
    $(".red")
    // Clear out any top-positioning set by startHScroll.
    .css("top","")
    .each(function () {
      var $this = $(this), 
        pos = $this.position();
      // Preserve our current horizontal position, munus the scroll position...
      $this.css("left", pos.left-leftScroll);
    })
    // ...before making it fixed positioned.
    .css("position", "fixed");
  }
});
10
Grinn

Je suis arrivé ici à la recherche d'une solution à un problème similaire, qui était d'avoir une barre de pied de page qui s'étend sur la largeur de la fenêtre et se trouve en dessous du contenu (hauteur et largeur variables). En d'autres termes, faites apparaître que le pied de page est "fixe" par rapport à sa position horizontale, mais conserve sa position normale dans le flux de documents par rapport à sa position verticale. Dans mon cas, j'avais le texte du pied de page aligné à droite, donc cela a fonctionné pour moi d'ajuster dynamiquement la largeur du pied de page. Voici ce que j'ai trouvé:

HTML

<div id="footer-outer">
<div id="footer">
    Footer text.
</div><!-- end footer -->
</div><!-- end footer-outer -->

CSS

#footer-outer
{
    width: 100%;
}
#footer
{
    text-align: right;
    background-color: blue;
    /* various style attributes, not important for the example */
}

CoffeeScript/JavaScript

(Utilisation de prototype.js).

class Footer
document.observe 'dom:loaded', ->
    Footer.width = $('footer-outer').getDimensions().width
Event.observe window, 'scroll', ->
    x = document.viewport.getScrollOffsets().left
    $('footer-outer').setStyle( {'width': (Footer.width + x) + "px"} )

qui se compile en:

Footer = (function() {

  function Footer() {}

  return Footer;

})();

document.observe('dom:loaded', function() {
  return Footer.width = $('footer-outer').getDimensions().width;
});

Event.observe(window, 'scroll', function() {
  var x;
  x = document.viewport.getScrollOffsets().left;
  return $('footer-outer').setStyle({
    'width': (Footer.width + x) + "px"
  });
});

Cela fonctionne bien dans FireFox, et assez bien dans Chrome (c'est un peu nerveux); je n'ai pas essayé d'autres navigateurs.

Je voulais également que tout espace libre sous le pied de page soit d'une couleur différente, j'ai donc ajouté ce footer-stretch div:

HTML

...
</div><!-- end footer -->
<div id="footer-stretch"></div>
</div><!-- end footer-outer -->

CSS

#footer-outer
{
    height: 100%;
}
#footer-stretch
{
    height: 100%;
    background-color: #2A2A2A;
}

Notez que pour que la div # footer-stretch fonctionne, tous les éléments parents jusqu'à l'élément body (et éventuellement l'élément html - pas sûr) doivent avoir une hauteur fixe (dans ce cas, hauteur = 100%).

7
Waz