2009-09-24 13 views
101

Parece que en jQuery cuando un elemento no es visible width() devuelve 0. Tiene sentido, pero necesito obtener el ancho de una tabla para poder establecer el ancho del padre antes de mostrar el padre.jQuery - Obtenga el ancho del elemento cuando no sea visible (Pantalla: ninguna)

Como se indica a continuación, hay texto en el elemento primario que hace que el elemento primario se vea torcido y se vea desagradable. Quiero que el padre sea tan ancho como la tabla y tenga el texto ajustado.

<div id="parent"> 
    Text here ... Can get very long and skew the parent 
    <table> ... </table> 
    Text here too ... which is why I want to shrink the parent based on the table 
</div> 

CSS:

#parent 
{ 
    display: none; 
} 

Javascript:

var tableWidth = $('#parent').children('table').outerWidth(); 
if (tableWidth > $('#parent').width()) 
{ 
    $('#parent').width(tableWidth); 
} 

TABLEWIDTH siempre devuelve 0, ya que no es visible (es mi conjetura, ya que me da un número cuando no visible). ¿Hay alguna manera de obtener el ancho de la tabla sin hacer que el padre sea visible?

Respuesta

89

Aquí hay un truco que he usado. Implica agregar algunas propiedades CSS para hacer que jQuery piense que el elemento es visible, pero de hecho todavía está oculto.

var $table = $("#parent").children("table"); 
$table.css({ position: "absolute", visibility: "hidden", display: "block" }); 
var tableWidth = $table.outerWidth(); 
$table.css({ position: "", visibility: "", display: "" }); 

Es un truco, pero parece funcionar bien para mí.

ACTUALIZACIÓN

he escrito desde entonces un que cubre este tema. El método utilizado anteriormente tiene el potencial de ser problemático ya que está restableciendo las propiedades de CSS a valores vacíos. ¿Qué pasa si tenían valores previamente? La solución actualizada usa el método swap() que se encontró en el código fuente de jQuery.

Código de entrada en el blog de referencia:

//Optional parameter includeMargin is used when calculating outer dimensions 
(function ($) { 
$.fn.getHiddenDimensions = function (includeMargin) { 
    var $item = this, 
    props = { position: 'absolute', visibility: 'hidden', display: 'block' }, 
    dim = { width: 0, height: 0, innerWidth: 0, innerHeight: 0, outerWidth: 0, outerHeight: 0 }, 
    $hiddenParents = $item.parents().andSelf().not(':visible'), 
    includeMargin = (includeMargin == null) ? false : includeMargin; 

    var oldProps = []; 
    $hiddenParents.each(function() { 
     var old = {}; 

     for (var name in props) { 
      old[name] = this.style[name]; 
      this.style[name] = props[name]; 
     } 

     oldProps.push(old); 
    }); 

    dim.width = $item.width(); 
    dim.outerWidth = $item.outerWidth(includeMargin); 
    dim.innerWidth = $item.innerWidth(); 
    dim.height = $item.height(); 
    dim.innerHeight = $item.innerHeight(); 
    dim.outerHeight = $item.outerHeight(includeMargin); 

    $hiddenParents.each(function (i) { 
     var old = oldProps[i]; 
     for (var name in props) { 
      this.style[name] = old[name]; 
     } 
    }); 

    return dim; 
} 
}(jQuery)); 
+0

Eso funcionó a la perfección, gracias! – Martin

+1

¿No hará esto que el navegador vuelva a dibujar la página dos veces? Si es así, esta es una solución subóptima. – marcusklaas

+0

@Tim Banks muy bien! De hecho, escribí una extensión similar. Lo que he notado es un comportamiento muy extraño en ** Firefox **, width() devuelve 0, tanto para ti como para mi complemento. Y además de eso, nunca tiene en cuenta el relleno. http://jsfiddle.net/67cgB/. Me está costando mucho averiguar qué más podría hacer para solucionarlo. –

38
function realWidth(obj){ 
    var clone = obj.clone(); 
    clone.css("visibility","hidden"); 
    $('body').append(clone); 
    var width = clone.outerWidth(); 
    clone.remove(); 
    return width; 
} 
realWidth($("#parent").find("table:first")); 
+0

buen truco sucio !!! Asegúrese de que el clon tenga las propiedades CSS correctas (por ejemplo, si el tamaño de fuente del elemento original es 15, debe usar clone.css ("visibilidad", "oculto"). Css ("font-size", "15px"));) – alex

+13

Solo para señalar, esto no funcionará si su elemento ha heredado su ancho de cualquier elemento primario. Simplemente heredará el ancho del cuerpo que es incorrecto. – Dean

+2

Modifiqué 'clone.css (" visibility "," hidden ");' to 'clone.css ({" visibility ":" hidden "," display ":" table "});' que resuelve el problema señalado por @Dean – isapir

3

Gracias por publicar la función realWidth anterior, lo que realmente me ayudó. Basado en la función "realWidth" anterior, escribí, un restablecimiento de CSS, (razón que se describe a continuación).

function getUnvisibleDimensions(obj) { 
    if ($(obj).length == 0) { 
     return false; 
    } 

    var clone = obj.clone(); 
    clone.css({ 
     visibility:'hidden', 
     width : '', 
     height: '', 
     maxWidth : '', 
     maxHeight: '' 
    }); 
    $('body').append(clone); 
    var width = clone.outerWidth(), 
     height = clone.outerHeight(); 
    clone.remove(); 
    return {w:width, h:height}; 
} 

"realWidth" obtiene el ancho de una etiqueta existente. Probé esto con algunas etiquetas de imagen. El problema era que, cuando la imagen daba dimensión CSS por ancho (o ancho máximo), nunca obtendría la dimensión real de esa imagen. Quizás, el img tiene "max-width: 100%", la función "realWidth" lo clona y lo agrega al cuerpo. Si el tamaño original de la imagen es más grande que el cuerpo, obtendrá el tamaño del cuerpo y no el tamaño real de esa imagen.

8

Basado en la respuesta de Roberts, aquí está mi función. Esto funciona para mí si el elemento o su elemento primario se han desvanecido a través de jQuery, puede obtener dimensiones internas o externas y también devuelve los valores de desplazamiento.

/edit1: reescribió la función. ahora es más pequeño y se puede invocar directamente sobre el objeto

/edit2: la función ahora insertará el clon justo después del elemento original en lugar del cuerpo, lo que permite que el clon mantenga las dimensiones heredadas.

$.fn.getRealDimensions = function (outer) { 
    var $this = $(this); 
    if ($this.length == 0) { 
     return false; 
    } 
    var $clone = $this.clone() 
     .show() 
     .css('visibility','hidden') 
     .insertAfter($this);   
    var result = { 
     width:  (outer) ? $clone.outerWidth() : $clone.innerWidth(), 
     height:  (outer) ? $clone.outerHeight() : $clone.innerHeight(), 
     offsetTop: $clone.offset().top, 
     offsetLeft: $clone.offset().left 
    }; 
    $clone.remove(); 
    return result; 
} 

var dimensions = $('.hidden').getRealDimensions(); 
+0

Versión de YUI para quienes lo necesitan: https: //gist.github.com/samueljseay/13c2e69508563b2f0458 –

+0

¿offsetTop es correcto para el elemento dado que es el clon y no el elemento "real"? ¿Deberías usar $ this.offset(). Top? Además, ¿por qué estás usando la parte superior/izquierda? Solo estoy usando 'ancho' pero me pregunto si debería preocuparme por esas compensaciones por alguna razón. – Terry

0

Además de the answer posted by Tim Banks, que siguió a la solución de mi problema que tuve que editar una cosa simple.

Alguien más podría tener el mismo problema; Estoy trabajando con un menú desplegable de Bootstrap en un menú desplegable. Pero el texto puede ser más amplio que el contenido actual en este punto (y no hay muchas maneras buenas de resolverlo a través de css).

I utilizados:

$table.css({ position: "absolute", visibility: "hidden", display: "table" }); 

lugar, que fija el contenedor a una tabla que siempre escalas de ancho si el contenido es más amplio.

3

Intento encontrar la función de trabajo para el elemento oculto pero me doy cuenta de que CSS es mucho más complejo de lo que todos piensan. Hay muchas técnicas de diseño nuevas en CSS3 que podrían no funcionar para todas las respuestas anteriores, como cuadro flexible, cuadrícula, columna o incluso elemento dentro del elemento padre complejo.

ejemplo Flexibox enter image description here

Creo que la única solución sostenible es sencilla & renderizado en tiempo real. En ese momento, el navegador debe darle ese tamaño de elemento correcto.

Lamentablemente, JavaScript no proporciona ningún evento directo para notificar cuando el elemento se muestra u oculta. Sin embargo, creo alguna función basada en la API modificada del atributo DOM que ejecutará la función de devolución de llamada cuando se cambie la visibilidad del elemento.

$('[selector]').onVisibleChanged(function(e, isVisible) 
{ 
    var realWidth = $('[selector]').width(); 
    var realHeight = $('[selector]').height(); 

    // render or adjust something 
}); 

Para obtener más información, visite mi proyecto GitHub.

https://github.com/Soul-Master/visible.event.js

demo: http://jsbin.com/ETiGIre/7

+4

** CSS es mucho más complejo de lo que todos piensan ** Esto es tan cierto ... – dgellow

1

Antes de tomar la anchura de hacer el programa de visualización principal, luego tomar la anchura y, finalmente, hacer que la piel de visualización principal. Al igual que tras

$('#parent').show(); 
var tableWidth = $('#parent').children('table').outerWidth(); 
$('#parent').hide(); 
if (tableWidth > $('#parent').width()) 
{ 
    $('#parent').width() = tableWidth; 
} 
1

El mayor problema que se perdió por la mayoría de soluciones aquí es que el ancho de un elemento se cambia a menudo mediante CSS en función de dónde se está en el ámbito en html.

Si tuviera que determinar la anchura de desplazamiento de un elemento añadiendo un clon de él al cuerpo cuando tiene estilos que solo se aplican en su ámbito actual, obtendría el ancho incorrecto.

por ejemplo:

// css

.module-contenedor .my-elem { frontera: 60px sólido negro; }

ahora cuando trato de determinar el ancho de mi elem en el contexto del cuerpo, será de 120px. En su lugar, podría clonar el contenedor del módulo, pero su JS no debería tener que conocer estos detalles.

No lo he probado, pero esencialmente la solución de Soul_Master parece ser la única que podría funcionar correctamente. Desafortunadamente, si observamos la implementación, es probable que resulte costoso y malo para el rendimiento (ya que la mayoría de las soluciones presentadas aquí también lo son).

Si es posible, utilice la visibilidad: oculta. Esto seguirá representando el elemento, lo que le permite calcular el ancho sin todo el alboroto.

0

Una solución, aunque no funcionará en todas las situaciones, es ocultar el elemento estableciendo la opacidad en 0. Un elemento completamente transparente tendrá ancho.

El inconveniente es que el elemento aún ocupará espacio, pero eso no será un problema en todos los casos.

Por ejemplo:

$(img).css("opacity", 0) //element cannot be seen 
width = $(img).width() //but has width 
3

Si necesita el ancho de algo que está oculto y no se puede des-ocultarlo por cualquier razón, puede clonar, cambiar el CSS por lo que muestra fuera de la página , hazlo invisible y luego mídelo. El usuario no será más prudente si está oculto y borrado después.

Algunas de las otras respuestas aquí solo ocultan la visibilidad que funciona, pero ocuparán un espacio en blanco en su página durante una fracción de segundo.

Ejemplo:

$itemClone = $('.hidden-item').clone().css({ 
     'visibility': 'hidden', 
     'position': 'absolute', 
     'z-index': '-99999', 
     'left': '99999999px', 
     'top': '0px' 
    }).appendTo('body'); 
var width = $itemClone.width(); 
$itemClone.remove(); 
+1

No funcionará si las dimensiones del elemento están influenciadas por un contenedor principal o un selector de CSS más complejo, según la jerarquía del diseño. –

Cuestiones relacionadas