2012-04-17 4 views
13

Ich habe eine HTML/JavaScript-Anwendung, die N Spalten enthält, die groß genug sein müssen alle möglichen LI-Elemente aus allen Spalten enthalten.JavaScript Größe der UL-Element nicht ändern manchmal beim Einfügen von LI-Elementen mit Jquery

Die einfache Lösung scheint die Höhen aller Elemente in jeder Spalte zu zählen, das Auffüllen auszugleichen und dann die Höhe auf diese Summe für jede der Spalten festzulegen.

Das funktioniert gut, wenn die LI-Elemente Klartext enthalten. Wenn die LI-Elemente stattdessen Bilder enthalten, haben leider verschiedene Browser Probleme. Zum Beispiel, wenn ich die Seite zum ersten Mal in FireFox lade, sieht es aus wie der Screenshot unten, aber bei einer anderen Aktualisierung funktioniert es gut. Es funktioniert auch nicht wie erwartet in Chrome.

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

Meine Anwendung nicht die LI-Elemente vorab füllen, wenn die Seite geladen wird - es verwendet JavaScript, wie folgt:

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

Wenn die Seite geladen wurde, eine Ajax-Anforderung holt alle der Objekte, die in LI-Elemente eingefügt werden sollen, und ruft dann die erste oben genannte Funktion auf.

Nachdem alle der LI-Elemente erstellt werden, nenne ich diese Funktion direkt nach dem es:

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

Gibt es eine Möglichkeit jQuery zu sagen, „Nenn() nicht vergrößert werden, bis alle der LI sind voll geladen und die Bilder haben gerendert "?

Ich denke, was passiert ist, dass auf der ersten Seite laden, die Höhe dieser LI-Elemente ist 0 oder ein kleiner Wert, weil es das Bild nicht enthält, so berechnet meine Größe Funktion das falsche Ergebnis (ich testete dies mit einigen Warnmeldungen). Solange die LIs gefüllt sind und die Bilder geladen sind, wird die Gesamthöhe gut berechnet.

Irgendwelche Hilfe? Danke

+0

Wo genau in Ihrem Code rufen Sie die 'resize()' Funktion auf? Von dem was ich hier sehen kann, kannst du es einfach am Ende von 'populateUnsetAnswers()' nennen und 'JavaScript' wird die Größenänderung genau richtig machen. –

+0

@KemalFadillah Ja, resize() wird direkt nach populateUnsetAnswers() aufgerufen. Dies funktioniert nur in Firefox, wenn Sie eine Seite aktualisieren. In Chrome funktioniert es immer noch nicht. –

+0

@FireEmblem Sie müssen keine Höhe einstellen, die Spalten werden vertikal erweitert, um dem Inhalt zu entsprechen. –

Antwort

1

Wenn Sie Probleme mit Bildern auf der ersten Seite haben, ist es vielleicht aufgrund der Tatsache, dass sie nicht zwischengespeichert und daher nicht sofort verfügbar sind. So ihre Höhenmessung wird zu schlechten Ergebnissen führen ... haben Sie die Höhe debuggen, die über jQuery (zB

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

Der einzige Weg, das zu tun geholt wurde, ist denke ich die Last Veranstaltungen aller Bilder zu beobachten (und wahrscheinlich der Fehler als auch) und zählen, ob alle Anfrage abgeschlossen wurden

+0

Ja, ich habe die aktuelle Höhe debuggen. Beim Laden der ersten Seite ist die Höhe häufig 0. Bei nachfolgenden Seitenaktualisierungen ist die Höhe korrekt. –

+1

dann ist es die Tatsache, dass die Bilder nicht vollständig angefordert werden, wenn Sie versuchen, ihre Höhe zu lesen ... da Serverantworten keine statische Zeit zu beenden haben Ich bezweifle, dass Sie erreichen können, was Sie wollen, es sei denn, Sie verwenden die Lade-/Fehlerereignisse der Bilder –

+0

@FireEmblem - Ich habe Code in meiner Antwort unten angeboten, die verfolgt, wenn alle Bilder geladen sind und ruft 'resize()' auf, wenn alle Höhen gültig sind. – jfriend00

0

versuchen Sie es mit

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

ich denke, Ihre HTML ist vermasselt. Insbesondere Ihre <img> Tags.

Fügen Sie die Attribute width und height zu Ihren <img> Tags hinzu. Alles wird magisch gelöst sein.

Sehen Sie diese jsfiddle zu verstehen, was ich meine: http://jsfiddle.net/Ralt/Vwg7P/

Auch wenn es in kein Bild vorhanden ist, die width und height Attribute den Raum für das Bild benötigt wird besetzen. Sobald das DOM geladen ist.

2

Hier ist ein jQuery-Plugin, das die Bilder geladen haben prüft: https://github.com/alexanderdickson/waitForImages

Verwendungsbeispiel für Ihren Fall wäre:

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

Ich würde auch für die Gesamthöhe des <ul> überprüfen gerade statt Durchlaufen der Listenelemente, da Sie das Skript manuell ändern müssten, wenn sich der Abstand, die Ränder oder die Ränder der Listenelemente später ändern.

+0

Dies ist definitiv ein Problem beim Versuch, die Höhe zu berechnen, bevor der Browser die Bilder vom Server lädt. (Bei der zweiten Anforderung werden die Bilder zwischengespeichert.) Die Lösung besteht darin, die Berechnung zu verzögern, bis alle Bilder geladen sind (oder sogar aktualisieren, nachdem jedes Bild geladen wurde). –

-1

Ich denke, dass der Browser die Abmessungen der Bilder nicht kennt, weil sie nicht geladen sind.

Entweder versuchen, den Aufruf von resize in einem

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

oder geben dem Bild width und height Attribute in der HTML-Tags img zu wickeln.

Vielleicht beide

+0

Dies funktioniert nicht, da die Bilder dynamisch über Javascript in die Seite eingefügt werden. – jfriend00

+0

Dann sollten Sie auch die Bildmaße laden, wenn Sie die Bilder über JS einfügen. – HerrSerker

0

Dies ist eher ein CSS Problem, wahrscheinlich aufgrund einer festen Höhe, mit Gegenständen entweder schwammen oder absolut positioniert.

Es gibt mehrere Möglichkeiten, dies zu beheben.

  1. Geben Sie eine min-height anstelle einer Höhe.

    #container { min-height: 100px; } 
    
  2. Klar die float und setzen keine Höhen

    #container { overflow: hidden; } 
    
  3. Verwenden Scripts auf die Höhe zu addieren, einmal wird jedes Element hinzugefügt. Wie das jQuery-Snippet unten

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

Ich glaube, ich könnte eine Lösung für Sie.

Die Grundidee meiner Lösung liegt in CSS. Sie möchten 3 Spalten gleicher Höhe haben, oder? Sie können so etwas wie diese: http://jsfiddle.net/agilius/NvzZp/46/

Es gibt eine ganze Reihe von CSS da, aber die Grundidee ist folgende:

  1. ich ein 3-Spalten-Layout unter dem eigentlichen Inhalt zu simulieren, mit der .inner und .Spaltenklassen.
  2. Der Inhalt wird über (z-index 2> .inner zindex 1) mit der gleichen Breite wie die darunter liegenden Spalten platziert.
  3. Wenn Inhalt zu den Inhaltsbereichen hinzugefügt wird, wird die Höhe des Hauptcontainers aktualisiert.
  4. Da .inner oben, links, rechts, unten = 0 ist, wird es aktualisiert, und da die .columns 100% Höhe haben, aktualisieren sie ihre Höhe so, dass sie der #containers-Höhe entspricht.

Beobachtungen.

Sie können Padding, Rahmen und Ränder in der .column-Klasse haben, wie Sie es für richtig halten.

Kein Javascript ist erforderlich.

3

buchstäblich Um die Frage zu beantworten, die Sie gefragt, ob Sie nur wollen resize() rufen, wenn alle Bilder fertig geladen haben, dann müssen Sie onload Handler für diese Bilder installieren und wenn Sie, dass die letzte aufgenommen haben jetzt geladen wird, Sie können die resize() Funktion aufrufen. Könnte man so wie diese (unter Code Erklärung) tun:

  • hinzufügen verfolgen, wie viele Bilder noch eine Variable remainingAnswerImages halten müssen:

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

    Als Erklärung, dieser Code die folgenden Änderungen vornimmt geladen sein.

  • Fügen Sie einen Onload-Handler für jedes erstellte -Tag hinzu, damit wir verfolgen können, wann es geladen ist.
  • Jedes Mal, wenn wir den HTML-Code für ein Tag mit dem Onload-Handler generieren, erhöhen Sie remainingAnswerImages.
  • Wenn Sie fertig sind mit dem Hinzufügen des gesamten HTML, überprüfen Sie die remainingAnswerImages Zählung, um zu sehen, ob es Null ist (dies wäre nur der Fall, wenn keine Bilder oder alle Bilder sofort aus dem Browser-Cache geladen wurden). Wenn ja, rufe resize() sofort auf.
  • Im Onload-Handler, der für jedes Bild aufgerufen wird, dekrementieren Sie remainingAnswerImages und wenn der Zählerstand Null erreicht hat, rufen Sie resize().
  • Fügen Sie während des Hinzufügens von Bildern ein zusätzliches Bild zu remainingAnswerImages als Gate hinzu, um zu verhindern, dass eine Zählung von null erreicht wird, bis die Bilder hinzugefügt sind. Wenn Sie Bilder hinzugefügt haben, nehmen Sie das eine Extra heraus.
  • Ich schrieb auch die categoryAnswerLiTag() Funktion um nur die DOM-Objekte direkt zu erstellen, anstatt eine Reihe von Zeichenfolgen zusammen in HTML concat. In diesem Fall ist der Code viel sauberer zu lesen und zu pflegen.
  • Ich habe auch die $('#categoryQuestionArea #possibleAnswers') aus Ihrer for Schleife verschoben, da es jedes Mal zur gleichen Sache verrechnet wird. Besser, es einmal vor der Schleife zu tun. In den meisten Fällen könnte dies auch auf $('#possibleAnswers') vereinfacht werden, da ids auf der Seite eindeutig sein sollen.
0

Eine weitere einfache gleiche Höhe CSS Lösung:

LOGIC ist sehr einfach - alle Spalten/LI mit .eh {padding-bottom geschwommen werden: X; margin-bottom: -X} und Wrapper/UL ist .EW {overflow: hidden}

X = große beliebige Menge an Pixel für den Faktor Sicherheit

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

0

Das klingt genau wie eines der Probleme, die ich beim Codieren von SudoSlider hatte.

Unten habe ich den Code kopiert ich löste es mit. Rufen Sie einfach autoheightwidth(i, 0, true) innerhalb Ihrer resize() - Funktion.

Die Grundidee ist, dass Sie nicht wissen, wann der Browser das Laden der Bilder abgeschlossen hat. Anstatt sich auf eine einzige Höhenanpassung zu verlassen, passen Sie die Höhe jedes Mal an, wenn etwas passiert (meistens nur ein Bild geladen wurde) .

Es sollte funktionieren, wenn Sie die Referenzen von "obj" und "li" in den ersten beiden Methoden ändern.

Es ist nicht sehr lesbar, aber es gab einen großen Fokus auf die Größe, wenn ich es codiert habe.

// 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; 
     } 
    }); 
} 
Verwandte Themen