0

I'm making a scheduling calendar. The events are horizontal blocks (Google Cal has vertical ones). And because I have loads of events in one date I want the events to stack onto eachother without wasting any space, like this: This is what I want

I did find plugins:
http://masonry.desandro.com/
http://isotope.metafizzy.co/
http://packery.metafizzy.co/
But I'm not keen on using a 30kb plugin just to do this simple thing.

To clarify: because this is a timeline, the div-events cannot move left/right, but must fit itself vertically amongst other div-events.

krivar
  • 342
  • 1
  • 3
  • 16

1 Answers1

0

My own solution is a 2.6kb (commented) jQuery script, that uses absolute positioning for events and a div as a container for each row.

This script generates randomly size bars. Each new bar checks for space in each row from top-down. I'm using percentages, because that way calculating bar position against time will be easy (100% / 24h).

Result

http://jsfiddle.net/cj23H/5/
Though works and is efficient enough, it feels bulky. You're welcome to improve.

jQuery code from my jsfiddle:

function rand() {
    return Math.floor(Math.random() * 101);
}

function get_target_row(width, left) {
    var i = 0;
    var target_found = false;

    // Loop through all the rows to see if there's space anywhere
    while (target_found === false) {

        // Define current row selector
        var target_i = '.personnel#row' + i;

        // Add row if none
        if ($(target_i).length === 0) {
            $('body').append('<div class="personnel" id="row' + i + '"></div>');
        }
        // See if there's space
        if ($(target_i).children().length === 0) {
            target_found = $(target_i);
        } else {
            var spaceFree = false;

            // Check collision for each child
            $(target_i).children().each(function () {

                // Get left and right coordinates
                var thisWidthPx = parseFloat($(this).css('width'), 10);
                var thisWidthParentPx = parseFloat($(this).parent().css('width'), 10);
                var thisWidth = (thisWidthPx / thisWidthParentPx * 100);
                var thisLeftPx = parseFloat($(this).css('left'), 10);
                var thisLeftParentPx = parseFloat($(this).parent().css('left'), 10);
                var thisLeft = (thisLeftPx / thisWidthParentPx * 100);
                var thisRight = thisLeft + thisWidth;
                var right = left + width;

                // Sexy way for checking collision
                if ((right > thisLeft && right < thisRight) || (left < thisRight && left > thisLeft) || (thisLeft > left && thisLeft < right) || (thisRight < right && thisRight > left)) {
                    spaceFree = false;
                    return false;
                } else {
                    spaceFree = true;
                }
            });

            // If no children have collided break the loop
            if (spaceFree === true) {
                target_found = $(target_i);
            }
        }
        i++;
    }

    // If after all the while loops target is still not found...
    if (target_found === false) {
        return false;
    }

    // Else, if target is found, return it.
    return target_found;
}

// Generate random bars
for (var i = 0; i < 10; i++) {
    var width = rand()/2;
    var left = rand()/2;
    var right = left + width;
    var target = get_target_row(width, left);
    target.append('<div class="block" style="width:' + width + '%;position:absolute;left:' + left + '%;background-color:rgba(' + rand() + ',' + rand() + ',' + rand() + ',0.4)" id="bar'+i+'">' + 'b' + i + '</div>');
}

CSS:

.block {
    position: absolute;
    background-color: rgba(200, 100, 100, 0.4);
    height: 32px;
}
.personnel {
    position: relative;
    float: left;
    width: 100%;
    height: 33px;
}
krivar
  • 342
  • 1
  • 3
  • 16