¿Comprobar si el contenido de un elemento se desborda?

154

¿Cuál es la forma más fácil de detectar si un elemento se ha desbordado?

Mi caso de uso es, quiero limitar un determinado cuadro de contenido para que tenga una altura de 300 px. Si el contenido interno es más alto que eso, lo corto con un desbordamiento. Pero si se desborda, quiero mostrar un botón "más", pero si no, no quiero mostrar ese botón.

¿Hay una manera fácil de detectar el desbordamiento o hay un método mejor?

Harry
fuente

Respuestas:

86

Si desea mostrar solo un identificador para más contenido, puede hacerlo con CSS puro. Utilizo sombras de desplazamiento puro para esto. El truco es el uso de background-attachment: local;. Su CSS se ve así:

.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>

El código y el ejemplo que puede encontrar en http://dabblet.com/gist/2462915

Y una explicación que puede encontrar aquí: http://lea.verou.me/2012/04/background-attachment-local/ .

RWAM
fuente
1
Buena idea, pero en tu ejemplo el fondo (sombra) estaría detrás del texto.
DreamTeK
Gran truco, sin embargo, en Chrome solo muestra sombra estática en la parte inferior, nunca desaparece y la sombra superior nunca se muestra
Jared
214

El elemento puede desbordarse verticalmente, horizontalmente o ambos. Esta función le devolverá un valor booleano si el elemento DOM se desborda:

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

Ejemplo ES6:

const isOverflown = ({ clientWidth, clientHeight, scrollWidth, scrollHeight }) => {
    return scrollHeight > clientHeight || scrollWidth > clientWidth;
}
micnic
fuente
41
Gran solución! Aquí hay una variante jQuery (para usar con $("#element").overflown()):$.fn.overflown=function(){var e=this[0];return e.scrollHeight>e.clientHeight||e.scrollWidth>e.clientWidth;}
Sygmoral
@micnic hola, estoy usando ionic2 e intento hacer lo mismo ... pero la función devuelve verdadero siempre ... ¿hay una manera diferente de hacer las cosas en angular 2
Yasir
@DavidBracoly, esta función es para API DOM pura, no tengo experiencia con Ionic / Angular, supongo que debería obtener el elemento DOM original y usar esta función
micnic
3
Solo para Firefox: element.addEventListener ("overflow", () => log( "overflow" ), false);Referencia: evento de desbordamiento
Reza
18

En comparación element.scrollHeightconelement.clientHeight debería hacer la tarea.

A continuación se muestran las imágenes de MDN que explican Element.scrollHeight y Element.clientHeight.

Altura de desplazamiento

Altura del cliente

Bergi
fuente
2
Sí, pero de acuerdo con el modo peculiar , funciona para la mayoría, y es mucho más simple que las otras soluciones publicadas.
Bergi
@Harry: es compatible con todos los navegadores actuales (al menos los de escritorio), por lo que no importa que sea formalmente no estándar.
Marat Tanalin
1
Aquí, estas propiedades siempre tienen los mismos valores.
koppor
7

Hice un codepen multiparte demostrando las respuestas anteriores (por ejemplo, usando el desbordamiento oculto y la altura) pero también cómo expandiría / colapsaría los elementos desbordados

Ejemplo 1: https://codepen.io/Kagerjay/pen/rraKLB ( Ejemplo realmente simple, sin javascript, solo para recortar elementos desbordados)

Ejemplo 2: https://codepen.io/Kagerjay/pen/LBErJL (el controlador de eventos individuales muestra más / showless en elementos desbordados)

Ejemplo 3: https://codepen.io/Kagerjay/pen/MBYBoJ (controlador de eventos múltiples en muchos mostrar más / mostrar menos en elementos desbordados)

También he adjuntado el ejemplo 3 a continuación , uso Jade / Pug, por lo que podría ser un poco detallado. Sugiero revisar los codepens que he hecho más fácil de entender.

// 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
fuente
4

¿Algo como esto: http://jsfiddle.net/Skooljester/jWRRA/1/ work? Simplemente verifica la altura del contenido y lo compara con la altura del contenedor. Si es mayor de lo que puede poner en el código para agregar un botón "Mostrar más".

Actualización: se agregó el código para crear un botón "Mostrar más" en la parte superior del contenedor.

ayyp
fuente
3

Si está utilizando jQuery, puede intentar un truco: hacer un div externo con overflow: hiddenun div interno con contenido. Luego use la .height()función para verificar si la altura del div interno es mayor que la altura del div externo. No estoy seguro de que funcione, pero pruébalo.

TMS
fuente
1

use js para verificar si el niño offsetHeightes más que los padres ... si es así, haga que los padres se desborden scroll/hidden/autolo que quiera y también display:blocken el más div ..

Vivek Chandra
fuente
1

Puede verificar los límites relativos al desplazamiento principal.

// 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 pasa este elemento, le dirá si sus límites están completamente dentro de un contenedor y, por defecto, será el elemento primario de desplazamiento del elemento si no se proporciona un contenedor explícito. Usa

Mike Samuel
fuente
1

Otro problema que debe considerar es la falta de disponibilidad de JS. Piensa en el encantamiento progresivo o la degradación elegante. Yo sugeriría:

  • agregar "más botón" por defecto
  • Agregar reglas de desbordamiento por defecto
  • botón oculto y, si es necesario, modificaciones CSS en JS después de comparar element.scrollHeight con element.clientHeight
MFix
fuente
1

Aquí hay un violín para determinar si un elemento se ha desbordado usando un contenedor wrap con overflow: hidden y JQuery height () para medir la diferencia entre el contenedor y un contenido interno div.

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);
});

Fuente: Marcos y extensiones en jsfiddle.net

usuario1295799
fuente
1

Esta es la solución jQuery que funcionó para mí. clientWidthetc. no funcionó.

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

Si esto no funciona, asegúrese de que el elemento primario de los elementos tenga el ancho deseado (personalmente, tuve que usarlo parent().parent()). positionEs relativo al elemento primario. También lo he incluido extra_widthporque mis elementos ("etiquetas") contienen imágenes que requieren poco tiempo para carga, pero durante la llamada a la función tienen ancho cero, lo que estropea el cálculo. Para evitarlo, utilizo el siguiente código de llamada:

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();
    }
});

Espero que esto ayude.

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

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

Esto me funcionó. Gracias.

Krunal
fuente
3
¿Cómo es esto diferente de la respuesta de micnic?
cuidadosoahora1
0

La alternativa de jquery a la respuesta es usar la tecla [0] para acceder al elemento sin formato como este:

if ($('#elem')[0].scrollHeight > $('#elem')[0].clientHeight){
Antonio
fuente
0

He extendido Element por razones de encapsulación. De la respuesta 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;
}

Úselo así

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

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