'transform3d' ne fonctionne pas avec la position: enfants fixes

140

J'ai une situation où, dans des circonstances CSS normales, un div fixe serait positionné exactement là où il est spécifié ( top:0px, left:0px).

Cela ne semble pas être respecté si j'ai un parent qui a une transformation translate3d. Est-ce que je ne vois pas quelque chose? J'ai essayé d'autres options de style et d'origine de transformation de type Webkit, mais je n'ai pas eu de chance.

J'ai joint un JSFiddle avec un exemple où je me serais attendu à ce que la boîte jaune soit dans le coin supérieur de la page plutôt qu'à l'intérieur de l'élément conteneur.

Vous pouvez trouver ci-dessous une version simplifiée du violon:

#outer {
    position:relative; 
    -webkit-transform:translate3d(0px, 20px , 0px); 
    height: 300px; 
    border: 1px solid #5511FF; 
    padding: 10px;
    background: rgba(100,180,250, .8); 
    width: 80%;
}
#middle{
    position:relative; 
    border: 1px dotted #445511; 
    height: 300px; 
    padding: 5px;
    background: rgba(250,10,255, .6);
}
#inner {
    position: fixed; 
    top: 0px;
    box-shadow: 3px 3px 3px #333; 
    height: 20px; 
    left: 0px;
    background: rgba(200,180,80, .8); 
    margin: 5px; 
    padding: 5px;
}
<div id="container">
    Blue: Outer, <br>
    Purple: Middle<br>
    Yellow: Inner<br>
    <div id="outer"> 
        <div id="middle">
            <div id="inner">
                Inner block
            </div>
        </div>
    </div>
</div>

Comment puis-je faire fonctionner translate3d avec des enfants à position fixe?

Juan Carlos Moreno
la source
Le même problème se produit avec le filtre: stackoverflow.com/q/52937708/8620333
Temani Afif

Réponses:

196

En effet, le transformcrée un nouveau système de coordonnées local, selon les spécifications du W3C :

Dans l'espace de noms HTML, toute valeur autre que celle nonede la transformation entraîne la création à la fois d'un contexte d'empilement et d'un bloc conteneur. L'objet agit comme un bloc conteneur pour les descendants positionnés fixes.

Cela signifie que le positionnement fixe devient fixe à l'élément transformé, plutôt qu'à la fenêtre.

Il n'y a actuellement pas de solution de rechange à ma connaissance.

Il est également documenté dans l'article d'Eric Meyer: Un-fix Fixed Elements with CSS Transforms .

saml
la source
Merci, je n'avais pas remarqué que la spécification réelle contenait cette information ..... Je suppose que j'ai eu l'équivalent de la réponse mathématique: "par définition" :)
Juan Carlos Moreno
3
@INT, je ne pense pas qu'il y ait une solution de contournement pour cela. Il existe un cas d'utilisation majeur pour ne pas autoriser les solutions de contournement: les entrées fournies par l'utilisateur pourraient potentiellement couvrir les contrôles en dehors de leur zone désignée (pensez à un e-mail malveillant ajoutant des options à la barre d'outils Gmail). La meilleure solution de contournement serait d'éviter les transformations pour le moment si vous allez utiliser fixe de l'intérieur.
saml
1
La définition de l'attribut CSS top sur n'importe quel window.scrollHeight ne fonctionnerait-il pas? Vous devrez peut-être absolument le positionner également, mais quelque chose comme ça devrait être faisable, non? (trop paresseux pour tester en ce moment)
Brad Orego
@bradorego vous aviez raison, je viens d'ajouter le code que j'ai utilisé.
UzumakiDev
Cela est toujours en évolution dans les spécifications pour autant que je sache. Voir Bogue 16328 - L'utilisation de "bloc contenant" ne correspond pas à la définition CSS2.1 .
troisième du
13

J'ai eu un scintillement sur ma navigation supérieure fixe lorsque les éléments de la page utilisaient la transformation, ce qui suit appliqué à ma navigation supérieure a résolu le problème de saut / scintillement:

#fixedTopNav {
    position: fixed;
    top: 0;
    transform: translateZ(0);
    -webkit-transform: translateZ(0);
}

Merci à cette réponse sur SO

rob.m
la source
12

Comme Bradoergo l'a suggéré, récupérez simplement la fenêtre scrollTopet ajoutez-la au sommet de la position absolue comme:

function fix_scroll() {
  var s = $(window).scrollTop();
  var fixedTitle = $('#fixedContainer');
  fixedTitle.css('position','absolute');
  fixedTitle.css('top',s + 'px');
}fix_scroll();

$(window).on('scroll',fix_scroll);

Cela a fonctionné pour moi de toute façon.

UzumakiDev
la source
3
Ça marche! mais au lieu de lier à "window", j'ai dû me lier au div qui défilait. De plus, l'élément fixe scintille.
train
Que doit faire jQuery ici?
FlorianB
Cela aurait pu le faire, mais comme @train l'a mentionné, il scintille.
Etienne Dupuis
Ouais, cela ne se met pas à jour assez vite pour être assez propre pour mon utilisation: - / bonne idée tho
reid
C'est une très mauvaise pratique, ne faites pas de changement de style avec js
Wannes
4

Dans Firefox et Safari, vous pouvez utiliser à la position: sticky;place de position: fixed;mais cela ne fonctionnera pas dans d'autres navigateurs. Pour cela, vous avez besoin de javascript.

Dušan
la source
2
Le positionnement collant est un hybride de positionnement relatif et fixe, et c'est vraiment expérimental , je recommande vivement d'éviter cela, car ce n'est pas encore standard.
Farside du
1
AFAIK sur Firefox et Safari, vous pouvez simplement utiliser position:fixedet cela fonctionnerait de toute façon comme prévu.
oriadam
@oriadam non, j'ai le problème lorsque le parent utilise translate3d et que la position fixe des enfants vole dans certains cas. Je vais essayer de l'utiliser stickyalors qu'il est déjà pris en charge par les principaux navigateurs: caniuse.com/#feat=css-sticky
Lukas Liesis
stickypeut être une solution, cela fonctionne avec un navigateur moderne.
aboutqx
3

À mon avis, la meilleure méthode pour résoudre ce problème est d'appliquer la même traduction, mais de séparer les enfants qui doivent être corrigés de leur élément parent (traduit); puis appliquez la traduction à un div à l'intérieur du position: fixedwrapper.

Les résultats ressemblent à ceci (dans votre cas):

<div style='position:relative; border: 1px solid #5511FF; 
            -webkit-transform:translate3d(0px, 20px , 0px); 
            height: 100px; width: 200px;'> 

</div>
<div style='position: fixed; top: 0px; 
            box-shadow: 3px 3px 3px #333; 
            height: 20px; left: 0px;'>
    <div style='-webkit-transform:translate3d(0px, 20px, 0px);'>
        Inner block
    </div>
</div>

JSFiddle: https://jsfiddle.net/hju4nws1/

Bien que cela puisse ne pas être idéal pour certains cas d'utilisation, généralement si vous corrigez un div, vous pourriez probablement moins vous soucier de quel élément est son parent / où il se situe dans l'arbre d'héritage de votre DOM, et semble résoudre la plupart des maux de tête. - tout en permettant les deux translateet position: fixedde vivre en harmonie (relative).

Reid
la source
0

J'ai rencontré le même problème. La seule différence est que mon élément avec 'position: fixed' avait ses propriétés de style 'top' et 'left' définies à partir de JS. J'ai donc pu appliquer un correctif:

var oRect = oElement.getBoundingClientRect();

L'objet oRect contiendra les coordonnées supérieures et gauches réelles (par rapport au port de vue). Vous pouvez donc ajuster vos propriétés oElement.style.top et oElement.style.left.

Mike Dobrin
la source
Cela fonctionne sur IE et Chrome, mais pas sur le navigateur standard Android. La gauche est un nombre mais il est toujours dessiné sur la position 0
Adaptabi
0

J'ai une barre latérale hors canevas qui utilise -webkit-transform: translate3d. Cela m'empêchait de placer un pied de page fixe sur la page. J'ai résolu le problème en ciblant une classe sur la page html qui est ajoutée à la balise lors de l'initialisation de la barre latérale, puis en écrivant un css: not qualifier to state "-webkit-transform: none;" à la balise html lorsque cette classe n'est pas présente sur la balise html. J'espère que cela aide quelqu'un avec ce même problème!

WeisbergWeb
la source
0

Essayez d'appliquer la transformation opposée à l'élément enfant:

<div style='position:relative; border: 1px solid #5511FF; 
            -webkit-transform:translate3d(0px, 20px , 0px); 
            height: 100px; width: 200px;'> 
    <div style='position: fixed; top: 0px; 
                -webkit-transform:translate3d(-100%, 0px , 0px); 
                box-shadow: 3px 3px 3px #333; 
                height: 20px; left: 0px;'>
        Inner block
    </div>
</div>
Mari Do
la source
0

Ajoutez une classe dynamique pendant que l'élément se transforme. $('#elementId').addClass('transformed'). Ensuite, déclarez en css,

.translat3d(@x, @y, @z) { 
     -webkit-transform: translate3d(@X, @y, @z); 
             transform: translate3d(@x, @y, @z);
      //All other subsidaries as -moz-transform, -o-transform and -ms-transform 
}

puis

#elementId { 
      -webkit-transform: none; 
              transform: none;
}

puis

.transformed {
    #elementId { 
        .translate3d(0px, 20px, 0px);
    }
}

Désormais, position: fixedlorsqu'elles sont fournies avec des valeurs de propriété topet z-indexsur un élément enfant, elles fonctionnent correctement et restent fixes jusqu'à ce que l'élément parent se transforme. Lorsque la transformation est annulée, l'élément enfant apparaît à nouveau comme fixe. Cela devrait faciliter la situation si vous utilisez réellement une barre latérale de navigation qui s'ouvre et se ferme en un clic, et que vous avez un ensemble d'onglets qui devrait rester collant lorsque vous faites défiler la page.

monsieur G.
la source
-1

Une façon de gérer cela consiste à appliquer la même transformation à l'élément fixe:

<br>
<div style='position:relative; border: 1px solid #5511FF; 
            -webkit-transform:translate3d(0px, 20px , 0px); 
            height: 100px; width: 200px;'> 
    <div style='position: fixed; top: 0px; 
                -webkit-transform:translate3d(0px, 20px , 0px); 
                box-shadow: 3px 3px 3px #333; 
                height: 20px; left: 0px;'>
        Inner block
    </div>
</div>
fgassert
la source
1
Sur le bug IE n'existe pas en premier lieu
oriadam