2012-04-17 72 views
13

我有一个包含N列的Html/JavaScript应用程序,它需要足够大,包含所有列中所有可能的LI元素。JavaScript不调整UL元素的高度有时使用Jquery插入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的,“不要叫调整(),直到所有的LI的是完全加载和图像已呈现“?

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

任何帮助?谢谢

+0

在代码中究竟是在调用'resize()'函数?从我在这里可以看到的,你可以在'populateUnsetAnswers()'末尾调用它,'JavaScript'将调整大小。 – 2012-04-19 17:05:56

+0

@KemalFadillah是的,resize()在populateUnsetAnswers()之后被调用。这只有在你刷新页面时才能在Firefox中使用。在Chrome中它依然不起作用。 – 2012-04-19 17:27:00

+0

@FireEmblem您根本不需要设置高度,这些列将垂直扩展以适应内容。 – 2012-04-23 15:24:38

回答

1

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

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

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

+0

是的,我调试了当前的高度。在首页加载时,高度通常为0.在后续页面刷新时,高度是正确的。 – 2012-04-19 17:25:26

+1

那么当你试图读取它们的高度时图像没有被完全请求......因为服务器响应没有静态的时间来完成我怀疑你可以实现你想要的东西,除非你正在使用加载/错误事件的图片 – 2012-04-19 17:40:59

+0

@FireEmblem - 我在下面的回答中提供了代码,用于跟踪所有图像何时加载并在所有高度有效时调用'resize()'。 – jfriend00 2012-04-26 22:34:16

0

尝试使用

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

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

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

看到这个的jsfiddle明白我的意思:http://jsfiddle.net/Ralt/Vwg7P/

即使有是没有图像的widthheight属性将占据图像所需的空间。一旦DOM被加载。

2

下面是检查图像已加载的jQuery插件:对于你的情况https://github.com/alexanderdickson/waitForImages

样品用法是:

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

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

+0

这在尝试计算浏览器从服务器加载图像之前计算高度时肯定是个问题。 (在第二个请求中,图像被缓存。)解决方案是延迟计算,直到所有图像加载(或者在每个图像加载后甚至更新它)之后。 – 2012-04-25 17:31:41

-1

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

可尝试包裹的resize调用在

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

或给图像中的HTML标签imgwidth属性height

也许都

+0

这是行不通的,因为图像是通过javascript代码动态插入到页面中的。 – jfriend00 2012-04-26 22:36:02

+0

然后你应该加载图像尺寸,以及通过JS插入图像。 – HerrSerker 2012-04-27 06:58:24

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()); 
    
0

我想我会为你提供一个解决方案。

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

有相当多的CSS的出现,但主要的思路是这样的:

  1. 我模拟实际内容下3栏布局,与.inner和.column类。
  2. 将内容放置在(通过z-index 2> .inner zindex 1)上,其宽度与所在列的宽度相同。
  3. 将内容添加到内容区域后,主#container的高度会更新。
  4. 由于.inner是顶部,左侧,右侧,底部= 0,它会更新,并且由于.columns的高度为100%,因此它们会更新高度以匹配#containers高度。

观察。

如果您认为合适,您可以在.column类中添加填充,边框和边距。

No javascript is required。

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

另一个简单相等的高度CSS溶液:

LOGIC是非常简单 - 所有列/ LI的浮起 与.eH {填充底:X;边距:-X}和 包装/ UL是.EW {溢出:隐藏}

X =任意大PX的为安全因子量

实施例: http://jsfiddle.net/rahen/TXVYD/4/

0

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

下面我复制了我解决它的代码。请在您的resize()函数内调用autoheightwidth(i, 0, true)

基本的想法是,你不知道浏览器何时完成加载图像,所以不必依靠单一的高度调整,而是每次发生某些事情时调整高度(大部分只是图像已加载) 。

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