2009-09-24 13 views
101

Sembra che in jQuery quando un elemento non è larghezza visibile() restituisce 0. Ha un senso, ma ho bisogno di ottenere la larghezza di un tavolo al fine di imposta la larghezza del genitore prima di mostrare il genitore.jQuery - Ottenere larghezza di Element, quando non è visibile (display: none)

Come indicato di seguito, v'è il testo nel genitore, che rende lo skew genitore e guardare brutto. Voglio che il genitore sia largo solo quanto il tavolo e abbia il testo a capo.

<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 restituisce sempre 0 dal momento che non è visibile (è la mia ipotesi perché mi dà un numero quando visibile). C'è un modo per ottenere la larghezza della tabella senza rendere visibile il genitore?

risposta

89

Ecco un trucco che ho usato. Si tratta di aggiungere alcune proprietà CSS per far sì che jQuery pensi che l'elemento sia visibile, ma in realtà è ancora nascosto.

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

Si tratta di una specie di hack, ma sembra funzionare bene per me.

UPDATE

Da allora ho scritto un che si occupa di questo argomento. Il metodo sopra utilizzato ha il potenziale per essere problematico poiché si stanno reimpostando le proprietà CSS sui valori vuoti. E se avessero valori in precedenza? La soluzione aggiornata utilizza il metodo swap() che è stato trovato nel codice sorgente di jQuery.

Codice da post sul blog di riferimento:

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

che ha funzionato perfettamente, grazie! – Martin

+1

Non farà in modo che il browser ridisegni la pagina due volte? Se è così, questa è una soluzione subottimale. – marcusklaas

+0

@Tim Banks molto bello! In realtà ho scritto un'estensione simile. Quello che ho notato è un comportamento molto strano in ** Firefox **, width() restituisce 0, sia per il tuo che per il mio plugin entrambi. E per di più non influisce mai sul riempimento. http://jsfiddle.net/67cgB/. Sto passando il tempo più difficile a capire cos'altro poter fare per risolverlo. –

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

bel trucco sporco !!! Assicurati che il clone abbia le proprietà CSS corrette (ad esempio, se la dimensione del font dell'elemento originale è 15, dovresti usare clone.css ("visibility", "hidden"). Css ("font-size", "15px")) – alex

+13

Giusto per sottolineare, questo non funzionerà se il tuo elemento ha ereditato la sua larghezza da qualsiasi genitore. Erediterà semplicemente la larghezza del corpo che non è corretta. – Dean

+2

Ho modificato 'clone.css (" visibility "," hidden ");' a 'clone.css ({" visibility ":" hidden "," display ":" table "});' che risolve il problema evidenziato @Dean – isapir

3

Grazie per l'invio della funzione realWidth sopra, in realtà mi ha aiutato. In base alla funzione "realWidth" di cui sopra, ho scritto, un reset CSS, (motivo descritto di seguito).

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" ottiene la larghezza di un tag esistente. Ho provato questo con alcuni tag immagine. Il problema era che, quando l'immagine ha dato dimensione CSS per larghezza (o larghezza massima), non si otterrà mai la dimensione reale di quell'immagine. Forse, l'img ha "max-width: 100%", la funzione "realWidth" lo clona e lo aggiunge al corpo. Se la dimensione originale dell'immagine è più grande del corpo, si ottiene la dimensione del corpo e non la dimensione reale di quell'immagine.

8

Sulla base della risposta di Roberts, ecco la mia funzione. Questo funziona per me se l'elemento o il suo genitore sono stati sfumati via jQuery, possono ottenere dimensioni interne o esterne e anche restituire i valori di offset.

/edit1: ha riscritto la funzione. ora è più piccolo e può essere chiamato direttamente sull'oggetto

/edit2: la funzione ora inserirà il clone subito dopo l'elemento originale anziché il corpo, consentendo al clone di mantenere dimensioni ereditate.

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

Versione YUI per chi ne ha bisogno: https: //gist.github.com/samueljseay/13c2e69508563b2f0458 –

+0

L'offset è corretto per l'articolo dato che si tratta del clone e non dell'elemento "reale"? Dovresti usare $ this.offset(). Top? Inoltre, curioso su cosa stai usando in alto/a sinistra? Sto solo usando 'width', ma mi chiedo se dovrei preoccuparmi di questi offset per qualche ragione. – Terry

0

Oltre a the answer posted by Tim Banks, che ha seguito per risolvere il mio problema ho dovuto modificare una cosa semplice.

Qualcun altro potrebbe avere lo stesso problema; Sto lavorando con un menu a discesa Bootstrap in un menu a discesa. Ma il testo può essere più ampio come il contenuto corrente a questo punto (e non ci sono molti buoni modi per risolverlo attraverso i CSS).

ho usato:

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

invece, che definisce il contenitore a una tabella che scala sempre in larghezza se il contenuto è più ampio.

3

Cerco di trovare la funzione di lavoro per l'elemento nascosto ma mi rendo conto che il CSS è molto più complesso di quanto tutti pensano. Ci sono molte nuove tecniche di layout in CSS3 che potrebbero non funzionare per tutte le risposte precedenti come box flessibile, griglia, colonna o anche elemento all'interno di un elemento genitore complesso.

esempio Flexibox enter image description here

Credo che l'unica & semplice soluzione sostenibile è rendering in tempo reale. A quel tempo, il browser dovrebbe darti che dimensione elemento corretto.

Purtroppo, JavaScript non fornisce alcun evento diretto da notificare quando un elemento è mostrato o nascosto. Tuttavia, creo una funzione basata sull'API DOM Modified API che eseguirà la funzione di callback quando viene modificata la visibilità dell'elemento.

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

    // render or adjust something 
}); 

Per ulteriori informazioni, visitare il mio progetto GitHub.

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

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

+4

** Il CSS è molto complesso di quanto tutti pensino ** Questo è vero ... – dgellow

1

Prima di prendere la larghezza del display più spettacolo genitore, poi prendere la larghezza e, infine, rendere la pelle di visualizzazione principale. Proprio come segue

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

Il più grande problema di essere persa dalla maggior parte delle soluzioni è che la larghezza di un elemento è spesso cambiata CSS in base a dove è ambito in html.

Se dovessi determinare l'offset di un elemento aggiungendone un clone al corpo quando ha stili che si applicano solo nel suo ambito corrente otterrei la larghezza sbagliata.

ad esempio:

// css

.module-contenitore .il mio-elem { border: 60px solido nero; }

ora quando provo a determinare la larghezza di my-elem nel contesto del corpo, verrà eliminata di 120 pixel. Potresti clonare il contenitore del modulo, ma il tuo JS non dovrebbe conoscere questi dettagli.

Non l'ho provato ma essenzialmente la soluzione di Soul_Master sembra essere l'unica in grado di funzionare correttamente. Ma sfortunatamente guardando all'implementazione sarà probabilmente costoso da usare e male per le prestazioni (come la maggior parte delle soluzioni presentate qui).

Se possibile, utilizzare la visibilità: nascosta. Questo renderà ancora l'elemento, permettendoti di calcolare la larghezza senza tante storie.

0

Una soluzione, sebbene non funzioni in tutte le situazioni, consiste nel nascondere l'elemento impostando l'opacità su 0. Un elemento completamente trasparente avrà larghezza.

Il draw back è che l'elemento occuperà ancora spazio, ma non sarà un problema in tutti i casi.

Ad esempio:

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

Se avete bisogno della larghezza di qualcosa che è nascosto e non si può non-nasconderlo per qualsiasi motivo, è possibile clonare esso, modificare il CSS in modo da visualizzare fuori dalla pagina , rendilo invisibile e poi misuralo. L'utente sarà nessuno più saggio se è nascosto e cancellato in seguito.

Alcune delle altre risposte qui rendono solo la visibilità nascosta che funziona, ma occuperà un punto vuoto sulla pagina per una frazione di secondo.

Esempio:

$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

Non funzionerà se le dimensioni dell'elemento sono influenzate da un contenitore genitore o da un selettore CSS più complesso, basato sulla gerarchia del layout. –

Problemi correlati