jQuery - отримуйте ширину елемента, коли його не видно (дисплей: немає)


108

Схоже, в jQuery, коли елемент не видно, ширина () повертає 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 до порожніх значень. Що робити, якщо вони раніше мали значення? Оновлене рішення використовує метод swap (), знайдений у вихідному коді jQuery.

Код із посилання на щоденник:

//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
Чи не змусить браузер перемалювати сторінку двічі? Якщо так, це неоптимальне рішення.
marcusklaas

@Tim Banks дуже приємно! Я фактично написав подібне розширення. Що я помічав, це дуже дивна поведінка у Firefox , ширина () повертає 0, як для вашого, так і для мого плагіна. І на додаток до того, що він ніколи не впливає на оббивки. jsfiddle.net/67cgB . Мені важче зрозуміти, що ще можна зробити, щоб виправити це.
Марк Пешак - Trilon.io

Як я можу скористатися цим хаком усередині гармошкою jquery, щоб отримати приховані розміри акордеона? Потрібна ваша допомога.
Deepak Ingole

1
@FercoCQ Я додав код із посилання на щоденник.
Тім Бенкс

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 ("видимість", "приховано"). Css ("font-size", "15px" );)
alex

18
Зазначимо, це не спрацює, якщо ваш елемент успадкував свою ширину від будь-якого з батьків. Він просто успадкує неправильну ширину тіла.
Дін

3
Я змінив, clone.css("visibility","hidden");до clone.css({"visibility":"hidden", "display":"table"});якого вирішується проблема, на яку вказував @Dean
isapir

Щиро дякую! Він працював із невеликими змінами для мого випадку: я змінив його для підтримки об’єкта, який клонував, та селектора всередині нього, щоб отримати реальну ширину його. Не завжди ми хочемо виміряти той самий кореневий об'єкт, який приховано.
фальсарела

Раніше я використовував той самий підхід, і в цей день я отримав купу низовин. Дивно, що у вас так багато відгуків: D Я скажу вам, чому це погане рішення та на що вказували під моєю відповіддю кілька років тому. Після того як ви скопіюєте елемент і введете його безпосередньо в тіло, ви, безумовно, видаляєте всі CSS, пов'язані з цим конкретним елементом. Наприклад. від цього: #some wrapper .hidden_element{/*Some CSS*/}Ви робите це: body .hidden_element. The #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

Чи правильно offsetTop для елемента, враховуючи, що це клон, а не 'реальний' елемент? Чи варто використовувати $ this.offset (). Top? Також цікаво, для чого ви використовуєте верхнє / ліве? Я використовую лише "ширину", але цікаво, чи варто мені чомусь турбувати ці компенсації.
Террі

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 не забезпечує жодної прямої події, щоб повідомити, коли елемент відображається або приховується. Однак я створюю деяку функцію на основі API модифікованого атрибуту DOM, який буде виконувати функцію зворотного виклику при зміні видимості елемента.

$('[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, заснований на ієрархії макета.
Себастьян Новак

2

Перш ніж взяти ширину, виведіть на дисплей батьківський дисплей, потім візьміть ширину і, нарешті, згорніть батьківський екран Так само, як і далі

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

2

Одне рішення, хоча воно не працюватиме у всіх ситуаціях, - приховати елемент, встановивши непрозорість на 0. Повністю прозорий елемент матиме ширину.

Відмова полягає в тому, що елемент все ще займе місце, але це не буде проблемою у всіх випадках.

Наприклад:

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

1

Найбільшою проблемою, яку пропускає більшість рішень тут, є те, що ширина елемента часто змінюється CSS залежно від того, де він розміщений у html.

Якби я мав визначити offsetWidth елемента, додавши клон його до тіла, коли він має стилі, які застосовуються лише в його поточній області, я отримав би неправильну ширину.

наприклад:

// css

.module-container .my-elem {межа: 60px суцільний чорний; }

тепер, коли я спробую визначити ширину мого елема в контексті тіла, він вийде на 120 пікселів. Ви могли б клонувати контейнер модулів замість цього, але ваш JS не повинен знати цих деталей.

Я цього не перевіряв, але, по суті, рішення Soul_Master є єдиним, яке може працювати належним чином. Але, на жаль, дивлячись на реалізацію, це, ймовірно, буде дорогим у використанні та поганим для роботи (як і більшість представлених тут рішень.)

Якщо це взагалі можливо, використовуйте видимість: приховано замість цього. Це все одно візуалізує елемент, що дозволяє обчислити ширину без суєти.


0

На додаток до відповіді, опублікованої Тімом Бенкссом , яка слідувала за вирішенням мого питання, мені довелося відредагувати одну просту річ.

У когось іншого може бути те саме питання; Я працюю зі спадною програмою Bootstrap у спадному меню. Але текст може бути ширшим як поточний вміст на даний момент (і не так багато хороших способів вирішити це за допомогою 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");
                }
            });
        }
    }

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.