Calculate left positioning in div jquery

Several days ago I asked the following question on webapps.stackexchange.com:
https://webapps.stackexchange.com/questions/54130/is-there-a-way-to-remove-overlaying-events-in-google-calendar

I didn't get any good answers so far, so I've decided to write my own little script that will change width of the event based on the number of overlapping events.

I want to avoid overlapping of the boxes and want them to stack.

Here's the initial test:

Below is a basic script with description:

    $('[class^="tg-col"]').each(function(){ // each day column 
                                           //(Mon, Tue, etc. has class tg-col or tg-col-weekend.
    var ItemArray = [];
    $(this).find(".chip").each(function(){ //each event box has chip class.

    var top = $$(this).position().top;  //Get top pixels
    var bottom = $$(this).height() + top;  //get end of the event.
    var arr = ItemArray.push({
        class: $$(this).attr("class").split(" ")[0], 
        start : top, 
        end:bottom
         }); 
    });
    var result = getOverlaps(ItemArray); //get overlaps counts number of overlapping events.
    $$.each(result, function(index, value){ 
      var ec = result[index].eventCount;
      var w = 100/result[index].eventCount-1 + "%";
    var el = $$("."+result[index].class);
      el.width(w);
    //el.css("left",w);
        });
    });
function getOverlaps(events) {
    // sort events
    events.sort(function (a, b) {
        return a.start - b.start;
    });

    var results = [];
    for (var i = 0, l = events.length; i < l; i++) {
        var oEvent = events[i];
        var nOverlaps = 0;
        for (var j = 0; j < l; j++) {
            var oCompareEvent = events[j];
            if (oCompareEvent.start <= oEvent.end && oCompareEvent.end > oEvent.start || oCompareEvent.end <= oEvent.start && oCompareEvent.start > oEvent.end) {
                nOverlaps++;
            }
        }
        if (nOverlaps > 1) {
            results.push({
                class: oEvent.class,
                eventCount: nOverlaps
            });
        }

    }
    return results;
}

This solution works only on simple events (on the left):

For more complex overlapping we cannot simply count numbers of overlaps and diving 100% / number of overlaps. We have to take into considerations other dimensions.

Also, after each manipulation in google calendar it redraws events and script changes are lost. Is there easier way to solve this problem?
(Maybe it is possible to modify js received from google directly? (But it's minified :().


You already have correct left positions so you only need to set correct width.

In pseudocode:

foreach chip
  chips_siblings = this.parent
                       .allchilds(chip)
                       .filter(this.top <= child.top <= this.bottom
                               && child.left > this.left)
  this.width = min(chips_siblings.left) - this.left

In JavaScript:

function placeChipsCorrectly() {
    "use strict"
    Array.prototype.forEach.call(
        document.getElementsByClassName('chip'),
        function(self) {
            var siblings = Array.prototype.filter.call(
                self.parentElement.getElementsByClassName('chip'),
                function(child) {
                  return child.offsetTop >= self.offsetTop-2 &&
                         child.offsetTop <= self.offsetTop+self.offsetHeight-3 &&
                         child.offsetLeft > self.offsetLeft;
                });
            if (siblings.length > 0) {
                var minLeft = Math.min.apply(null, Array.prototype.map.call(
                    siblings,
                    function(el) {
                        return el.offsetLeft;
                    }));
                self.style.width = (minLeft - self.offsetLeft - 2) + "px";
            }
        });
}
placeChipsCorrectly();

As for tracking modification events:

After each data change you can see "Loading..." in the top right corner. After inspecting how it works you can see that it changes style attribute of the container with id lo-c .
So you can track it appearance and disappearance as following:

(new MutationObserver(placeChipsCorrectly)).observe(document.getElementById('lo-c'),{attributes: true});

When you only modify view and not data (eg by choosing another day or week) then "Loading..." doesn't appear. For these events you may track global click event:

document.body.addEventListener('click', placeChipsCorrectly);

Keep in mind that both methods would take some overhead. If you would like to improve them, start by modifying the observer so that it calls placeChipsCorrectly only on disappearance and by limiting click tracking to specific controls.


You could use the bounding box of your elements and try to avoid the overlap by using (in pure javascript)

document.getElementById("elemID").getBoundingClientRect();

I think the equivalent in jquery is something like

elemID.offset()

With this, you can have all Top, Bottom, Right and Left properties. With it, you can way more easily detect if you are overlaping or not. And once you have detect, just make the ajustment from the overlaping box bounds.


I think you should approach the problem from a different angle. You have a list of events, with a starting time and an ending time. I believe Google Calendar displays events with a precision of 15 minutes on a grid. Furthermore, notice that we don't need to know how many events a certain event conflicts with during it's entire timespan, but instead how many events it conflict with on every interval.

So... what if you do some preperation and determine for each 15-minute timespan how many events there are. First you prepare an Array:

var timespans = Array();
//24*4 quarters
for( var i = 0; i < 96; i++ ) {
  timespans[i] = 0;
}

Then we populate this day:

for( var i = 0, l = events.length; i < l; i++) {
  var oEvent = events[i];
  //Assuming that .start and .end is 'minutes since midnight'
  var firstQuarter = Math.floor( oEvent.start / 15 );
  var lastQuarter = Math.ceil( oEvent.end / 15 );
  for( var j = firstQuarter; j < lastQuarter; j++ ) {
    timespans[j] += 1;
  }
}

Then if we loop over all events again, we just have to check the Array we prepared.

for( var i = 0, l = events.length; i < l; i++) {
  var oEvent = events[i];
  //Assuming that .start and .end is 'minutes since midnight'
  var firstQuarter = Math.floor( oEvent.start / 15 );
  var lastQuarter = Math.ceil( oEvent.end / 15 );
  var conflictingTimespans = timespans.slice( firstQuarter, lastQuarter );
  //http://stackoverflow.com/a/1669222/2209007
  var maxConflicting = Math.max.apply( null, conflictingTimespans );
  //Set width of said element to columnWidth / maxConflicting
}

Besides that it should get rid of your problem, you will only check 2n events, instead of n^2 events, which should increase performance.

As for your second question, I am unsure how to help you with that.

链接地址: http://www.djcxy.com/p/18546.html

上一篇: 有什么办法可以避免子查询?

下一篇: 计算div jquery中的左侧定位