13

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

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

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

屏幕截图显示 UL 元素的高度与 LI 元素不同步

我的应用程序在页面加载时没有预先填充 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 并且加载了图像,就可以很好地计算总高度。

有什么帮助吗?谢谢

4

10 回答 10

3

从字面上回答您提出的问题,如果您只想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>以便我们可以跟踪它的加载时间。
  • 每次我们使用 onload 处理程序为标签生成 HTML 时,递增remainingAnswerImages
  • 添加完所有 HTML 后,检查remainingAnswerImages计数是否为零(仅当没有图像或所有图像立即从浏览器缓存加载时才会出现这种情况)。如果是这样,请立即调用 resize()。
  • 在将为每个图像调用的 onload 处理程序中,递减remainingAnswerImages,如果计数达到零,则调用resize().
  • 添加图像时,添加一个额外的remainingAnswerImages作为门,以防止计数为零,直到我们完成添加图像。完成添加图像后,将额外的图像取出。
  • 我还重写了categoryAnswerLiTag()函数以直接创建 DOM 对象,而不是将一堆字符串连接到 HTML 中。在这种情况下,代码更易于阅读和维护。
  • 我也将你的循环$('#categoryQuestionArea #possibleAnswers')移出了你的for循环,因为它每次都解决相同的问题。最好在循环之前执行一次。此外,在大多数情况下,这可以简化为,$('#possibleAnswers')因为 id 在页面中应该是唯一的。
于 2012-04-25T20:28:18.373 回答
2

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

您的案例的示例用法是:

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

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

于 2012-04-21T12:30:42.797 回答
1

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

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

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

于 2012-04-19T17:09:24.853 回答
0

我猜你的 HTML 搞砸了。特别是您的<img>标签。

widthheight属性添加到您的<img>标签。一切都会神奇地解决。

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

即使那里没有图像,widthandheight属性也会占用图像所需的空间。一旦加载了 DOM。

于 2012-04-19T18:14:36.370 回答
0

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

LOGIC非常简单——所有的列/LI 都用.eH{ padding-bottom: X;浮动。margin-bottom: -X }和 wrapper/UL 是.eW{overflow: hidden}

X = 安全系数的任意数量的 px

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

于 2012-04-25T21:39:29.517 回答
0

尝试使用

$('img').load(function(){
    //put code here
});
于 2012-04-19T17:48:19.723 回答
0

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

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

  1. 给一个min-height而不是固定一个高度。

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

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

    $("#container").append($("#theimg"));
    $("#container").height($("#container").height()+$("#theimg").height());
    
于 2012-04-24T10:18:01.120 回答
0

这听起来就像我在编写 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;
        }
    }); 
}   
于 2012-04-26T14:13:33.137 回答
0

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

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

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

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

观察。

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

不需要javascript。

于 2012-04-24T11:56:38.917 回答
-1

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

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

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

或在 HTML标签中给出图像widthheight属性。img

也许两者兼而有之

于 2012-04-23T17:15:12.450 回答