Подтвердить что ты не робот

JavaScript, не изменяющий высоту элемента UL, иногда при вставке элементов LI с использованием JQuery

У меня есть приложение Html/JavaScript, которое содержит N столбцов, которые должны быть достаточно большими, содержат все возможные элементы LI из всех столбцов.

Простое решение, кажется, подсчитывает высоты всех элементов в каждом столбце, компенсирует заполнение, а затем устанавливает высоту этой общей для каждого столбца.

Это отлично работает, когда элементы LI содержат простой текст. К сожалению, когда элементы LI содержат изображения, различные браузеры имеют проблемы. Например, когда я впервые загружаю страницу в FireFox, она выглядит как снимок экрана ниже, но при повторном обновлении он отлично работает. Он также не работает в Chrome.

Screenshot Showing the height of the UL element is not in sync with the LI elements

Мое приложение не предварительно заполняет элементы LI при загрузке страницы - оно использует JavaScript, как показано ниже:

function populateUnsetAnswers(unsetCategoryAnswers) {
    for (i in unsetCategoryAnswers) {
        if (unsetCategoryAnswers.hasOwnProperty(i.toString())) {
            $('#categoryQuestionArea #possibleAnswers').append(
                categoryAnswerLiTag(unsetCategoryAnswers[i])
            );
        }
    }
}

function categoryAnswerLiTag(unsetCategoryAnswer) {
    var html = '<li id="' + unsetCategoryAnswer.id + '">';

    if (unsetCategoryAnswer.image) {
        html += '<img class="categoryAnswerImage" title="';
        html += unsetCategoryAnswer.text;
        html += '" src="/trainingdividend/rest/streaming/';
        html += unsetCategoryAnswer.image.fileName;
        html += '" style="height: ';
        html += unsetCategoryAnswer.image.height;
        html += ';';
        html += '" />';
    } else {
        html += unsetCategoryAnswer.text
    }

    html += '</li>';

    return html;
}

Когда страница загружается, запрос ajax извлекает все объекты, которые должны быть помещены в элементы LI, а затем вызывает первую функцию выше.

После создания всех элементов LI я вызываю эту функцию сразу после нее:

function resize() {
    var currentHeight, totalHeight;
    totalHeight = 0;

    $("#categoryQuestionArea ul").children().each(function() {
        currentHeight = $(this).height();

        totalHeight += currentHeight + 13;
    });

    $("#categoryQuestionArea ul").height(totalHeight);
    $("#categoryQuestionArea div#separator").css("padding-top", (totalHeight / 2) + "px");
}

Есть ли способ сказать jQuery: "Не вызывайте resize(), пока все LI не будут полностью загружены и изображения будут отображаться"?

Я думаю, что происходит то, что при начальной загрузке страницы высота этих элементов LI равна 0 или маленькому значению, потому что она не содержит изображения, поэтому моя функция изменения размера вычисляет неправильный результат (я тестировал это с помощью некоторые предупреждения). Пока LI заполняются и загружаются изображения, общая высота вычисляется просто отлично.

Любая помощь? Благодаря

4b9b3361

Ответ 1

Чтобы буквально ответить на заданный вами вопрос, если вы хотите только позвонить resize(), когда все изображения закончили загрузку, вам необходимо установить обработчики onload для этих изображений, и когда вы записали, что последний из них теперь вы можете вызвать функцию resize(). Вы можете сделать это вот так (объяснение кода ниже):

var remainingAnswerImages = 0;

function categoryAnswerImageLoadHandler() {
    --remainingAnswerImages;
    if (remainingAnswerImages === 0) {
        resize();
    }
}

function populateUnsetAnswers(unsetCategoryAnswers) {
    // add one extra to the image count so we won't have any chance 
    // at getting to zero  before loading all the images
    ++remainingAnswerImages;
    var possibleAnswers$ = $('#categoryQuestionArea #possibleAnswers');
    for (i in unsetCategoryAnswers) {
        if (unsetCategoryAnswers.hasOwnProperty(i.toString())) {
            possibleAnswers$.append(categoryAnswerLiTag(unsetCategoryAnswers[i]));
        }
    }
    // remove the one extra
    --remainingAnswerImages;
    // if we hit zero on the count, then there either were no images 
    // or all of them loaded immediately from the cache
    // if the count isn't zero here, then the 
    // categoryAnswerImageLoadHandler() function will detect when it does hit zero
    if (remainingAnswerImages === 0) {
        resize();
    }
}

function categoryAnswerLiTag(unsetCategoryAnswer) {
    var obj = document.createElement("li");
    obj.id = unsetCategoryAnswer.id;

    if (unsetCategoryAnswer.image) {
        // count this image
        ++remainingAnswerImages;
        var img = new Image();
        img.onload = img.onerror = img.onabort = categoryAnswerImageLoadHandler;
        img.title = unsetCategoryAnswer.text;
        img.style.height = unsetCategoryAnswer.image.height;
        img.src = "/trainingdividend/rest/streaming/" + unsetCategoryAnswer.image.fileName;
        obj.appendChild(img);
    } else {
        obj.innerHTML = unsetCategoryAnswer.text;
    }
    return obj;
}

В качестве пояснения этот код вносит следующие изменения:

  • Добавьте переменную remainingAnswerImages, чтобы отслеживать, сколько изображений еще нужно загрузить.
  • Добавьте обработчик onload для каждого созданного тега <img>, чтобы мы могли отслеживать его загрузку.
  • Каждый раз, когда мы генерируем HTML для тега с обработчиком onload, increment remainingAnswerImages.
  • Когда вы закончите добавление всего HTML, проверьте счетчик remainingAnswerImages, чтобы узнать, равен ли он нулю (это было бы только в случае, если не было изображений или все изображения, загруженные сразу из кеша браузера). Если это так, немедленно вызовите resize().
  • В обработчике onload, который будет вызываться для каждого изображения, уменьшите remainingAnswerImages, и если счетчик достигнет нуля, вызовите resize().
  • При добавлении изображений добавьте один лишний элемент в remainingAnswerImages в качестве затвора, чтобы не доходить до нуля, пока мы не добавим изображения. Когда вы делаете добавление изображений, извлеките это еще раз.
  • Я также переписал функцию categoryAnswerLiTag(), чтобы просто создать объекты DOM напрямую, а не конкатенировать связку строк в HTML. В этом случае код становится более чистым для чтения и обслуживания.
  • Я также переместил $('#categoryQuestionArea #possibleAnswers') из вашего цикла for, так как он каждый раз решает одно и то же. Лучше сделать это один раз перед циклом. Кроме того, в большинстве случаев это можно упростить до $('#possibleAnswers'), так как идентификаторы должны быть уникальными на странице.

Ответ 2

Вот плагин jquery, который проверяет загруженные изображения: https://github.com/alexanderdickson/waitForImages

Пример использования вашего случая:

$('#categoryQuestionArea').waitForImages(function() {
   resize();
});

Я также просто проверил бы общую высоту <ul> вместо того, чтобы циклически перебирать элементы списка, так как вам придется вручную изменить script, если позже изменения полей, полей или границ элементов списка на.

Ответ 3

Если у вас возникли проблемы с изображениями на первой странице, возможно, это связано с тем, что они не кэшируются и, следовательно, не доступны сразу. Таким образом, измерение их высоты приведет к плохим результатам... вы отлаживали высоту, полученную с помощью jQuery (например,

currentHeight = $(this).height();
console.log(currentHeight);

Единственный способ сделать это - я думаю наблюдать за событиями загрузки всех изображений (и, вероятно, с ошибкой), и подсчитывать, завершен ли весь запрос

Ответ 4

попробуйте использовать

$('img').load(function(){
    //put code here
});

Ответ 5

Я предполагаю, что ваш HTML запутан. В частности, ваши теги <img>.

Добавьте атрибуты width и height к тэгам <img>. Все будет волшебным образом решено.

Смотрите этот jsfiddle, чтобы понять, что я имею в виду: http://jsfiddle.net/Ralt/Vwg7P/

Даже если там нет изображения, атрибуты width и height будут занимать пространство, необходимое для изображения. Как только загрузится DOM.

Ответ 6

Это скорее проблема CSS, скорее всего, из-за фиксированной высоты, с позициями или плавающей позицией.

Существует несколько способов исправить это.

  • Дайте min-height вместо фиксации высоты.

    #container { min-height: 100px; }
    
  • Очистите float и не устанавливайте высоту

    #container { overflow: hidden; }
    
  • Используйте Сценарии, чтобы добавить высоту, как только будет добавлен каждый элемент. Как и фрагмент jQuery ниже

    $("#container").append($("#theimg"));
    $("#container").height($("#container").height()+$("#theimg").height());
    

Ответ 7

Думаю, у меня может быть решение для вас.

Основная идея моего решения заключается в CSS. Вы хотите иметь 3 столбца одинаковой высоты, не так ли? У вас может быть что-то вроде этого: http://jsfiddle.net/agilius/NvzZp/46/

Там очень много CSS, но основная идея такова:

  • Я имитирую раскладку из трех столбцов под фактическим содержимым, с классами .inner и .column.
  • Содержимое помещается поверх (через z-index 2 > .inner zindex 1) с той же шириной, что и столбцы, расположенные ниже.
  • Когда содержимое добавляется в зоны содержимого, высота основного # контейнера обновляется.
  • Так как .inner - это верхний, левый, правый, нижний = 0, он обновляется, а поскольку .columns имеют высоту 100%, они обновляют их высоту, чтобы соответствовать высоте #containers.

Наблюдения.

Вы можете иметь отступы, границы, поля в классе .column по своему усмотрению.

Не требуется javascript.

Ответ 8

Еще одно простое решение для выравнивания по высоте:

ЛОГИКА очень проста - все столбцы /LI плавают с .eH {padding-bottom: X; margin-bottom: -X} и wrapper/UL .eW {overflow: hidden}

X = большое произвольное количество px для коэффициента безопасности

Пример: http://jsfiddle.net/rahen/TXVYD/4/

Ответ 9

Это звучит точно как одна из проблем, которые я испытывал при кодировании SudoSlider.

Ниже я скопировал код, с которым я его решил. Просто вызовите autoheightwidth(i, 0, true) внутри функции resize().

Основная идея заключается в том, что вы не знаете, когда браузер завершил загрузку изображений, поэтому вместо того, чтобы полагаться на одну настройку высоты, вы каждый раз настраиваете высоту (в основном, только то, что изображение загружено).

Он должен работать, если вы измените ссылки "obj" и "li" в первых двух методах.

Это не очень читаемо, но когда я закодировал его, был большой фокус на размере.

// Automaticly adjust the height and width, i love this function. 
// Before i had one function for adjusting height, and one for width.
function autoheightwidth(i, speed, axis) // Axis: true == height, false == width.
{
    obj.ready(function() {// Not using .load(), because that only triggers when something is loaded.
        adjustHeightWidth (i, speed, axis);
        // Then i run it again after the images has been loaded. (If any)
        // I know everything should be loaded, but just in case. 
        runOnImagesLoaded (li.eq(i), falsev, function(){
            adjustHeightWidth (i, speed, axis);
        });
    });
};
function adjustHeightWidth (i, speed, axis)
{
    var i = getRealPos(i); // I assume that the continuous clones, and the original element is the same height. So i allways adjust acording to the original element.
    var target = li.eq(i);
    // First i run it. In case there are no images to be loaded. 
    var b = target[axis ? "height" : "width"]();
    obj.animate(
        axis ? {height : b} : {width : b},
        {
            queue:falsev,
            duration:speed,
            easing:option[8]/*ease*/
        }
    );
}
function runOnImagesLoaded (target, allSlides, callback) // This function have to be rock stable, cause i use it ALL the time!
{
    var elems = target.add(target.find('img')).filter('img');
    var len = elems.length;
    if (!len)
    {
        callback();
        // No need to do anything else. 
        return this;
    }
    function loadFunction(that)
    {
        $(that).unbind('load').unbind('error');
        // Webkit/Chrome (not sure) fix. 
        if (that.naturalHeight && !that.clientHeight)
        {
            $(that).height(that.naturalHeight).width(that.naturalWidth);
        }
        if (allSlides)
        {
            len--;
            if (len == 0)
            {
                callback();
            }
        }
        else
        {
            callback();
        }
    }
    elems.each(function(){
        var that = this;
        $(that).load(function () {
            loadFunction(that);
        }).error(function () {
            loadFunction(that);
        });
        /*
         * Start ugly working IE fix. 
         */
        if (that.readyState == "complete") 
        {
            $(that).trigger("load");    
        }
        else if (that.readyState)
        {
            // Sometimes IE doesn't fire the readystatechange, even though the readystate has been changed to complete. AARRGHH!! I HATE IE, I HATE IT, I HATE IE!
            that.src = that.src; // Do not ask me why this works, ask the IE team!
        }
        /*
         * End ugly working IE fix. 
         */
        else if (that.complete)
        {
            $(that).trigger("load");
        }
        else if (that.complete === undefined)
        {
            var src = that.src;
            // webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f
            // data uri bypasses webkit log warning (thx doug jones)
            that.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="; // This is about the smallest image you can make. 
            that.src = src;
        }
    }); 
}   

Ответ 10

Я думаю, что браузер не знает размеры изображений, потому что они не загружены.

Попробуйте обернуть вызов resize в

jQuery(document).load( function funcName() {
   ...
} )

или предоставить атрибуты image width и height в тегах HTML img.

Возможно, оба