jQuery - Ottieni la larghezza dell'elemento quando non è visibile (Visualizzazione: nessuno)


109

Sembra che in jQuery quando un elemento non è visibile width () restituisca 0. Ha senso, ma ho bisogno di ottenere la larghezza di una tabella per impostare la larghezza del genitore prima di mostrare il genitore.

Come notato di seguito, c'è del testo nel genitore, che lo fa distorcere e sembrare sgradevole. Voglio che il genitore sia largo quanto la tabella 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 poiché non è visibile (è la mia ipotesi poiché mi dà un numero quando visibile). C'è un modo per ottenere la larghezza della tabella senza rendere visibile il genitore?

Risposte:


95

Ecco un trucco che ho usato. Implica l'aggiunta di alcune proprietà CSS per far credere a jQuery 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: "" });

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

AGGIORNARE

Da allora ho scritto un post sul blog che copre questo argomento. Il metodo usato sopra può essere problematico poiché stai reimpostando le proprietà CSS su valori vuoti. E se avessero valori in precedenza? La soluzione aggiornata utilizza il metodo swap () che è stato trovato nel codice sorgente di jQuery.

Codice dal post del 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));

1
Questo non farà sì che il browser ridisegni la pagina due volte? Se è così, questa è una soluzione non ottimale.
marcusklaas

@Tim Banks molto gentile! 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. E per di più non tiene mai conto dell'imbottitura. jsfiddle.net/67cgB . Faccio fatica a capire cos'altro fare per risolverlo.
Mark Pieszak - Trilon.io

Come posso utilizzare questo trucco all'interno della fisarmonica jquery per ottenere dimensioni nascoste della fisarmonica? Hai bisogno del tuo aiuto.
Deepak Ingole

1
@FercoCQ Ho aggiunto il codice dal post del blog di riferimento.
Tim Banks

1
.andSelf () è deprecato in jQuery 1.8+ e rimosso in jQuery 3+ . Se stai usando jQuery 1.8+, sostituisci .andSelf () con .addBack () .
Tim

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

bel trucco sporco !!! Assicurati che il clone abbia le proprietà CSS corrette (es. Se la dimensione del carattere dell'elemento originale è 15 dovresti usare clone.css ("visibilità", "nascosto"). Css ("dimensione del carattere", "15px" );)
alex

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

3
Ho modificato clone.css("visibility","hidden");per clone.css({"visibility":"hidden", "display":"table"});risolvere il problema segnalato da @Dean
isapir

Grazie mille! Ha funzionato con piccoli cambiamenti per il mio caso: l'ho cambiato per supportare un oggetto da clonare e un selettore al suo interno per ottenerne la larghezza reale. Non tutte le volte vogliamo misurare lo stesso oggetto radice nascosto.
falsarella

Usavo lo stesso approccio e quel giorno ho ricevuto molti voti negativi. Strano che tu abbia così tanti voti positivi: D Ti dirò perché questa è una cattiva soluzione e cosa è stato indicato sotto la mia risposta pochi anni fa. Una volta che copi l'elemento e lo metti direttamente nel corpo, sicuramente rimuovi tutti i CSS relativi a questo elemento specifico. Per esempio. da questo: #some wrapper .hidden_element{/*Some CSS*/}Stai facendo questo: body .hidden_element. Non #some_wrapper .hidden_elementviene più utilizzato! Di conseguenza, le dimensioni calcolate potrebbero differire dalle dimensioni originali. TLTR: Stai solo perdendo stili
invece del

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 tramite jQuery, può ottenere dimensioni interne o esterne e restituisce anche 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 invece del corpo, consentendo al clone di mantenere le 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();

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

OffsetTop è corretto per l'elemento dato che è il clone e non l'elemento "reale"? Dovresti usare $ this.offset (). Top? Inoltre, curioso di cosa stai usando la parte superiore / sinistra? Sto usando solo "larghezza" ma mi chiedo se dovrei preoccuparmi di quegli offset per qualche motivo.
Terry

3

Grazie per aver pubblicato la funzione realWidth sopra, mi ha davvero aiutato. Basato sulla funzione "realWidth" 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. L'ho testato con alcuni tag immagine. Il problema era che, quando l'immagine ha dato la dimensione CSS per larghezza (o larghezza massima), non otterrai 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 dell'immagine.


3

Cerco di trovare una funzione funzionante per l'elemento nascosto ma mi rendo conto che i CSS sono molto complessi 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 persino elemento all'interno di un elemento genitore complesso.

esempio di flexibox inserisci qui la descrizione dell'immagine

Penso che l'unica soluzione sostenibile e semplice sia il rendering in tempo reale . A quel punto, il browser dovrebbe darti la dimensione dell'elemento corretta .

Purtroppo, JavaScript non fornisce alcun evento diretto per notificare quando l'elemento viene mostrato o nascosto. Tuttavia, creo alcune funzioni basate su DOM Attribute Modified API che eseguiranno la funzione di callback quando la visibilità dell'elemento viene modificata.

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

    // render or adjust something
});

Per ulteriori informazioni, visita il mio progetto GitHub.

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

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


4
I CSS sono molto complessi di quanto tutti pensano Questo è così vero ...
gellow

3

Se hai bisogno della larghezza di qualcosa che è nascosto e non puoi nasconderlo per qualsiasi motivo, puoi clonarlo, cambiare il CSS in modo che venga visualizzato fuori dalla pagina, renderlo invisibile e quindi misurarlo. L'utente non sarà più saggio se viene nascosto e cancellato in seguito.

Alcune delle altre risposte qui rendono solo la visibilità nascosta che funziona, ma occuperà uno spazio vuoto sulla tua 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();

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

2

Prima di prendere la larghezza, mostra il display principale, quindi prendi la larghezza e infine nascondi il display principale. Proprio come seguire

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

2

Una soluzione, sebbene non funzionerà in tutte le situazioni, è nascondere l'elemento impostando l'opacità a 0. Un elemento completamente trasparente avrà larghezza.

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

Per esempio:

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

1

Il problema più grande che manca alla maggior parte delle soluzioni qui è che la larghezza di un elemento viene spesso modificata dai CSS in base a dove è definita in html.

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

per esempio:

// css

.module-container .my-elem {border: 60px solid black; }

ora quando provo a determinare la larghezza del mio elemento nel contesto del corpo sarà fuori di 120px. Potresti invece clonare il contenitore del modulo, ma il tuo JS non dovrebbe conoscere questi dettagli.

Non l'ho testato ma essenzialmente la soluzione di Soul_Master sembra essere l'unica che potrebbe funzionare correttamente. Ma sfortunatamente guardando l'implementazione sarà probabilmente costoso da usare e dannoso per le prestazioni (come lo sono anche la maggior parte delle soluzioni presentate qui).

Se possibile, usa la visibilità: nascosta invece. Questo renderà ancora l'elemento, permettendoti di calcolare la larghezza senza troppi problemi.


0

Oltre alla risposta pubblicata da Tim Banks , che ha risolto il mio problema, ho dovuto modificare una semplice cosa.

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 tramite CSS).

Ero solito:

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

invece, che imposta il contenitore su una tabella che viene sempre ridimensionata in larghezza se il contenuto è più ampio.


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

Al passaggio del mouse possiamo calcolare l'altezza dell'elemento dell'elenco.


0

Come è stato detto prima, il metodo clone e collega altrove non garantisce gli stessi risultati in quanto lo styling potrebbe essere diverso.

Di seguito è il mio approccio. Viaggia su per i genitori alla ricerca del genitore responsabile del nascondiglio, quindi lo scopre temporaneamente per calcolare la larghezza, l'altezza richieste, ecc.

    var width = parseInt($image.width(), 10);
    var height = parseInt($image.height(), 10);

    if (width === 0) {

        if ($image.css("display") === "none") {

            $image.css("display", "block");
            width = parseInt($image.width(), 10);
            height = parseInt($image.height(), 10);
            $image.css("display", "none");
        }
        else {

            $image.parents().each(function () {

                var $parent = $(this);
                if ($parent.css("display") === "none") {

                    $parent.css("display", "block");
                    width = parseInt($image.width(), 10);
                    height = parseInt($image.height(), 10);
                    $parent.css("display", "none");
                }
            });
        }
    }

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.