Vérifier si le contenu d'un élément déborde?

154

Quel est le moyen le plus simple de détecter si un élément a été débordé?

Mon cas d'utilisation est que je souhaite limiter une certaine zone de contenu à une hauteur de 300 px. Si le contenu interne est plus grand que cela, je le coupe avec un débordement. Mais s'il déborde, je veux afficher un bouton «plus», mais sinon je ne veux pas afficher ce bouton.

Existe-t-il un moyen simple de détecter les débordements ou existe-t-il une meilleure méthode?

Harry
la source

Réponses:

86

Si vous souhaitez afficher uniquement un identifiant pour plus de contenu, vous pouvez le faire avec du CSS pur. J'utilise des ombres de défilement pures pour cela. L'astuce est l'utilisation de background-attachment: local;. Votre css ressemble à ceci:

.scrollbox {
  overflow: auto;
  width: 200px;
  max-height: 200px;
  margin: 50px auto;

  background:
    /* Shadow covers */
    linear-gradient(white 30%, rgba(255,255,255,0)),
    linear-gradient(rgba(255,255,255,0), white 70%) 0 100%,
    
    /* Shadows */
    radial-gradient(50% 0, farthest-side, rgba(0,0,0,.2), rgba(0,0,0,0)),
    radial-gradient(50% 100%,farthest-side, rgba(0,0,0,.2), rgba(0,0,0,0)) 0 100%;
  background:
    /* Shadow covers */
    linear-gradient(white 30%, rgba(255,255,255,0)),
    linear-gradient(rgba(255,255,255,0), white 70%) 0 100%,
    
    /* Shadows */
    radial-gradient(farthest-side at 50% 0, rgba(0,0,0,.2), rgba(0,0,0,0)),
    radial-gradient(farthest-side at 50% 100%, rgba(0,0,0,.2), rgba(0,0,0,0)) 0 100%;
  background-repeat: no-repeat;
  background-color: white;
  background-size: 100% 40px, 100% 40px, 100% 14px, 100% 14px;
  
  /* Opera doesn't support this in the shorthand */
  background-attachment: local, local, scroll, scroll;
}
<div class="scrollbox">
  <ul>
    <li>Not enough content to scroll</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
  </ul>
</div>


<div class="scrollbox">
  <ul>
    <li>Ah! Scroll below!</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
    <li>10</li>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>The end!</li>
    <li>No shadow there.</li>
  </ul>
</div>

Le code et l'exemple que vous pouvez trouver sur http://dabblet.com/gist/2462915

Et une explication que vous pouvez trouver ici: http://lea.verou.me/2012/04/background-attachment-local/ .

RWAM
la source
1
Bonne idée mais dans votre exemple le fond (ombre) serait derrière le texte!
DreamTeK
Excellent truc, mais dans Chrome, il ne montre qu'une ombre statique en bas, il ne disparaît jamais et l'ombre supérieure ne s'affiche jamais
Jared
214

L'élément peut être survolé verticalement, horizontalement ou les deux. Cette fonction vous renverra une valeur booléenne si l'élément DOM est survolé:

function isOverflown(element) {
  return element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
}

Exemple ES6:

const isOverflown = ({ clientWidth, clientHeight, scrollWidth, scrollHeight }) => {
    return scrollHeight > clientHeight || scrollWidth > clientWidth;
}
micnique
la source
41
Excellente solution! Voici une variante de jQuery (à utiliser avec $("#element").overflown()):$.fn.overflown=function(){var e=this[0];return e.scrollHeight>e.clientHeight||e.scrollWidth>e.clientWidth;}
Sygmoral
@micnic salut, j'utilise ionic2 et j'essaie de faire la même chose ... mais la fonction retourne toujours vrai ... y a-t-il une façon différente de faire les choses dans angular 2
Yasir
@DavidBracoly, cette fonction est pour l'API DOM pure, je n'ai pas d'expérience avec Ionic / Angular, je suppose que vous devriez obtenir l'élément DOM original et utiliser cette fonction
micnic
3
Uniquement pour Firefox: element.addEventListener ("overflow", () => log( "overflow" ), false);Référence: événement de débordement
Reza
18

La comparaison element.scrollHeightavec element.clientHeightdevrait faire la tâche.

Vous trouverez ci-dessous les images de MDN expliquant Element.scrollHeight et Element.clientHeight.

Hauteur de défilement

Hauteur du client

Bergi
la source
2
Ouais, mais selon quirksmode, cela fonctionne pour la plupart - et est beaucoup plus simple que les autres solutions publiées.
Bergi
@Harry: Il est pris en charge dans tous les navigateurs actuels (au moins ceux de bureau), donc peu importe que ce soit formellement non standard.
Marat Tanalin
1
Ici, ces propriétés ont toujours les mêmes valeurs.
koppor
7

J'ai fait un codepen en plusieurs parties démontrant les réponses ci-dessus (par exemple en utilisant le débordement caché et la hauteur) mais aussi comment vous étendre / réduire les éléments débordés

Exemple 1: https://codepen.io/Kagerjay/pen/rraKLB (Véritable exemple simple, pas de javascript, juste pour couper les éléments débordés)

Exemple 2: https://codepen.io/Kagerjay/pen/LBErJL (gestionnaire d'événements unique show more / showless sur les éléments débordés)

Exemple 3: https://codepen.io/Kagerjay/pen/MBYBoJ (Gestionnaire multi-événements sur beaucoup afficher plus / afficher moins sur les éléments débordés)

J'ai également joint l'exemple 3 ci-dessous , j'utilise Jade / Pug, donc ça pourrait être un peu verbeux. Je suggère de vérifier les codepens que j'ai simplifiés à saisir.

// Overflow boolean checker
function isOverflown(element){
  return element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
}

// Jquery Toggle Text Plugin
$.fn.toggleText = function(t1, t2){
  if (this.text() == t1) this.text(t2);
  else                   this.text(t1);
  return this;
};

// Toggle Overflow
function toggleOverflow(e){
  e.target.parentElement.classList.toggle("grid-parent--showall");
  $(e.target).toggleText("Show More", "Show LESS");
}

// Where stuff happens
var parents = document.querySelectorAll(".grid-parent");

parents.forEach(parent => {
  if(isOverflown(parent)){
    parent.lastElementChild.classList.add("btn-show");
    parent.lastElementChild.addEventListener('click', toggleOverflow);
  }
})
body {
  background-color: #EEF0ED;
  margin-bottom: 300px;
}

.grid-parent {
  margin: 20px;
  width: 250px;
  background-color: lightgrey;
  display: flex;
  flex-wrap: wrap;
  overflow: hidden;
  max-height: 100px;
  position: relative;
}
.grid-parent--showall {
  max-height: none;
}

.grid-item {
  background-color: blue;
  width: 50px;
  height: 50px;
  box-sizing: border-box;
  border: 1px solid red;
}
.grid-item:nth-of-type(even) {
  background-color: lightblue;
}

.btn-expand {
  display: none;
  z-index: 3;
  position: absolute;
  right: 0px;
  bottom: 0px;
  padding: 3px;
  background-color: red;
  color: white;
}

.btn-show {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section>
  <p>Any grid-parent over 10 child items has a "SHOW MORE" button to expand</p>
  <p>Click "SHOW MORE" to see the results</p>
</section>
<radio></radio>
<div class="wrapper">
  <h3>5 child elements</h3>
  <div class="grid-parent">
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="btn-expand">Show More</div>
  </div>
  <h3>8 child elements</h3>
  <div class="grid-parent">
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="btn-expand">Show More</div>
  </div>
  <h3>10 child elements</h3>
  <div class="grid-parent">
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="btn-expand">Show More</div>
  </div>
  <h3>13 child elements</h3>
  <div class="grid-parent">
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="btn-expand">Show More</div>
  </div>
  <h3>16 child elements</h3>
  <div class="grid-parent">
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="btn-expand">Show More</div>
  </div>
  <h3>19 child elements</h3>
  <div class="grid-parent">
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="btn-expand">Show More</div>
  </div>
</div>

Vincent Tang
la source
4

Quelque chose comme ceci: http://jsfiddle.net/Skooljester/jWRRA/1/ fonctionne? Il vérifie simplement la hauteur du contenu et la compare à la hauteur du conteneur. Si c'est plus grand que vous pouvez mettre dans le code pour ajouter un bouton "Afficher plus".

Mise à jour: Ajout du code pour créer un bouton «Afficher plus» en haut du conteneur.

ayyp
la source
3

Si vous utilisez jQuery, vous pouvez essayer une astuce: créer un div externe avec overflow: hiddenet un div interne avec du contenu. Ensuite, utilisez la .height()fonction pour vérifier si la hauteur de la div interne est supérieure à la hauteur de la div externe. Je ne suis pas sûr que cela fonctionnera, mais essayez-le.

TMS
la source
1

utilisez js pour vérifier si l'enfant offsetHeightest plus que les parents .. si c'est le cas, faites déborder les parents scroll/hidden/autoselon ce que vous voulez et aussi display:blocksur le plus de div ..

Vivek Chandra
la source
1

Vous pouvez vérifier les limites relatives au parent de décalage.

// Position of left edge relative to frame left courtesy
// http://www.quirksmode.org/js/findpos.html
function absleft(el) {
  var x = 0;
  for (; el; el = el.offsetParent) {
    x += el.offsetLeft;
  }
  return x;
}

// Position of top edge relative to top of frame.
function abstop(el) {
  var y = 0;
  for (; el; el = el.offsetParent) {
    y += el.offsetTop;
  }
  return y;
}

// True iff el's bounding rectangle includes a non-zero area
// the container's bounding rectangle.
function overflows(el, opt_container) {
  var cont = opt_container || el.offsetParent;
  var left = absleft(el), right = left + el.offsetWidth,
      top = abstop(el), bottom = top + el.offsetHeight;
  var cleft = absleft(cont), cright = cleft + cont.offsetWidth,
      ctop = abstop(cont), cbottom = ctop + cont.offsetHeight;
  return left < cleft || top < ctop
      || right > cright || bottom > cbottom;
}

Si vous passez cet élément, il vous dira si ses limites sont entièrement à l'intérieur d'un conteneur, et sera par défaut le parent offset de l'élément si aucun conteneur explicite n'est fourni. Il utilise

Mike Samuel
la source
1

Un autre problème à prendre en compte est l'indisponibilité de JS. Pensez à l'enchantement progressif ou à la dégradation gracieuse. Je voudrais suggerer:

  • ajout d'un "bouton plus" par défaut
  • ajout de règles de débordement par défaut
  • bouton masquant et si nécessaire modifications CSS dans JS après comparaison de element.scrollHeight à element.clientHeight
MFix
la source
1

Voici un violon pour déterminer si un élément a été débordé à l'aide d'un div wrapper avec débordement: hidden et JQuery height () pour mesurer la différence entre le wrapper et un div de contenu interne.

outers.each(function () {
    var inner_h = $(this).find('.inner').height();
    console.log(inner_h);
    var outer_h = $(this).height();
    console.log(outer_h);
    var overflowed = (inner_h > outer_h) ? true : false;
    console.log("overflowed = ", overflowed);
});

Source: Frameworks & Extensions sur jsfiddle.net

user1295799
la source
1

C'est la solution jQuery qui a fonctionné pour moi. clientWidthetc. n'a pas fonctionné.

function is_overflowing(element, extra_width) {
    return element.position().left + element.width() + extra_width > element.parent().width();
}

Si cela ne fonctionne pas, assurez-vous que le parent des éléments a la largeur souhaitée (personnellement, j'ai dû utiliser parent().parent()). positionEst relatif au parent. J'ai également inclus extra_widthparce que mes éléments ("balises") contiennent des images qui prennent peu de temps à charge, mais pendant l'appel de fonction, ils ont une largeur nulle, ce qui gâche le calcul. Pour contourner cela, j'utilise le code d'appel suivant:

var extra_width = 0;
$(".tag:visible").each(function() {
    if (!$(this).find("img:visible").width()) {
        // tag image might not be visible at this point,
        // so we add its future width to the overflow calculation
        // the goal is to hide tags that do not fit one line
        extra_width += 28;
    }
    if (is_overflowing($(this), extra_width)) {
        $(this).hide();
    }
});

J'espère que cela t'aides.

Dennis Golomazov
la source
0
setTimeout(function(){
    isOverflowed(element)           
},500)

function isOverflowed(element){
    return element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
}

Cela a fonctionné pour moi. Je vous remercie.

Krunal
la source
3
En quoi est-ce différent de la réponse de micnic?
Attentionnow1
0

L'alternative jquery à la réponse est d'utiliser la touche [0] pour accéder à l'élément brut tel que celui-ci:

if ($('#elem')[0].scrollHeight > $('#elem')[0].clientHeight){
Antony
la source
0

J'ai étendu Element pour des raisons d'encapsulation. De la réponse micnic.

/*
 * isOverflowing
 * 
 * Checks to see if the element has overflowing content
 * 
 * @returns {}
 */
Element.prototype.isOverflowing = function(){
    return this.scrollHeight > this.clientHeight || this.scrollWidth > this.clientWidth;
}

Utilisez-le comme ça

let elementInQuestion = document.getElementById("id_selector");

    if(elementInQuestion.isOverflowing()){
        // do something
    }
David Clews
la source