【问题标题】:JavaScript not resizing height of UL element sometimes when inserting LI elements using Jquery使用 Jquery 插入 LI 元素时,JavaScript 有时不会调整 UL 元素的高度
【发布时间】:2012-04-28 09:38:18
【问题描述】:

我有一个 Html/JavaScript 应用程序,其中包含 N 列,这些列需要足够大,包含所有列中所有可能的 LI 元素。

简单的解决方案似乎是计算每列中所有项目的高度,补偿填充,然后将高度设置为每列的总高度。

当 LI 元素包含纯文本时,这很有效。不幸的是,当 LI 元素包含图像时,各种浏览器都会出现问题。例如,当我第一次在 FireFox 中加载页面时,它看起来像下面的屏幕截图,但在再次刷新时,它工作正常。它在 Chrome 中也无法正常工作。

我的应用程序在页面加载时没有预先填充 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,“在所有的 LI 完全加载并且图像已经渲染之前,不要调用 resize()”?

我认为发生的情况是在初始页面加载时,这些 LI 元素的高度为 0 或一个较小的值,因为它不包含图像,所以我的 resize 函数正在计算错误的结果(我用一些警告声明)。只要填充了 LI 并且加载了图像,就可以很好地计算总高度。

有什么帮助吗?谢谢

【问题讨论】:

  • 您在代码中的哪个位置调用了resize() 函数?从我在这里看到的情况来看,你可以在populateUnsetAnswers() 的末尾调用它,JavaScript 会正确调整大小。
  • @KemalFadillah 是的,resize() 已经在 populateUnsetAnswers() 之后调用了。这仅在您进行页面刷新时在 Firefox 中有效。它在 Chrome 中仍然无法正常工作。
  • @FireEmblem 你根本不需要设置高度,列会垂直扩展以适应内容。
  • 你在我们可以现场看到的地方做一个演示吗?像 jsfiddle
  • 请您编辑您的问题,添加完整代码(HTML、CSS、JavaScript)或演示(可能在 [jsfiddle.net/](jsFiddle))。如果没有完整的问题,很难找到解决方案。看到已经给出的大多数答案实际上都是猜测!

标签: javascript html ajax height


【解决方案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 以跟踪还需要加载多少图像。
  • 为每个创建的 &lt;img&gt; 标签添加一个 onload 处理程序,以便我们跟踪它的加载时间。
  • 每次我们使用 onload 处理程序为标签生成 HTML 时,增加 remainingAnswerImages
  • 添加完所有 HTML 后,检查 remainingAnswerImages 计数是否为零(仅当没有图像或所有图像立即从浏览器缓存加载时才会出现这种情况)。如果是,请立即调用 resize()。
  • 在将为每个图像调用的 onload 处理程序中,递减 remainingAnswerImages,如果计数达到零,则调用 resize()
  • 在添加图像时,在remainingAnswerImages 中添加一个额外的值,以防止计数为零,直到我们完成添加图像。添加完图片后,再取出一张。
  • 我还重写了categoryAnswerLiTag() 函数来直接创建DOM 对象,而不是将一堆字符串连接到HTML 中。在这种情况下,代码更易于阅读和维护。
  • 我还将$('#categoryQuestionArea #possibleAnswers') 移出您的for 循环,因为它每次都解析为相同的内容。最好在循环之前执行一次。此外,在大多数情况下,这可以简化为 $('#possibleAnswers'),因为 id 在页面中应该是唯一的。

【讨论】:

    【解决方案2】:

    这听起来就像我在编写 SudoSlider 时遇到的问题之一。

    下面我复制了我解决它的代码。只需在 resize() 函数中调用 autoheightwidth(i, 0, true) 即可。

    基本思想是您不知道浏览器何时完成加载图像,因此您无需依赖单一的高度调整,而是在每次发生某些事情时调整高度(通常只是加载图像)。

    如果您在前 2 个方法中更改“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;
            }
        }); 
    }   
    

    【讨论】:

      【解决方案3】:

      另一个简单的等高 CSS 解决方案:

      LOGIC 很简单—— 所有列/LI 都是浮动的 with .eH{ padding-bottom: X;边距底部:-X } 和 包装器/UL 是 .eW{overflow: hidden}

      X= 安全系数的任意大的像素数

      示例: http://jsfiddle.net/rahen/TXVYD/4/

      【讨论】:

        【解决方案4】:

        我想我可能会为您提供解决方案。

        我的解决方案的主要思想在于 CSS。你想有 3 列相同的高度,对吧?你可以有这样的东西:http://jsfiddle.net/agilius/NvzZp/46/

        那里有相当多的 CSS,但主要思想是这样的:

        1. 我在实际内容下模拟了一个 3 列布局,包含 .inner 和 .column 类。
        2. 内容置于上方(通过 z-index 2 > .inner zindex 1),其宽度与下方的列相同。
        3. 将内容添加到内容区域时,主#container 的高度会更新。
        4. 由于 .inner 是 top,left,right,bottom = 0,因此它会更新,并且由于 .columns 具有 100% 的高度,它们会更新其高度以匹配 #containers 高度。

        观察。

        您可以在 .column 类中设置您认为合适的内边距、边框、边距。

        不需要javascript。

        【讨论】:

          【解决方案5】:

          这是一个 CSS 问题,很可能是由于固定高度,项目要么浮动要么绝对定位。

          有很多方法可以解决这个问题。

          1. 提供min-height 而不是固定高度。

            #container { min-height: 100px; }
            
          2. 清除float,不要设置任何高度

            #container { overflow: hidden; }
            
          3. 在添加每个元素后,使用脚本添加高度。像下面的jQuery sn-p

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

          【讨论】:

            【解决方案6】:

            我认为浏览器不知道图像的尺寸,因为它们没有加载。

            要么尝试将resize 的调用包装在一个

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

            或者在 HTML 的img 标签中赋予图像widthheight 属性。

            也许两者都有

            【讨论】:

            • 这不起作用,因为图像是通过 javascript 代码动态插入到页面中的。
            • 那么在通过 JS 插入图片时,你也应该加载图片尺寸。
            【解决方案7】:

            这是一个检查图像是否已加载的 jquery 插件:https://github.com/alexanderdickson/waitForImages

            您的案例的示例用法是:

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

            我也只会检查&lt;ul&gt; 的总高度,而不是循环遍历列表项,因为如果列表项上的填充、边距或边框稍后发生更改,您将不得不手动更改脚本。

            【讨论】:

            • 这绝对是在浏览器从服务器加载图像之前尝试计算高度的问题。 (在第二个请求中,图像被缓存。)解决方案是延迟计算,直到所有图像都加载完毕(甚至在每个图像加载后更新它)。
            【解决方案8】:

            我猜你的 HTML 搞砸了。特别是您的&lt;img&gt; 标签。

            widthheight 属性添加到您的&lt;img&gt; 标记中。一切都会神奇地解决。

            请参阅此 jsfiddle 以了解我的意思:http://jsfiddle.net/Ralt/Vwg7P/

            即使其中没​​有图像,widthheight 属性也会占用图像所需的空间。一旦加载了 DOM。

            【讨论】:

              【解决方案9】:

              尝试使用

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

              【讨论】:

                【解决方案10】:

                如果您在第一页加载时确实遇到图像问题,可能是因为它们没有被缓存,因此无法立即使用。所以测量它们的高度会导致不好的结果......你是否调试过通过 jQuery 获取的高度(例如

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

                我认为唯一的方法是观察所有图像的加载事件(可能还有错误)并计算所有请求是否已完成

                【讨论】:

                • 是的,我确实调试了当前高度。在第一次加载页面时,高度通常为 0。在后续页面刷新时,高度是正确的。
                • 那么事实是,当您尝试读取它们的高度时,图像并没有被完全请求......因为服务器响应没有静态时间来完成我怀疑你可以实现你想要的,除非你正在使用图像的加载/错误事件
                • @FireEmblem - 我在下面的答案中提供了代码,用于跟踪所有图像的加载时间,并在所有高度都有效时调用resize()
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2015-09-25
                • 2022-12-05
                • 1970-01-01
                • 2011-10-16
                • 1970-01-01
                • 2012-08-12
                • 1970-01-01
                相关资源
                最近更新 更多