jQuery - Elementbreite abrufen, wenn nicht sichtbar (Anzeige: Keine)

108

Es scheint wie in jQuery, wenn ein Element nicht sichtbar ist. Width () gibt 0 zurück. Sinnvoll, aber ich muss die Breite einer Tabelle abrufen, um die Breite des übergeordneten Elements festzulegen, bevor ich das übergeordnete Element zeige.

Wie unten erwähnt, enthält das übergeordnete Element Text, der das übergeordnete Element schief macht und böse aussieht. Ich möchte, dass das übergeordnete Element nur so breit wie die Tabelle ist und der Text umbrochen wird.

<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 gibt immer 0 zurück, da es nicht sichtbar ist (ist meine Vermutung, da es mir eine Zahl gibt, wenn es sichtbar ist). Gibt es eine Möglichkeit, die Breite der Tabelle zu ermitteln, ohne das übergeordnete Element sichtbar zu machen?

Martin
quelle

Antworten:

93

Hier ist ein Trick, den ich benutzt habe. Es beinhaltet das Hinzufügen einiger CSS-Eigenschaften, damit jQuery glaubt, das Element sei sichtbar, aber tatsächlich ist es immer noch verborgen.

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

Es ist eine Art Hack, aber es scheint gut für mich zu funktionieren.

AKTUALISIEREN

Ich habe seitdem einen Blog-Beitrag geschrieben , der dieses Thema behandelt. Die oben verwendete Methode kann problematisch sein, da Sie die CSS-Eigenschaften auf leere Werte zurücksetzen. Was wäre, wenn sie vorher Werte hätten? Die aktualisierte Lösung verwendet die swap () -Methode, die im jQuery-Quellcode gefunden wurde.

Code aus dem referenzierten Blog-Beitrag:

//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));
Tim Banks
quelle
1
Wird der Browser dadurch die Seite nicht zweimal neu zeichnen? Wenn ja, ist dies eine suboptimale Lösung.
Marcusklaas
@ Tim Banks sehr schön! Ich habe tatsächlich eine ähnliche Erweiterung geschrieben. Was mir aufgefallen ist, ist ein sehr seltsames Verhalten in Firefox . Width () gibt 0 für Ihr und mein Plugin zurück. Und obendrein wird die Polsterung nie berücksichtigt. jsfiddle.net/67cgB . Es fällt mir am schwersten, herauszufinden, was ich sonst noch tun kann, um das Problem zu beheben.
Mark Pieszak - Trilon.io
Wie kann ich diesen Hack in jquery Akkordeon verwenden, um versteckte Akkordeon Dimensionen zu erhalten? Benötigen Sie Ihre Hilfe.
Deepak Ingole
1
@FercoCQ Ich habe den Code aus dem referenzierten Blog-Beitrag hinzugefügt.
Tim Banks
1
.andSelf () ist in jQuery 1.8+ veraltet und in jQuery 3+ entfernt . Wenn Sie jQuery 1.8+ verwenden, ersetzen Sie .andSelf () durch .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"));
Fábio Paiva
quelle
dreckig schöner Trick !!! Stellen Sie sicher, dass der Klon die richtigen CSS-Eigenschaften hat (z. B. wenn die Schriftgröße des ursprünglichen Elements 15 beträgt, sollten Sie clone.css ("Sichtbarkeit", "versteckt"). CSS ("Schriftgröße", "15px") verwenden. );)
Alex
18
Nur um darauf hinzuweisen, funktioniert dies nicht, wenn Ihr Element seine Breite von einem übergeordneten Element geerbt hat. Es wird nur die Körperbreite geerbt, die falsch ist.
Dean
3
Ich habe geändert, clone.css("visibility","hidden");um clone.css({"visibility":"hidden", "display":"table"});das Problem zu beheben, auf das @Dean
isapir am
Vielen Dank! Es hat mit kleinen Änderungen für meinen Fall funktioniert: Ich habe es geändert, um ein zu klonendes Objekt und einen Selektor darin zu unterstützen, um die tatsächliche Breite zu erhalten. Nicht immer möchten wir dasselbe Stammobjekt messen, das verborgen ist.
Falsarella
Früher habe ich den gleichen Ansatz gewählt und an diesem Tag eine Reihe von Abstimmungen erhalten. Seltsam, dass Sie so viele positive Stimmen haben: D Ich werde Ihnen sagen, warum dies eine schlechte Lösung ist und worauf ich vor einigen Jahren unter meiner Antwort hingewiesen habe. Sobald Sie das Element kopiert und direkt in den Text eingefügt haben, entfernen Sie mit Sicherheit alle CSS-Elemente, die sich auf dieses bestimmte Element beziehen. Beispielsweise. daraus: #some wrapper .hidden_element{/*Some CSS*/}Du machst das : body .hidden_element. Das #some_wrapper .hidden_elementwird nicht mehr benutzt! Infolgedessen kann Ihre berechnete Größe von der ursprünglichen Größe abweichen. TLTR: Sie verlieren nur Stile
stattdessen
8

Basierend auf Roberts Antwort ist hier meine Funktion. Dies funktioniert bei mir, wenn das Element oder sein übergeordnetes Element über jQuery ausgeblendet wurde, entweder innere oder äußere Dimensionen abrufen kann und auch die Versatzwerte zurückgibt.

/ edit1: Die Funktion wurde neu geschrieben. Es ist jetzt kleiner und kann direkt auf dem Objekt aufgerufen werden

/ edit2: Die Funktion fügt jetzt den Klon direkt nach dem ursprünglichen Element anstelle des Körpers ein, sodass der Klon geerbte Dimensionen beibehalten kann.

$.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();
Marco Kerwitz
quelle
YUI-Version für diejenigen, die es brauchen: gist.github.com/samueljseay/13c2e69508563b2f0458
Code Novitiate
Ist offsetTop für das Element korrekt, da es sich um den Klon und nicht um das "echte" Element handelt? Sollten Sie $ this.offset (). Top verwenden? Neugierig, wofür Sie oben / links verwenden? Ich verwende nur 'width', frage mich aber, ob ich mir diese Offsets aus irgendeinem Grund Sorgen machen sollte.
Terry
3

Vielen Dank, dass Sie die realWidth-Funktion oben veröffentlicht haben. Sie hat mir wirklich geholfen. Basierend auf der obigen "realWidth" -Funktion habe ich einen CSS-Reset geschrieben (Grund unten beschrieben).

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" erhält die Breite eines vorhandenen Tags. Ich habe dies mit einigen Bild-Tags getestet. Das Problem war, wenn das Bild eine CSS-Dimension pro Breite (oder maximale Breite) angegeben hat, erhalten Sie nie die tatsächliche Dimension dieses Bildes. Vielleicht hat das Bild "max-width: 100%", die "realWidth" -Funktion klont es und hängt es an den Körper an. Wenn die Originalgröße des Bildes größer als der Körper ist, erhalten Sie die Größe des Körpers und nicht die tatsächliche Größe dieses Bildes.

Robert
quelle
3

Ich versuche, eine funktionierende Funktion für versteckte Elemente zu finden, aber mir ist klar, dass CSS viel komplexer ist, als jeder denkt. Es gibt viele neue Layouttechniken in CSS3, die möglicherweise nicht für alle vorherigen Antworten wie flexible Boxen, Raster, Spalten oder sogar Elemente in komplexen übergeordneten Elementen funktionieren.

Flexibox Beispiel Geben Sie hier die Bildbeschreibung ein

Ich denke, die einzige nachhaltige und einfache Lösung ist das Echtzeit-Rendering . Zu diesem Zeitpunkt sollte der Browser die richtige Elementgröße angeben .

Leider bietet JavaScript kein direktes Ereignis, um zu benachrichtigen, wenn ein Element angezeigt oder ausgeblendet wird. Ich erstelle jedoch eine Funktion basierend auf der DOM Attribute Modified API, die die Rückruffunktion ausführt, wenn die Sichtbarkeit des Elements geändert wird.

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

    // render or adjust something
});

Weitere Informationen finden Sie in meinem Projekt GitHub.

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

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

Seelen Meister
quelle
4
CSS ist viel komplexer als jeder denkt Das ist so wahr ...
dgellow
3

Wenn Sie die Breite von etwas benötigen, das verborgen ist, und Sie es aus irgendeinem Grund nicht ausblenden können, können Sie es klonen, das CSS so ändern, dass es auf der Seite angezeigt wird, es unsichtbar machen und es dann messen. Der Benutzer ist nicht klüger, wenn er danach ausgeblendet und gelöscht wird.

Einige der anderen Antworten hier machen die Sichtbarkeit nur ausgeblendet, was funktioniert, aber sie nimmt für den Bruchteil einer Sekunde einen leeren Punkt auf Ihrer Seite ein.

Beispiel:

$itemClone = $('.hidden-item').clone().css({
        'visibility': 'hidden',
        'position': 'absolute',
        'z-index': '-99999',
        'left': '99999999px',
        'top': '0px'
    }).appendTo('body');
var width = $itemClone.width();
$itemClone.remove();
Rannmann
quelle
2
Es funktioniert nicht, wenn die Elementdimensionen von einem übergeordneten Container oder einem komplexeren CSS-Selektor basierend auf der Layouthierarchie beeinflusst werden.
Sebastian Nowak
2

Bevor Sie die Breite nehmen, lassen Sie die übergeordnete Anzeige anzeigen, nehmen Sie dann die Breite und lassen Sie die übergeordnete Anzeige schließlich ausblenden. Genau wie beim Folgen

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

Eine Lösung, die nicht in allen Situationen funktioniert, besteht darin, das Element auszublenden, indem die Deckkraft auf 0 gesetzt wird. Ein vollständig transparentes Element hat eine Breite.

Der Nachteil ist, dass das Element immer noch Platz beansprucht, aber das ist nicht in allen Fällen ein Problem.

Beispielsweise:

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

Das größte Problem, das von den meisten Lösungen hier übersehen wird, ist, dass die Breite eines Elements häufig durch CSS geändert wird, je nachdem, wo es sich in HTML befindet.

Wenn ich offsetWidth eines Elements durch Anhängen eines Klons an den Body bestimmen würde, wenn es Stile enthält, die nur in seinem aktuellen Bereich gelten, würde ich die falsche Breite erhalten.

beispielsweise:

// css

.module-container .my-elem {Rand: 60px festes Schwarz; }}

Wenn ich jetzt versuche, die Breite meines Elems im Kontext des Körpers zu bestimmen, wird sie um 120 Pixel größer sein. Sie könnten stattdessen den Modulcontainer klonen, aber Ihr JS sollte diese Details nicht kennen müssen.

Ich habe es nicht getestet, aber im Grunde scheint die Lösung von Soul_Master die einzige zu sein, die richtig funktionieren könnte. Wenn man sich die Implementierung ansieht, ist die Verwendung wahrscheinlich teuer und die Leistung schlecht (wie die meisten der hier vorgestellten Lösungen auch).

Wenn möglich, verwenden Sie stattdessen Sichtbarkeit: versteckt. Dadurch wird das Element weiterhin gerendert, sodass Sie die Breite ohne großen Aufwand berechnen können.

Code Novitiate
quelle
0

Zusätzlich zu der Antwort von Tim Banks , die zur Lösung meines Problems folgte, musste ich eine einfache Sache bearbeiten.

Jemand anderes könnte das gleiche Problem haben; Ich arbeite mit einem Bootstrap-Dropdown in einem Dropdown. Der Text kann jedoch an dieser Stelle breiter sein als der aktuelle Inhalt (und es gibt nicht viele gute Möglichkeiten, dies durch CSS zu beheben).

Ich benutzte:

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

Stattdessen wird der Container auf eine Tabelle gesetzt, deren Breite immer skaliert, wenn der Inhalt breiter ist.

Mathijs Segers
quelle
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);
    });
});

Beim Hover können wir die Listenelementhöhe berechnen.

Rajashekar
quelle
0

Wie bereits erwähnt, garantiert die Methode zum Klonen und Anhängen an anderer Stelle nicht die gleichen Ergebnisse, da das Styling unterschiedlich sein kann.

Unten ist mein Ansatz. Es wandert die Eltern hinauf und sucht nach dem Elternteil, der für das Verstecken verantwortlich ist. Anschließend wird es vorübergehend ausgeblendet, um die erforderliche Breite, Höhe usw. zu berechnen.

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

Ibraheem
quelle