Giter Club home page Giter Club logo

columnizer-jquery-plugin's Introduction

Support

Has Columnizer saved you hours? Become a Github Sponsor and buy me a coffee ☕️ 😄

Documentation

CSS Classes for Created Columns

Columnizer will add CSS classes to the columns it creates. Each column will have a "column" classname. The first column will have "first" and last column will have "last". This lets you target specific columns in your CSS markup more easily.

Options

. Default is false.
Option Name Purpose
width a rough width your columns, and Columnizer will create as many as will fit in the browser window
height This option can only be used in conjunction with the width option. When both the width and height options are set, columns will continue to be built to those measurements to fill all of the content. This is useful for scrolling columns horizontally. See sample 5 for an example.
columns an alternative to the width option. Sets a static number of columns to build, regardless of widget.
target An optional CSS selector may be used here to determine where the columnized content should be placed. If a target is specified, the columnized node will remain unchanged, and the target node will contain columnized content.
doneFunc This function will be called when columnizing is complete.
ignoreImageLoading
true by default. If set to false, Columnizer will try to wait until images in the content have loaded before columnizing the data.
columnFloat default is “left”. Change to “right” for right to left languages.
lastNeverTallest
false by default. Set to true to ensure that the last column of the columnized content is not the tallest column.
buildOnce if buildOnce is false, the content will be re-columnized when the window is resized. If buildOnce is true, the content will only be columnized once.
overflow If this option is used, then a static height is set to the columnized content, and any content that does not fit within the height is put into the element specified by $(id). See demo 1 for an example.
height (required): the static height for the columnized content
id (required): The id of the element to put the remainder of the content
doneFunc (optional): a function to be called after the content has been columnized. This is a great place to columnize the remainder content.
manualBreaks Defaults to false. Set to true if you only want to create columns with manual column breaks. If true, then width, height, columns options are ignored.
disableSingle Disables single column layout if number of columns is less or equal to 1. Useful to force columns scrolling horizontally on small screens. See demo 5 for an example.

CSS Classes

Class Name Purpose
columnbreak Any node that has the CSS class “columnbreak” will act as a column break, as you'd expect in any word processor. The "columnbreak" node will always be the last node in its column. Works well with the optional "columnBreak" option.
dontsplit Any node that has the CSS class “dontsplit” won’t be split into multiple columns. This is handy to make sure that tables, etc, aren’t chopped in half if they land at the bottom of a column.
dontend Any node that has the CSS class “dontend” will never be put at the end of a column.
removeiffirst Any node that has the CSS class “removeiffirst” will be removed from the content if it is the first node in a column.
removeiflast Any node that has the CSS class “removeiflast” will be removed from the content if it is the last node in a column.

Helpful Hints

  1. You can specify a rough width your columns, and Columnizer will create as many as will fit in the browser window. Just use: $(‘selector’).columnize({width: 400 }) syntax
  2. You can specify a specific number of columns, and Columnizer will distribute your content between that many columns. Just use: $(‘selector’).columnize({columns: 2 }) syntax
  3. When using the width and height options to scroll horizontally, make sure that the .column CSS class does not specify any padding or margin or border. See CSS for sample 5 for an example on how to create buffer between columns.
  4. Make sure that you are columnizing visible content. If your content is display:none it may not columnize correctly. Try visibility:hidden and display:block instead.
  5. Columnizer does not auto-class any of your content. See the Suggested Defaults For Your Content section.

Suggested Defaults For Your Content

Columnizer does not add default "dontsplit" or "dontend" classes to your content. If you are finding your content is breaking columns at awkward locations, try the following:

 $yourContent.find('table, thead, tbody, tfoot, colgroup, caption, label, legend, script, style, textarea, button, object, embed, tr, th, td, li, h1, h2, h3, h4, h5, h6, form').addClass('dontsplit');
 $yourContent.find('h1, h2, h3, h4, h5, h6').addClass('dontend');
 $yourContent.find('br').addClass('removeiflast').addClass('removeiffirst');

Uncolumnize

You can revert your columnized DOM by using the "uncolumnize" function.

$('selector').uncolumnize();

Troubleshooting

Why isn't my content columnizing?

Make sure that your content "has display". If the content you're columnizing has display:none, then the browser has difficulty estimating the size of some nodes, which makes columnizing impossible. instead of display:none, use visibility:hidden and keep display:block. then in the doneFunc() of columnizer, change the visibility and display to whatever your page or application needs.

Additional Notes

Minimize

To compress into a zip file, run compress.sh

Bug report?

Check the issues on the GitHub page

columnizer-jquery-plugin's People

Contributors

adamwulf avatar borkweb avatar boryane avatar danbrianwhite avatar faizshukri avatar jmarceli avatar josephdburdick avatar kirara avatar mklickman avatar msamsonoff avatar nathanziarek-ds avatar roboterhund avatar scm6079 avatar sfgeorge avatar slashlife avatar tomalterman avatar vldmit avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

columnizer-jquery-plugin's Issues

Column widths

I've just upgraded to the revision that fixed the lastNeverTallest / endless loop issue. However, this one now sets e.g. two-column layouts to have 50% column widths. Previously they were 48%, which allowed some margin to separate the columns. Is there a good way to pad out the columns?

Manual breaks (suggestion)

I wanted manual breaks in there, by adding <br class="force-break" /> around.

This code will do that. I've coded it so that if you use manual breaks, you have to then use them to define all your columns break points. This adds predictability and simplicity, avoiding lots of potential difficulties that would arise combining automated and manual breaks (e.g. what if you want an extra long column).

This is useful to allow you to consistently apply columnising on your site, but then in special cases guide the columnizer precisely, without having to go back to the point of creating your column markup manually.

Patch would be as follows..

@@ -39,11 +39,15 @@
        // (int) the minimum number of characters to jump when splitting
        // text nodes. smaller numbers will result in higher accuracy
        // column widths, but will take slightly longer
-       accuracy : false
+       accuracy : false,
+       // if we need a 'force-break' class set
+       explicitBreaks : $(this).find('.force-break').length!=0,
    };
    var options = $.extend(defaults, options);

-   $(this).find('h1, h2, h3, h4, h5, h6').addClass('dontend');
+   if (!options.explicitBreaks) {
+       $(this).find('h1, h2, h3, h4, h5, h6').addClass('dontend');
+   }
    $(this).find('table, thead, tbody, tfoot, colgroup, caption, label, legend, script, style, textarea, button, object, embed, tr, th, td, li, h1, h2, h3, h4, h5, h6, form').addClass('dontsplit');
    $(this).find('br').addClass('removeiflast').addClass('removeiffirst');

@@ -103,14 +107,31 @@
        /**
         * Create a node that has a height
         * less than or equal to height.
-        * Returns a boolean on whether we did some splitting successfully at a text point (so we know we don't need to split a real element).
+        * Returns a boolean on whether we did some splitting successfully at an optimal point (so we know we don't need to continue to split elements recursively).
         *
         * @param putInHere, a jQuery element
         * @$pullOutHere, a dom element
         */
-       function columnize($putInHere, $pullOutHere, $parentColumn, height){
-           while($parentColumn.height() < height && $pullOutHere[0].childNodes.length){
-               $putInHere.append($pullOutHere[0].childNodes[0]); // Because we're not cloning, jquery will actually move the element
+       function columnize($putInHere, $pullOutHere, $parentColumn, height) {
+           if (options.explicitBreaks) { // We're doing explicit breaks, meaning we only ever break on a force-break node
+               var splitHere = false;
+               while($pullOutHere[0].childNodes.length) {
+                   var next = $($pullOutHere[0].childNodes[0]);
+                   splitHere = next.hasClass('force-break');
+                   if (splitHere) break;
+                   var splitUnder = next.find('force-break').length != 0;
+                   if (splitUnder) break;
+                   $putInHere.append(next); // Because we're not cloning, jquery will actually move the element
+               }
+               if (splitHere) {
+                   $($pullOutHere[0].childNodes[0]).remove();
+                   return true; // No further splits needs
+               }
+               return false; // Will need to go down to split recursively
+           } else{
+               while($parentColumn.height() < height && $pullOutHere[0].childNodes.length) {
+                   $putInHere.append($pullOutHere[0].childNodes[0]); // Because we're not cloning, jquery will actually move the element
+               }
            }
            if($putInHere[0].childNodes.length == 0) return false;

@@ -190,27 +210,29 @@
-                   if($clone.is("img") && $parentColumn.height() < height + 20){ // Images are easy to handle, just shift them
+                   if((options.explicitBreaks) && ($clone.hasClass('force-break'))) { // Explicit break
+                       $cloneMe.remove();
+                   } else if((!options.explicitBreaks) && ($clone.is("img") && $parentColumn.height() < height + 20)) { // Images are easy to handle, just shift them
                        $cloneMe.remove();
-                   }else if(!dontsplit && $parentColumn.height() < height + 20){ // If this is a viable split point, do it
+                   }else if((!options.explicitBreaks) && (!dontsplit && $parentColumn.height() < height + 20)) { // If this is a viable split point, do it
                        $cloneMe.remove(); // Remove from from
-                   }else if($clone.is("img") || dontsplit){ // Can't split, we'll just have to let it all stay where it is
+                   }else if((!options.explicitBreaks) && ($clone.is("img") || dontsplit)) { // Can't split, we'll just have to let it all stay where it is
                        $clone.remove(); // Remove from to (i.e. undo copy). Stays at from.
@@ -228,9 +230,13 @@
                    }else{ // Look deeper for split point
                        $clone.empty();
                        if(!columnize($clone, $cloneMe, $parentColumn, height)) {
-                           if($cloneMe.children().length) {
+                           if($cloneMe.children().length != 0 ) {
                                split($clone, $cloneMe, $parentColumn, height);
                            }
+                       } else {
+                           // Case where explicit break might be at end of list item, we need to not copy over shell of this list item
+                           if ((options.explicitBreaks) && ($cloneMe[0].nodeName.toLowerCase()=='li') && ($cloneMe[0].childNodes.length == 1) && ($cloneMe[0].childNodes[0].nodeType == 3) && ($cloneMe[0].childNodes[0].nodeValue.replace(/\s*/g,'') == ''))
+                               $cloneMe.first().remove();
                        }
                        if($clone.get(0).childNodes.length == 0) {
                            // it was split, but nothing is in it :(. No deeper to go.
@@ -311,7 +317,7 @@
                    var needsDeepSplit = !columnize($col, $destroyable, $col, targetHeight);
                    if (needsDeepSplit) {
                        // do a split, but only if the last item in the column isn't a "dontend"
-                       if(!$destroyable.contents().find(":first-child").hasClass("dontend")) {
+                       if(options.explicitBreaks || !$destroyable.contents().find(":first-child").hasClass("dontend")) {
                            split($col/*put in here*/, $destroyable/*pull out here*/, $col, targetHeight);
                        }else{
    //                      alert("not splitting a dontend");
@@ -278,43 +299,46 @@
-                   // Any "dontend" stuff needs to be yonked off our current column and put back onto the start of $destroyable. Loop whilst we need to keep doing this.
-                   while(checkDontEndColumnOnConstraints($col.contents(":last").length != 0 && $col.contents(":last").get(0))){
-                       var $lastKid = $col.contents(":last");
-                       $lastKid.remove();
-                       $destroyable.prepend($lastKid);
+
+                   if (!options.explicitBreaks) {
+                       // Any "dontend" stuff needs to be yonked off our current column and put back onto the start of $destroyable. Loop whilst we need to keep doing this.
+                       while(checkDontEndColumnOnConstraints($col.contents(":last").length != 0 && $col.contents(":last").get(0))) {
+                           var $lastKid = $col.contents(":last");
+                           $lastKid.remove();
+                           $destroyable.prepend($lastKid);
+                       }

That patch isn't going to apply. I ran it off from my own (hopefully temporary) fork of columnizer, but I manually chopped it about a bit. But the changes here work if applied manually.

"dontend" implementation wrong

In

                    while(checkDontEndColumn($col.children(":last").length && $col.children(":last").get(0))){
                        var $lastKid = $col.children(":last");
                        $lastKid.remove();
                        $destroyable.prepend($lastKid);
                    }

'children' needs to be 'contents'. Otherwise it skips past text nodes and rearranges the order.

Removeiffirst/removeiflast implementation wrong

It assumes you only have 1 of them. But if you have a sequence, it won't work.

I have adapted the code to remove as many as there are.

            while ($inBox.children(".column").children(":first-child.removeiffirst").length!=0)
                $inBox.children(".column").children(":first-child.removeiffirst").remove();
            while ($inBox.children(".column").children(":last-child.removeiflast").length!=0)
                $inBox.children(".column").children(":last-child.removeiflast").remove();

DHTMLsuite.windowwidget

I have a page that loaded content dynamically into a windowwidget.... but the content does not column in the window... columnizes if you call the page straight from the browser though.

any ideas?

columnise by nytimes

Thanks a lot for your plugin.

I have to do a multicolum with carousel and swipe event like this one http://nytimes.com/chrome (but more simple)
Do you think i'm on a good way to try with your plugin ?

Excuse me for my horrible english ;-)

infinite loop from lastnevertallest

http://welcome.totheinter.net/columnizer-jquery-plugin/#comment-14432

I you select “lastNeverTallest” and you only have one item with the class “dontsplit” f.ex. one li element, you run into an infinite loop an the browser crashes because of this line of code:

if(options.lastNeverTallest && lastIsMax){
// the last column is the tallest
// so allow columns to be taller
// and retry
targetHeight = targetHeight + 30;
if(loopCount == maxLoops-1) maxLoops++;
}

maxLoops will grow to infinite.. And the single item will be in the last column.

Doesn't work properly with jQuery 1.6

Using jQuery 1.6 unequal columns will sometimes be created.

To fix this change line 155 from
if($clone.attr("nodeType") == 1 && !$clone.hasClass("dontend")){
to
if($clone[0].nodeType == 1 && !$clone.hasClass("dontend")){

IE crashes when using "dontsplit"

I'm using the latest version 1.5.0 with JQuery 1.6.2 trying to set fixed width and height columns scrolling horizontally. It works fine in Firefox and Chrome but crashes in IE when I try to set "dontspilt" elements.

Here is a test URL: http://199.83.241.18/test/

Any help is appreciated. Thanks - Gabriela

disappearing content

I have a simple test list:

<ul class="wide">
<li>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse urna urna, interdum non adipiscing eu, consectetur a augue.</li>
<li>Morbi vehicula nunc in quam placerat at ornare orci suscipit. Sed mauris eros, viverra non sodales quis, fermentum non purus.</li>
<li>Nullam pulvinar fermentum laoreet. Nullam lobortis velit nec mauris eleifend faucibus.</li>
<li>Maecenas aliquam velit vel lorem porttitor a rhoncus dolor vulputate. </li>
<li>Suspendisse potenti.</li>
<li>Curabitur at turpis sed nunc auctor feugiat sed nec mauris.</li>
<li>Sed ut nunc erat, non tincidunt nisi.</li>
<li>Pellentesque placerat magna non lectus porta suscipit.</li>
</ul>
</div>

The script is simple.

<script type="text/javascript">
$(function() {
$('.wide').columnize({width:290});
});
</script>

But what I get is:

<ul class="wide"><div style="width: 33%; float: left;" class="first column">
<li>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse urna urna, interdum non adipiscing eu, consectetur a augue.</li>
</div><div style="width: 33%; float: left;" class="column"></div><div style="width: 33%; float: left;" class="last column"></div></ul>

Any thoughts? I'm using jquery 1.4.2 (no choice)

float is a reserved property

You need to change option.float to something else like option.columnfloat as float is a reserved property in javascript. As a result, the YUI and Google Closure compilersand most js code checkers will throw an error—even if the code seems to work just fine.

Missing text

Hey Adam,

Hello again. The thing is like a told you. In some screen resolutions some parts of the text just disappear. The text is there in the base code, but when it's columnized, in some screen sizes part of it is missing.

OK, that's it!... if you need more info, please let me know!

Cheers from Argentina,

Ernesto.

Note in README about visibility

You should note in README, that for the plugin to work, the element must be visible.

It took me quite some time to figure out why my tabbed content wasn't columnized right.

"dontsplit" and "dontend" create infinite loop

Adam,

I understand that the latest committed version (1.5.0) may not be production ready, but attempting to use the 'dontsplit' or 'dontend' classes create the infinite loop bug.

Due to the requirement of compatibility for jQuery 1.6.2, I require to use v1.5.0.

Usability on text splitting (suggestion)

If some text is split mid-paragraph/mid-sentence, it is a nice little touch to add an "mdash" entity at the split point, to make it clear to the reader what has happened.

Columns aren't usual for websites, so pulling out this traditional typographical trick works well to hint to the user what is going on, so they don't need to think.

                var partsUsed = 0;
                while($parentColumn.height() < height && oText.length != 0){
                    ...
                    partsUsed++;
                    ...
                }

                if($parentColumn.height() >= height && latestTextNode != null){
                    ...
                    partsUsed--;
                }
                if(oText.length){
                    ...
                    if (partsUsed != 0)
                    {
                        latestTextNode = document.createTextNode(' \u2014');
                        $putInHere.append(latestTextNode);
                    }

Duplication issues across columns

If only one string of 23 characters or more (e.g. a URL) is present within a columnized <div< and nothing else, it duplicates the content. See http://jsfiddle.net/hFw6q/1/ for an example.

If a sentence of 76 characters is present within a columnized

, it will duplicate the last X characters (X varies according to the number of spaces in the sentence). See http://jsfiddle.net/93PLq/ for an example.

floating columns container cause it to disappear

When using set width and height eg:

$('.make-columns').columnize({
width : 230,
height : 390
});

If you set a float property for .make-columns it will have a 0px by 0px size. Even if you set it's size through CSS you will not be able to float elements after it.

Fails on Javascript strict mode

                $cloneMe = $pullOutHere.children(":first");
                $clone = $cloneMe.clone(true);

Undeclared variables, just shove "var" in front.

attr error

In line 169 you have

if($clone.prop("nodeType") == 1 && !$clone.hasClass("dontend")){ 

and you need to have

if($clone.attr("nodeType") == 1 && !$clone.hasClass("dontend")){ 

Newspaper justification!

Columnizer is amazing!
It would look even more amazing with newspaper justified columns.

Tried with CSS of ...
text-align: justify;
text-justify: newspaper;
... but this just breaks columnizer.

Any workarounds, or plans to implement this?

Code is poorly commented

Algorithmically this code is very complex, but nothing really explains the algorithm. It took me a few hours to work it all out to fix the nasty bugs I had.

The API header comment for 'columnize' is completely wrong. It refers to jQuery and DOM nodes, but it's the other way around. It does not return as it says. Other parameters are not mentioned there. The description isn't helpful either.

The first while loop in 'columnize' really confused me. It relies on a non-documented jQuery quirk that an 'append' will move a node. Probably some jQuery programmers know this automatically, but it is not in the jQuery docs for the append method, and it's quite a magical thing to do given it is happening via a DOM reference not a jQuery reference.
So I'd add a comment such as:
"Because we're not cloning, jquery will actually move the element"

Then later on the maxLoops stuff is very confusing. I added this comment:
"We loop as we try and workout a good height to use. We know it initially as an average but if the last column is higher than the first ones (which can happen, depending on split points) we need to raise 'adjustment'. We try this over a few iterations until we're 'solid'."

I actually made various cleanups. My full changes (including bug fixes, and simplification for my purposes are as follows)...

// Based on http://welcome.totheinter.net/columnizer-jquery-plugin/
//  But with fixes and better flexibility, and pure CSS-based activation

addEventListenerAbstract(window,'load',function () {
    $('.column_wrapper').columnize({ columns: 3 });
    $('.column_wrapper_2').columnize({ columns: 2 });
} );

// version 1.5.0
// http://welcome.totheinter.net/columnizer-jquery-plugin/
// created by: Adam Wulf @adamwulf, [email protected]

(function($){

 $.fn.columnize = function(options) {


    var defaults = {
        // optional # of columns instead of width
        columns : false,
        // true to build columns once regardless of window resize
        // false to rebuild when content box changes bounds
        buildOnce : false,
        // an object with options if the text should overflow
        // it's container if it can't fit within a specified height
        overflow : false,
        // this function is called after content is columnized
        doneFunc : function(){},
        // if the content should be columnized into a 
        // container node other than it's own node
        target : false,
        // re-columnizing when images reload might make things
        // run slow. so flip this to true if it's causing delays
        ignoreImageLoading : true,
        // should columns float left or right
        columnFloat : "left",
        // ensure the last column is never the tallest column
        lastNeverTallest : false,
        // (int) the minimum number of characters to jump when splitting
        // text nodes. smaller numbers will result in higher accuracy
        // column widths, but will take slightly longer
        accuracy : false
    };
    var options = $.extend(defaults, options);

    $(this).find('h1, h2, h3, h4, h5, h6').addClass('dontend');

    return this.each(function() {
        var $inBox = options.target ? $(options.target) : $(this);
        var maxHeight = $(this).height();
        var $cache = $('<div></div>'); // this is where we'll put the real content
        var lastWidth = 0;
        var columnizing = false;

        var adjustment = 0;

        $cache.append($(this).contents().clone(true));

        // images loading after dom load
        // can screw up the column heights,
        // so recolumnize after images load
        if(!options.ignoreImageLoading && !options.target){
            if(!$inBox.data("imageLoaded")){
                $inBox.data("imageLoaded", true);
                if($(this).find("img").length > 0){
                    // only bother if there are
                    // actually images...
                    var func = function($inBox,$cache){ return function(){
                        if(!$inBox.data("firstImageLoaded")){
                            $inBox.data("firstImageLoaded", "true");
                            $inBox.empty().append($cache.children().clone(true));
                            $inBox.columnize(options);
                        }
                    }}($(this), $cache);
                    $(this).find("img").one("load", func);
                    $(this).find("img").one("abort", func);
                    return;
                }
            }
        }

        $inBox.empty();

        columnizeIt();

        if(!options.buildOnce){
            $(window).resize(function() {
                if(!options.buildOnce && $.browser.msie){
                    if($inBox.data("timeout")){
                        clearTimeout($inBox.data("timeout"));
                    }
                    $inBox.data("timeout", setTimeout(columnizeIt, 200));
                }else if(!options.buildOnce){
                    columnizeIt();
                }else{
                    // don't rebuild
                }
            });
        }

        /**
         * Create a node that has a height
         * less than or equal to height.
         * Returns a boolean on whether we did some splitting successfully at a text point (so we know we don't need to split a real element).
         *
         * @param putInHere, a jQuery element
         * @$pullOutHere, a dom element
         */
        function columnize($putInHere, $pullOutHere, $parentColumn, height){
            while($parentColumn.height() < height && $pullOutHere[0].childNodes.length){
                $putInHere.append($pullOutHere[0].childNodes[0]); // Because we're not cloning, jquery will actually move the element
            }
            if($putInHere[0].childNodes.length == 0) return false;

            // now we're too tall, undo the last one
            var kids = $putInHere[0].childNodes;
            var lastKid = kids[kids.length-1];
            $putInHere[0].removeChild(lastKid);
            var $item = $(lastKid);

            // and now try and put a split version of it
            if($item[0].nodeType == 3){
                // it's a text node, split it up
                var oText = $item[0].nodeValue;

                var counter2 = $putInHere.width() / 18;
                if(options.accuracy)
                counter2 = options.accuracy;
                var columnText;
                var latestTextNode = null;
                while($parentColumn.height() < height && oText.length){
                    var pos = oText.indexOf(' ', counter2);
                    if (pos == 0) pos = oText.substring(1).indexOf(' ', counter2)+1;
                    if (pos != -1) {
                        columnText = oText.substring(0, pos);
                    } else {
                        columnText = oText;
                    }
                    latestTextNode = document.createTextNode(columnText);
                    $putInHere.append(latestTextNode);

                    if((oText.length > counter2) && (pos)) {
                        oText = oText.substring(pos);
                    }else{
                        oText = "";
                    }
                }
                if($parentColumn.height() >= height && latestTextNode != null){
                    // too tall :(
                    $putInHere[0].removeChild(latestTextNode);
                    oText = latestTextNode.nodeValue + oText;
                }
                if(oText.length){
                    $item[0].nodeValue = oText;
                }else{
                    return false; // we ate the whole text node, move on to the next node
                }
            }

            // Put what is left back
            if($pullOutHere.children().length){
                $pullOutHere.prepend($item);
            }else{
                $pullOutHere.append($item);
            }

            return $item[0].nodeType == 3;
        }

        // Split up an element, which is more complex than splitting text. We need to create two copies of the element with it's contents divided between each
        function split($putInHere, $pullOutHere, $parentColumn, height){
            if($pullOutHere.children().length){
                var $cloneMe = $pullOutHere.children(":first"); // From
                var $clone = $cloneMe.clone(true); // To
                if($clone.prop("nodeType") == 1 && !$clone.hasClass("dontend")){ 
                    $putInHere.append($clone);
                    var dontsplit=$cloneMe.hasClass("dontsplit") || $clone.is("thead") || $clone.is("table") || $clone.is("tbody") || $clone.is("tr") || $clone.is("th") || $clone.is("td") || $clone.is("li");
                    if($clone.is("img") && $parentColumn.height() < height + 20){ // Images are easy to handle, just shift them
                        $cloneMe.remove();
                    }else if(!dontsplit && $parentColumn.height() < height + 20){ // If this is a viable split point, do it
                        $cloneMe.remove(); // Remove from from
                    }else if($clone.is("img") || dontsplit){ // Can't split, we'll just have to let it all stay where it is
                        $clone.remove(); // Remove from to (i.e. undo copy). Stays at from.
                    }else{ // Look deeper for split point
                        $clone.empty();
                        if(!columnize($clone, $cloneMe, $parentColumn, height)){
                            if($cloneMe.children().length){
                                split($clone, $cloneMe, $parentColumn, height);
                            }
                        }
                        if($clone.get(0).childNodes.length == 0){
                            // it was split, but nothing is in it :(. No deeper to go.
                            $clone.remove();
                        }
                    }
                }
            }
        }

        function checkDontEndColumnOnConstraints(dom){
            if(dom.nodeType != 1) return false;
            if($(dom).hasClass("dontend")) return true;
            if(dom.childNodes.length == 0) return false;
            return checkDontEndColumnOnConstraints(dom.childNodes[dom.childNodes.length-1]);
        }

        function columnizeIt() {
            if(lastWidth == $inBox.width()) return;
            lastWidth = $inBox.width();

            var numCols = options.columns;

            if($inBox.data("columnizing")) return;
            $inBox.data("columnized", true);
            $inBox.data("columnizing", true);

            $inBox.empty();
            $inBox.append($("<div style='float: " + options.columnFloat + ";'></div>")); //"
            $col = $inBox.children(":last");
            $col.append($cache.clone());
            maxHeight = $col.height();
            $inBox.empty();

            var targetHeight = maxHeight / numCols;
            var firstTime = true;
            var maxLoops = 3;
            var scrollHorizontally = false;
            if(options.overflow){
                maxLoops = 1;
                targetHeight = options.overflow.height;
            }else if(options.height){
                maxLoops = 1;
                targetHeight = options.height;
                scrollHorizontally = true;
            }

            for(var loopCount=0;loopCount<maxLoops;loopCount++){
                if (typeof window.console!='undefined') console.log('STARTING COLUMNISATION ITERATION');

                $inBox.empty();
                var $destroyable; // This is where we'll pull all our data from, as we progressively fill our columns
                try{
                    $destroyable = $cache.clone(true);
                }catch(e){
                    // jquery in ie6 can't clone with true
                    $destroyable = $cache.clone();
                }
                $destroyable.css("visibility", "hidden");
                // create the columns
                for (var i = 0; i < numCols; i++) {
                    /* create column */
                    var className = (i == 0) ? "first column" : "column";
                    var className = (i == numCols - 1) ? ("last " + className) : className;
                    $inBox.append($("<div class='" + className + "' style='float: " + options.columnFloat + ";'></div>")); //"
                }

                // fill all but the last column (unless overflowing)
                var i = 0;
                while(i < numCols - (options.overflow ? 0 : 1) || scrollHorizontally && $destroyable.contents().length){
                    if($inBox.children().length <= i){
                        // we ran out of columns, make another
                        $inBox.append($("<div class='" + className + "' style='float: " + options.columnFloat + ";'></div>")); //"
                    }
                    var $col = $inBox.children().eq(i);
                    if (!columnize($col, $destroyable, $col, targetHeight))
                    {
                        // do a split, but only if the last item in the column isn't a "dontend"
                        if(!$destroyable.contents().find(":first-child").hasClass("dontend")){
                            split($col/*put in here*/, $destroyable/*pull out here*/, $col, targetHeight);
                        }else{
    //                      alert("not splitting a dontend");
                        }
                    }

                    while(checkDontEndColumnOnConstraints($col.children(":last").length && $col.children(":last").get(0))){
                        var $lastKid = $col.children(":last");
                        $lastKid.remove();
                        $destroyable.prepend($lastKid);
                    }
                    i++;
                }
                if(options.overflow && !scrollHorizontally){
                    var IE6 = false /*@cc_on || @_jscript_version < 5.7 @*/;
                    var IE7 = (document.all) && (navigator.appVersion.indexOf("MSIE 7.") != -1);
                    if(IE6 || IE7){
                        var html = "";
                        var div = document.createElement('DIV');
                        while($destroyable[0].childNodes.length > 0){
                            var kid = $destroyable[0].childNodes[0];
                            for(var i=0;i<kid.attributes.length;i++){
                                if(kid.attributes[i].nodeName.indexOf("jQuery") == 0){
                                    kid.removeAttribute(kid.attributes[i].nodeName);
                                }
                            }
                            div.innerHTML = "";
                            div.appendChild($destroyable[0].childNodes[0]);
                            html += div.innerHTML;
                        }
                        var overflow = $(options.overflow.id)[0];
                        overflow.innerHTML = html;
                    }else{
                        $(options.overflow.id).empty().append($destroyable.contents().clone(true));
                    }
                }else if(!scrollHorizontally){
                    // it's scrolling horizontally, try and workout our average height. We know it initially but if the last column is too high we need to raise 'adjustment'. We try this over a few iterations until we're 'solid'.

                    // the last column in the series
                    $col = $inBox.children().eq($inBox.children().length-1);
                    while($destroyable.contents().length) $col.append($destroyable.contents(":first"));
                    var afterH = $col.height();
                    var diff = afterH - targetHeight;
                    var totalH = 0;
                    var min = 10000000;
                    var max = 0;
                    var lastIsMax = false;
                    $inBox.children().each(function($inBox){ return function($item){
                        var h = $inBox.children().eq($item).height();
                        lastIsMax = false;
                        totalH += h;
                        if(h > max) {
                            max = h;
                            lastIsMax = true;
                        }
                        if(h < min) min = h;
                    }}($inBox));

                    var avgH = totalH / numCols;
                    if(options.lastNeverTallest && lastIsMax){
                        // the last column is the tallest
                        // so allow columns to be taller
                        // and retry
                        adjustment += 30;
                        if(adjustment < 100){
                            targetHeight = targetHeight + 30;
                            if(loopCount == maxLoops-1) maxLoops++;
                        }else{
                            debugger;
                            loopCount = maxLoops;
                        }
                    }else if(max - min > 30){
                        // too much variation, try again
                        targetHeight = avgH + 30;
                    }else if(Math.abs(avgH-targetHeight) > 20){
                        // too much variation, try again
                        targetHeight = avgH;
                    }else {
                        // solid, we're done
                        loopCount = maxLoops;
                    }
                }else{
                    // it's scrolling horizontally, fix the classes of the columns
                    $inBox.children().each(function(i){
                        $col = $inBox.children().eq(i);
                        if(i==0){
                            $col.addClass("first");
                        }else if(i==$inBox.children().length-1){
                            $col.addClass("last");
                        }else{
                            $col.removeClass("first");
                            $col.removeClass("last");
                        }
                    });
                }
                $inBox.append($("<br style='clear:both;'>"));
            }
            for (var i=0;i<10;i++)
                $inBox.find('.column>br:first-child').remove();
            $inBox.find('.column>:first-child.removeiffirst').remove();
            $inBox.find('.column>:last-child.removeiflast').remove();
            $inBox.data("columnizing", false);

            if(options.overflow){
                options.overflow.doneFunc();
            }
            options.doneFunc();
        }
    });
 };
})(jQuery);

debugger ?!

you left 'debugger' in the code, even in the minified version...

lastNeverTallest option causes endless loop, crashes page

This code:

if(options.lastNeverTallest && lastIsMax){
    // the last column is the tallest
    // so allow columns to be taller
    // and retry
    targetHeight = targetHeight + 30;
    if(loopCount == maxLoops-1) maxLoops++;
}

easily causes the surrounding loop to continue forever as it always puts the one element into the last column, so lastIsMax is always true, so the loop continues forever. changing the targetHeight does not change anything. This crashes Firefox and crashes the tab in Chrome.

Adding endless last columns -> infinite loop

I use columnizer with a specific hight and width. The content changes from time to time (on reload page). In some situations the js never leave the while loop in line 309 (columnizer.js) because $destroyable.contents().length is always 1. This is beacuse the split doesn't work (I think) and the script tries to split a non splitable element to fit height/width params or something else.

Infinite Loop on resize in FF

Plugin has been used for a wordpress structured site with a horizontal column layout, having different problems in different browsers:

FF - content is laid out correctly but on window resize an infinite number of 'last' columns are generated
CHROME & SAFARI - content is not laid out correctly until browser window is resized

link: http://officemoore.com/project/house-kalafatas-challita/
as the site is still being built the directory is currently password protected

user: tester
pass: have_a_look_at_this!

any help would be greatly appreciated

Column inline style

Documentation says that "You can style your columns however you wish." but at the same time plugin adds inline styles to columns. This forces to use !important in .first.column{ } style. I suggest to add options for not adding inline styles to columns.

Image Split Improperly

When I add a image that has a size of the total width of the container, text is placed on top of the image starting on the second column.

Is there some way where I can control how my image is placed, so that it is not split and spanned across all three columns?

I tried don't split.

Columns not displaying properly until element inspected

This sounds like an odd issue, but I've been poking around for a while and haven't found the culprit.

I used the advanced example as a starting point to create "panels" that would slide in and out based on user interactions. Each panel includes two columns and are positioned horizontally. When I slide out the first panel to show the second, the second panel isn't columnized correctly.

No errors are thrown and I can see that all the panels contain the appropriate information with the correct number of columns. It appears that the second column is actually an empty div but the second I click on the column to inspect the DOM in Chrome, the columnizing happens.

Same problem in Safari, no problem at all in Firefox.

Any ideas?

Javascript, and iframes

Columnizer borks adsense really badly. Actually it crashes IE7 completely, and crashes a tab process on IE8.

After a few hours of debugging this and trying various patches, I have not been able to resolve it, and I don't think it can be resolved. So I think it needs documenting instead.

Adsense uses an iframe, which is outputted via Javascript, filled via Javascript, and itself contains Javascript. That just borks for various reasons on different browsers.

I tried patching Columnizer to support a new "protectme" class which would isolate a DOM node into a buffer, remove it from the document tree, and put it back after Columnizer fixes it. Therefore it would not be clone'd (Columnizer clones stuff a lot and it's kind of inevitable for performance). But all browsers bork even on that. Chrome does a reload of the iframe, as does Firefox I think. That causes an ad reload, which Google are not going to like. I think IE just loses the contents completely (as it was written via direct writing, not an iframe src, and IE isn't preserving that) and then it dies because JS events are firing off dead content.

"protectme" is a useful patch though for other scenarios, so I am going to keep it in my "jquery fork" (I've given a URL to this in another issue).

I tried deferring adsense loading, but there's no way to do that. You can't do an after-the-fact document.write, and adsense won't let you run it via innerHTML (probably because it's expected DOM load methods aren't getting fired).

You can't also try and make adsense reload itself fully after Columnizer runs, it just won't do that either.

There are various posts out there talking about moving adsense around using DOM methods. But if they worked once, they don't work now. Or maybe they only work pre-DOM-ready.

So to recap, I implemented a "protectme", which is useful. But generally you can't use adsense with Columnizer and that needs documenting.

column breaks too early sometimes

I'm reproducing that behaviour in Firefox and Internet Explorer 7/8.

The following steps walk you through recreating the issue

Open sample1 in Firefox
Resize the browser to smaller width, until the last line from the first column starts with "reprimique, vix no erat soluta suavitate. At mel" and you will see that the last line is cut of to early

Double or leading spaces in text split locations

It'll get in an infinite loop if it's splitting at the top level (i.e. in columnize) with some text node that includes double spaces. This is because it assumes each space is a delimiter of text blocks of length greater than 1.

The while loop in columnize needs changing to:

                while($parentColumn.height() < height && oText.length){
                    var pos = oText.indexOf(' ', counter2);
                    if (pos == 0) pos = oText.substring(1).indexOf(' ', counter2)+1;
                    if (pos != -1) {
                        columnText = oText.substring(0, pos);
                    } else {
                        columnText = oText;
                    }
                    latestTextNode = document.createTextNode(columnText);
                    $putInHere.append(latestTextNode);

                    if((oText.length > counter2) && (pos)) {
                        oText = oText.substring(pos);
                    }else{
                        oText = "";
                    }
                }

columnize and split don't interact

If some top level text node is split via columnize, split is still subsequently called, and the first node from the next column is moved through.

The result: The node after the text is pulled in half way through the text.

So instead of calling columnize then split, you need to check the return value of columnize. This is what my code ended up as...

                    if (!columnize($col, $destroyable, $col, targetHeight))
                    {
                        // do a split, but only if the last item in the column isn't a "dontend"
                        if(!$destroyable.contents().find(":first-child").hasClass("dontend")){
                            split($col/*put in here*/, $destroyable/*pull out here*/, $col, targetHeight);
                        }else{
    //                      alert("not splitting a dontend");
                        }
                    }

Also make sure you're returning consistently from columnize, the return behaviour for this function was a real mess. I haven't checked if changes were needed, but I did clean up my copy to make sure it always returned a boolean and never an implied null.

Crazy empty li's

Great tool but I'm having a problem. I am columnizing a list of item (ul/li) and while they distribute evenly and properly, there is this rogue empty li tag at the bottom of the first two of three columns and I don't know why. They don't exist in my original list...they are just empty items, visible only because of my styling. Any ideas?

Specifying height causes infinite loop

line 309 to 329 loops indefinitely if you specify a height, rather than adding elements to the columns it continues to keep adding more and more last columns.

this breaks the horizontal scrolling capability.

using:
width: 400
height: 600
lastNeverTallest: true

Empty <LI> Nodes being created.

I am columnizing a UL list into two columns and each column ends up with an empty

  • at the bottom of the list. Normally this is benign however I am adding styles to my LI elements which betray the existence of these phantom nodes. No content is being lost, these nodes are just being created. I'm using the latest build, version 1.4.0

    So far it's occurring only when lists have 2 or 4 items. 3, 5, and 6 item lists don't trigger it.

  • line 43 should use .contents() instead of .children()

    $(this).children().clone(true) should be $(this).contents().clone(true)

    it's currently not properly cloning text nodes

    asdf

    $("#foobar").columnize() will currently just empty out the div instead of columnize it.

    Workaround is to:

    asdf

    Pass in height defining element

    Could it be possible to pass in an element that specifies the height for horizontal scrolling so that rebuilds repoll the elements new height?

    Recommend Projects

    • React photo React

      A declarative, efficient, and flexible JavaScript library for building user interfaces.

    • Vue.js photo Vue.js

      🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

    • Typescript photo Typescript

      TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

    • TensorFlow photo TensorFlow

      An Open Source Machine Learning Framework for Everyone

    • Django photo Django

      The Web framework for perfectionists with deadlines.

    • D3 photo D3

      Bring data to life with SVG, Canvas and HTML. 📊📈🎉

    Recommend Topics

    • javascript

      JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

    • web

      Some thing interesting about web. New door for the world.

    • server

      A server is a program made to process requests and deliver data to clients.

    • Machine learning

      Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

    • Game

      Some thing interesting about game, make everyone happy.

    Recommend Org

    • Facebook photo Facebook

      We are working to build community through open source technology. NB: members must have two-factor auth.

    • Microsoft photo Microsoft

      Open source projects and samples from Microsoft.

    • Google photo Google

      Google ❤️ Open Source for everyone.

    • D3 photo D3

      Data-Driven Documents codes.