2012-04-17 7 views
13

Tengo una aplicación Html/JavaScript que contiene N columnas que deben ser lo suficientemente grandes para contener todos los elementos LI posibles de todas las columnas.JavaScript no cambia el tamaño de la altura del elemento UL a veces al insertar elementos LI usando Jquery

La solución simple parece contar las alturas de todos los elementos en cada columna, compensar el relleno y luego establecer la altura en ese total para cada una de las columnas.

Esto funciona muy bien cuando los elementos LI contienen texto sin formato. Desafortunadamente, cuando los elementos LI contienen imágenes, varios navegadores tienen problemas. Por ejemplo, cuando primero carga la página en FireFox, se parece a la siguiente captura de pantalla, pero con otra actualización, funciona bien. Tampoco funciona como se esperaba en Chrome.

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

Mi aplicación no rellenar previamente los elementos LI cuando se carga la página - que utiliza JavaScript, de la siguiente manera:

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; 
} 

Cuando la página se realiza la carga, una petición AJAX Obtiene todas las de los objetos que se colocarán en elementos LI, y luego llama a la primera función de arriba.

Después de que todos los elementos LI son creados, que llamo esta función justo después de que:

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"); 
} 

¿Hay alguna manera de saber jQuery: "No llamo cambiar el tamaño() hasta que todo el LI'S están completamente cargado y las imágenes han renderizado "?

Creo que lo que sucede es que en la carga de la página inicial, la altura de estos elementos LI es 0 o un valor pequeño porque no contiene la imagen, por lo que mi función de ajuste de tamaño está calculando el resultado incorrecto (Probé esto con algunas declaraciones de alerta). Mientras que los LI se llenen y las imágenes se hayan cargado, la altura total se calcula muy bien.

¿Algún ayuda? Gracias

+0

¿En qué parte de su código llama exactamente a la función 'resize()'? Por lo que puedo ver aquí, puede simplemente llamarlo al final de 'populateUnsetAnswers()' y 'JavaScript' hará el cambio de tamaño a la perfección. –

+0

@KemalFadillah Sí, resize() se llama justo después de poblarUnsetAnswers() ya. Esto funciona en Firefox solo cuando realizas una actualización de página. Todavía no funciona en absoluto en Chrome. –

+0

@FireEmblem no necesita establecer una altura en absoluto, las columnas se expandirán verticalmente para ajustarse al contenido. –

Respuesta

1

Si tiene problemas con las imágenes en la primera página, la carga puede deberse a que no están en la memoria caché y, por lo tanto, no están disponibles de inmediato. Por lo que medir su altura dará lugar a malos resultados ... qué se depura la altura que se fue a buscar a través de jQuery (por ejemplo

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

La única manera de hacerlo es que creo que para observar los eventos de carga de todas las imágenes (y probablemente el error también) y el recuento de si todas las solicitudes han sido terminado

+0

Sí, depuré la altura actual. En la carga de la primera página, la altura suele ser 0. En las actualizaciones de página subsiguientes, la altura es correcta. –

+1

, entonces es el hecho de que las imágenes no se solicitan completamente cuando intenta leer su altura ... ya que las respuestas del servidor no tienen tiempo estático para terminar. Dudo que pueda lograr lo que quiere a menos que esté utilizando los eventos de carga/error de las imágenes –

+0

@FireEmblem - He ofrecido código en mi respuesta a continuación que realiza un seguimiento de cuándo se cargan todas las imágenes y las llamadas 'resize()' cuando todas las alturas son válidas. – jfriend00

0

intentar usar

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

supongo que su HTML se arrugó. En particular, sus <img> etiquetas.

Agregue los atributos width y height a sus etiquetas <img>. Todo será mágicamente resuelto.

ver este jsFiddle a entender lo que quiero decir: http://jsfiddle.net/Ralt/Vwg7P/

A pesar de que no hay ninguna imagen en ese país, los width y height atributos ocupará el espacio necesario para la imagen. Tan pronto como se carga el DOM.

2

Aquí es un plugin jQuery que controle las imágenes han cargado: el uso https://github.com/alexanderdickson/waitForImages

la muestra para su caso sería:

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

que habría también acaba de comprobar la altura total de la <ul> en lugar de recorriendo los elementos de la lista, ya que tendría que cambiar manualmente la secuencia de comandos si el relleno, los márgenes o los bordes de los elementos de la lista cambian más adelante.

+0

Esto es definitivamente un problema al tratar de calcular la altura antes de que el navegador cargue las imágenes del servidor. (En la segunda solicitud, las imágenes se almacenan en caché.) La solución es retrasar el cálculo hasta que se hayan cargado todas las imágenes (o incluso actualizarlas después de cargar cada imagen). –

-1

Creo que el navegador no conoce las dimensiones de las imágenes, porque no están cargadas.

o bien tratar de envolver la invocación de resize en un

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

o dar la imagen width y height atributos en el código HTML img etiquetas.

Tal tanto

+0

Esto no funcionará porque las imágenes se insertan dinámicamente en la página a través del código javascript. – jfriend00

+0

Luego, también debe cargar las dimensiones de la imagen al insertar las imágenes a través de JS. – HerrSerker

0

Esto es más bien un problema de CSS, muy probablemente debido una altura fija, ya sea con los objetos que flotaban o con posición absoluta.

Existen varias maneras de solucionar esto.

  1. Dar un min-height en lugar de fijar una altura.

    #container { min-height: 100px; } 
    
  2. Borrar el float y no establecen alturas

    #container { overflow: hidden; } 
    
  3. utilizar scripts para suman a la altura, una vez que se añade cada elemento. Al igual que el siguiente fragmento de código jQuery

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

Creo que podría tener una solución para usted.

La idea principal de mi solución está en CSS. Quieres tener 3 columnas de la misma altura, ¿verdad? Puede tener algo como esto: http://jsfiddle.net/agilius/NvzZp/46/

Hay un buen montón de CSS, pero la idea principal es la siguiente:

  1. i simular un diseño de 3 columnas bajo el contenido real, con el .inner y clases de columna
  2. El contenido se coloca sobre (a través de z-index 2> .inner zindex 1), con el mismo ancho que las columnas que están debajo.
  3. Cuando se agrega contenido a las zonas de contenido, se actualiza la altura del #container principal.
  4. Dado que .inner es superior, izquierda, derecha, inferior = 0, se actualiza, y como las columnas .columnas tienen una altura del 100%, actualizan su altura para que coincida con la altura #contenedores.

Observaciones.

Puede tener relleno, bordes, márgenes en la clase .column como mejor le parezca.

No es necesario Javascript.

3

Para responder literalmente a la pregunta que ha hecho, si solo desea llamar al resize() cuando todas las imágenes hayan terminado de cargarse, deberá instalar los controladores onload para esas imágenes y cuando haya registrado que la última ya está cargada, puede llamar a la función resize(). Se podría hacer eso como esto (explicación código de abajo):

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; 
} 

A modo de explicación, este código realiza los siguientes cambios:

  • agregar una variable remainingAnswerImages hacer un seguimiento de la cantidad de imágenes aún necesita para ser cargado
  • Agregue un controlador de carga para cada etiqueta <img> que se crea para que podamos realizar un seguimiento de cuándo se carga.
  • Cada vez que generamos el código HTML para una etiqueta con el controlador de carga, incremente remainingAnswerImages.
  • Cuando termine de agregar todo el código HTML, verifique el recuento de remainingAnswerImages para ver si es cero (esto solo sería el caso si no hubiera imágenes o si todas las imágenes se cargaran inmediatamente desde el caché del navegador). Si es así, llame a resize() inmediatamente.
  • En el controlador de carga que se llamará para cada imagen, disminuya remainingAnswerImages y si el recuento ha llegado a cero, llame al resize().
  • Al agregar imágenes, agregue una extra a remainingAnswerImages como una puerta para evitar llegar a un conteo cero hasta que terminemos de agregar imágenes. Cuando termines de agregar imágenes, saca esa extra.
  • También reescribí la función categoryAnswerLiTag() para simplemente crear los objetos DOM directamente en lugar de combinar un conjunto de cadenas en HTML. En este caso, el código es mucho más limpio para leer y mantener.
  • También moví el $('#categoryQuestionArea #possibleAnswers') fuera de su bucle for ya que siempre resuelve lo mismo. Mejor hacerlo una vez antes del ciclo. Además, en la mayoría de los casos, esto podría simplificarse a $('#possibleAnswers') ya que se supone que los identificadores son únicos en la página.
0

Otra solución simple CSS igual altura:

LÓGICA es muy simple - todas las columnas/LI están flotando con .EH {padding-bottom: X; margin-bottom: -X} y envoltura/UL es .EW {overflow: hidden}

X = gran cantidad arbitraria de px para el factor de seguridad

Ejemplo: http://jsfiddle.net/rahen/TXVYD/4/

0

Esto suena exactamente como uno de los problemas que tuve al codificar SudoSlider.

A continuación he copiado el código con el que lo resolví. Simplemente llame al autoheightwidth(i, 0, true) dentro de su función resize().

La idea básica es que no sabes cuando el navegador ha completado la carga de imágenes, así que en vez de depender de un solo ajuste de altura, ajustas la altura cada vez que sucede algo (principalmente solo que una imagen ha sido cargada) .

Debería funcionar si cambia las referencias de "obj" y "li" en los primeros 2 métodos.

No es muy legible, pero hubo un gran enfoque en el tamaño cuando lo codifiqué.

// 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; 
     } 
    }); 
} 
Cuestiones relacionadas