web-dev-qa-db-ja.com

jQuery-表示されないときに要素の幅を取得(表示:なし)

JQueryでは要素が表示されていないように見えますwidth()は0を返します。理にかなっていますが、親を表示する前に親の幅を設定するためにテーブルの幅を取得する必要があります。

以下に示すように、親にはテキストがあり、それにより親が歪んで見た目が悪くなります。親の幅をテーブルと同じくらいにし、テキストを折り返すようにします。

<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は表示されていないため、常に0を返します(表示されているときに数字が表示されるため、推測です)。親を表示せずにテーブルの幅を取得する方法はありますか?

106
Martin

これが私が使ったトリックです。 CSSプロパティをいくつか追加して、jQueryに要素が表示されていると思わせますが、実際にはまだ非表示になっています。

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

それは一種のハックですが、私にとってはうまくいくようです。

UPDATE

それ以来、このトピックをカバーする ブログ投稿 を書きました。上記の方法では、CSSプロパティを空の値にリセットしているため、問題が発生する可能性があります。以前に値があった場合はどうなりますか?更新されたソリューションは、jQueryソースコードで見つかったswap()メソッドを使用します。

参照ブログ投稿のコード:

//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));
91
Tim Banks
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"));
40
Fábio Paiva

ロバーツの回答に基づいて、ここに私の機能があります。これは、要素またはその親がjQueryを介してフェードアウトされ、内側または外側の寸法を取得でき、オフセット値も返す場合に機能します。

/ edit1:関数を書き直しました。小さくなり、オブジェクトで直接呼び出すことができます

/ edit2:関数は、本体ではなく元の要素の直後にクローンを挿入するようになり、クローンが継承されたディメンションを維持できるようになりました。

$.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();
8
Marco Kerwitz

私は隠された要素の機能を見つけようとしますが、CSSは誰もが考えるよりもはるかに複雑であることを認識しています。 CSS3には、柔軟なボックス、グリッド、列、または複雑な親要素内の要素など、以前のすべての回答に対して機能しない可能性のある新しいレイアウトテクニックが多数あります。

flexiboxの例 enter image description here

持続可能でシンプルなソリューションは、リアルタイムレンダリングだけだと思います。その時点で、ブラウザはその正しい要素サイズを提供するはずです。

悲しいことに、JavaScriptは、要素が表示または非表示になったときに通知する直接イベントを提供しません。ただし、要素の可視性が変更されたときにコールバック関数を実行するDOM Attribute Modified APIに基づいていくつかの関数を作成します。

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

    // render or adjust something
});

詳細については、私のプロジェクトGitHubをご覧ください。

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

デモ: http://jsbin.com/ETiGIre/7

3
Soul_Master

上記のrealWidth関数を投稿していただきありがとうございます。本当に役立ちました。上記の「realWidth」関数に基づいて、CSSリセットを記述しました(理由は以下で説明します)。

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」は、既存のタグの幅を取得します。これをいくつかの画像タグでテストしました。問題は、画像が幅(または最大幅)ごとにCSSの寸法を指定した場合、その画像の実際の寸法を取得できないことでした。おそらく、imgには「max-width:100%」があり、「realWidth」関数はそれを複製して本体に追加します。画像の元のサイズが本体よりも大きい場合、その画像の実際のサイズではなく、本体のサイズを取得します。

3
Robert

非表示の幅が必要で、何らかの理由で非表示を解除できない場合は、クローンを作成し、CSSを変更してページから見えないようにし、非表示にしてから測定することができます。非表示にして後で削除すると、ユーザーは賢くなくなります。

ここでの他の答えのいくつかは、可視性を非表示にするだけで機能しますが、ページの空白部分を数秒で占有します。

例:

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

1つの解決策は、すべての状況で機能するわけではありませんが、不透明度を0に設定して要素を非表示にすることです。完全に透明な要素には幅があります。

欠点は、要素がまだスペースを占有することですが、それはすべての場合で問題になるわけではありません。

例えば:

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

幅を取得する前に、親ディスプレイを表示してから、幅を取得し、最終的に親ディスプレイを非表示にします。次のように

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

ここでのほとんどのソリューションで見落とされている最大の問題は、htmlのスコープに基づいてCSSによって要素の幅がしばしば変更されることです。

現在のスコープにのみ適用されるスタイルがあるときに、要素のクローンをボディに追加することで要素のoffsetWidthを決定する場合、間違った幅が取得されます。

例えば:

// css

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

ボディのコンテキストで私のエレムの幅を決定しようとすると、120pxずつ外に出ます。代わりにモジュールコンテナーを複製することもできますが、JSはこれらの詳細を知る必要はありません。

私はそれをテストしていませんが、本質的にSoul_Masterのソリューションが適切に機能する唯一のソリューションのようです。しかし、残念ながら実装を見ると、使用するのにコストがかかり、パフォーマンスが低下する可能性があります(ここで紹介するほとんどのソリューションも同様です)。

可能な場合は、代わりにvisibility:hiddenを使用してください。これにより、要素が引き続きレンダリングされ、面倒なことなく幅を計算できます。

1
Code Novitiate

Tim Banksが投稿した回答 に加えて、私の問題の解決に続いて、1つの簡単なものを編集する必要がありました。

他の誰かが同じ問題を抱えている可能性があります。ドロップダウンでBootstrapドロップダウンを使用しています。ただし、この時点でテキストは現在のコンテンツほど広くなる可能性があります(CSSを使用してそれを解決する良い方法はあまりありません)。

私が使用した:

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

代わりに、コンテンツがより広い場合に常に幅が拡大縮小するテーブルにコンテナを設定します。

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

ホバーすると、リストアイテムの高さを計算できます。

0
rajashekar

前に述べたように、スタイリングが異なる可能性があるため、クローンおよび別の場所にアタッチする方法は同じ結果を保証しません。

以下は私のアプローチです。隠れている親を探して親を上に移動し、必要な幅、高さなどを計算するために一時的に非表示を解除します。

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