jQuery: obtiene el ancho del elemento cuando no es visible (Pantalla: ninguna)

Parece que en jQuery cuando un elemento no es visible, width () devuelve 0. Tiene sentido, pero necesito obtener el ancho de una tabla para establecer el ancho del elemento primario antes de mostrar el elemento principal.

Como se indica a continuación, hay texto en el elemento primario, lo 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.

Text here ... Can get very long and skew the parent ...
Text here too ... which is why I want to shrink the parent based on the table

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 está visible (es mi suposición ya que me da un número cuando está visible). ¿Hay alguna manera de obtener el ancho de la tabla sin hacer que el padre sea visible?

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 una especie de truco, pero parece funcionar bien para mí.

ACTUALIZAR

Desde entonces, he escrito una publicación de blog 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 publicación de blog referenciada:

 //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)); 
 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")); 

Según 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(); 

Gracias por publicar la función realWidth arriba, 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 tags 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, entonces obtienes el tamaño del cuerpo y no el tamaño real de esa imagen.

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 de flexibox enter image description here

Creo que la única solución sostenible y simple es la representación 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 callback 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

Si necesita el ancho de algo que está oculto y no puede ocultarlo por alguna razón, puede clonarlo, cambiar el CSS para que se muestre fuera de la página, hacerlo invisible y luego medirlo. 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á un espacio en blanco en su página por 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(); 

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 

Antes de tomar el ancho, haga que se muestre la pantalla principal, luego tome el ancho y finalmente haga que el padre muestre oculto. Como seguir

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

El problema más importante que se pasa por alto en la mayoría de las soluciones aquí es que el ancho de un elemento a menudo se cambia por CSS en función de su scope en html.

Si tuviera que determinar offsetWidth de un elemento agregando 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-container .my-elem {border: 60px negro sólido; }

ahora cuando trato de determinar el ancho de mi elem en el contexto del cuerpo, saldrá 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. Pero desafortunadamente al observar la implementación, probablemente sea costoso de usar y malo para el rendimiento (ya que la mayoría de las soluciones presentadas aquí también lo son).

Si es posible, use visibilidad: oculto en su lugar. Esto seguirá representando el elemento, lo que le permite calcular el ancho sin todo el alboroto.

Además de la respuesta publicada por Tim Banks , que siguió a la solución de mi problema, 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).

Solía:

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

en cambio, que establece el contenedor en una tabla que siempre se amplía en ancho si el contenido es más ancho.

 jQuery(".megamenu li.level0 .dropdown-container .sub-column ul .level1").on("mouseover", function () { var sum = 0; jQuery(this).find('ul li.level2').each(function(){ sum -= jQuery(this).height(); jQuery(this).parent('ul').css('margin-top', sum / 1.8); console.log(sum); }); }); 

En el vuelo estacionario, podemos calcular la altura del elemento de la lista.