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


108

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

回答:


94

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

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

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

更新

それ以来、私はこのトピックをカバーするブログ投稿を書きました。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));

1
これにより、ブラウザはページを2回再描画しませんか?もしそうなら、これは次善の解決策です。
marcusklaas

@ティムバンクスはとても素敵です!私は実際に同様の拡張機能を書きました。私が気づいているのは、Firefoxの非常に奇妙な動作です。あなたと私のプラグインの両方で、width()は0を返します。その上、パディングを考慮に入れません。jsfiddle.net/67cgB。私はそれを修正するために他に何ができるかを理解するのに最も苦労しています。
マークピエザック-Trilon.io 2013年

このハックをjqueryアコーディオン内で使用して、非表示のアコーディオンディメンションを取得する方法
Deepak Ingole 2013

1
@FercoCQ参照先のブログ投稿からコードを追加しました。
Tim Banks

1
.andSelf()はjQuery 1.8以降では非推奨であり、jQuery 3以降では削除されています。jQuery 1.8以降を使用している場合は、.andSelf()を.addBack()に置き換えます。
ティム

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

汚い素敵なトリック!!! クローンに適切なcssプロパティがあることを確認します(たとえば、元の要素のフォントサイズが15の場合、clone.css( "visibility"、 "hidden")。css( "font-size"、 "15px"を使用する必要があります。 );)
アレックス2012

18
指摘しておくと、要素が親からその幅を継承している場合、これは機能しません。正しくないボディ幅をそのまま継承します。
ディーン

3
@Deanによって指摘された問題を解決するように変更clone.css("visibility","hidden");clone.css({"visibility":"hidden", "display":"table"});ました
isapir

まことにありがとうございます!それは私の場合のための小さな変更で機能しました:私はそれを変更して、クローンするオブジェクトとその内部のセレクターをサポートして実際の幅を取得しました。常に、非表示になっている同じルートオブジェクトを測定する必要はありません。
ファルサレッラ

私は以前と同じアプローチを使っていましたが、その日はたくさんの反対票を得ました。あなたが非常に多くの賛成票を持っているのは奇妙です:Dなぜこれが悪い解決策であり、数年前の私の答えの下で何が指摘されたのかをお話しします。要素をコピーして本文に直接挿入すると、おそらくこの特定の要素に関連するすべてのCSSが削除されます。例えば。#some wrapper .hidden_element{/*Some CSS*/}これから:あなたはこれを作っています:body .hidden_element。もう#some_wrapper .hidden_element使われていません!その結果、計算されたサイズは元のサイズと異なる場合があります。TLTR:あなたはただのスタイルを失っています
その代わりに

8

ロバーツの回答に基づいて、ここに私の機能があります。要素またはその親が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();

それを必要とする人のためのYUIバージョン:gist.github.com/samueljseay/13c2e69508563b2f0458
Code Novitiate

それが「実際の」アイテムではなくクローンである場合、offsetTopはアイテムに対して正しいですか?$ this.offset()。topを使用する必要がありますか?また、あなたがトップ/レフトを何に使用しているのか知りたいですか?私は「幅」のみを使用していますが、何らかの理由でこれらのオフセットを考慮する必要があるかどうか疑問に思っています。
2016年

3

上記の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

私は隠し要素の機能を見つけようとしましたが、CSSは誰もが考えるよりもはるかに複雑であることに気づきました。CSS3には、フレキシブルボックス、グリッド、列、複雑な親要素内の要素など、以前のすべての回答では機能しない可能性のある新しいレイアウト手法がたくさんあります。

flexiboxの例 ここに画像の説明を入力してください

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

悲しいことに、JavaScriptは要素が表示または非表示になったときに通知する直接イベントを提供しません。ただし、要素の可視性が変更されたときにコールバック関数を実行するDOM属性変更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


4
CSSは誰もが考えるよりもはるかに複雑です これは本当です…
dgellow

3

非表示になっているものの幅が必要で、なんらかの理由で再表示できない場合は、クローンを作成し、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();

2
要素の寸法が、レイアウト階層に基づいて、親コンテナーまたはより複雑なCSSセレクターの影響を受ける場合は機能しません。
Sebastian Nowak 2017

2

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

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

2

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

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

例えば:

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

1

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

現在のスコープでのみ適用されるスタイルを持つ要素のクローンをbodyに追加して、要素のoffsetWidthを決定すると、間違った幅が得られます。

例えば:

// css

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

ボディのコンテキストでmy-elemの幅を決定しようとすると、120pxアウトになります。代わりにモジュールコンテナのクローンを作成することもできますが、JSがこれらの詳細を知っている必要はありません。

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

可能であれば、可視性を使用してください。代わりに非表示にします。これでも要素はレンダリングされ、手間をかけずに幅を計算できます。


0

ティムバンクスが投稿た答えに加えて私の問題の解決に続いて、1つの簡単なことを編集する必要がありました。

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

私が使用した:

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

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


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

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


0

前に述べたように、クローンと他の場所への接続メソッドは、スタイルが異なる場合があるため、同じ結果を保証しません。

以下は私のアプローチです。隠蔽を担当する親を探して親を上に移動し、一時的に再表示して、必要な幅、高さなどを計算します。

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.