简体   繁体   English

检查两个或多个DOM元素是否重叠

[英]Check if two or more DOM elements overlap

Good day people! 美好的一天!

I have run into an issue with my simple single day calendar script. 我的简单单日历脚本遇到了问题。

I've been tasked to create a single day calendar, which shows each block hour from 9am to 6pm. 我的任务是创建一个日历,显示从上午9点到下午6点的每个小时。 If an event overlaps another, they should equal the same width and not overlap. 如果事件与另一个事件重叠,则它们应该等于相同的宽度而不重叠。 I have managed to achieve this for two events, however if more than two overlap, things go abit south, I need help figuring out a method to fix this where any number of events overlap, their widths will equal the same. 我已经设法为两个事件实现了这个目标,但是如果有两个以上的重叠,那么事情就会向南发展,我需要帮助找出解决这个问题的方法,其中任意数量的事件重叠,它们的宽度将相同。

Events are rendered on the calendar using a global function: 使用全局函数在日历上呈现事件:

renderDay([{start: 30, end: 120},{start: 60, end: 120}]) renderDay([{start:30,end:120},{start:60,end:120}])

which takes an array of objects as an argument, where the integers are the number of minutes pasted from 9am. 它将一个对象数组作为参数,其中整数是从上午9点粘贴的分钟数。 eg. 例如。 30 is 9:30am, 120 is 11am 30是上午9:30,120是上午11点

here is the collision function I took from stackoverflow 这是我从stackoverflow中获取的碰撞函数

// collision function to return boolean
// attribute: http://stackoverflow.com/questions/14012766/detecting-whether-two-divs-overlap
function collision($div1, $div2) {
  let x1 = $div1.offset().left;
  let y1 = $div1.offset().top;
  let h1 = $div1.outerHeight(true);
  let w1 = $div1.outerWidth(true);
  let b1 = y1 + h1;
  let r1 = x1 + w1;
  let x2 = $div2.offset().left;
  let y2 = $div2.offset().top;
  let h2 = $div2.outerHeight(true);
  let w2 = $div2.outerWidth(true);
  let b2 = y2 + h2;
  let r2 = x2 + w2;

  if (b1 < y2 || y1 > b2 || r1 < x2 || x1 > r2) return false;
  return true;
}

I run a loop on all the event divs which I want to check for overlaps 我在所有事件div上运行一个循环,我想检查重叠

// JQuery on each, check if collision
$('.event').each(function(index, value) {

      // statement to break out on final loop
      if(index === $('.event').length - 1) return;

      console.log('at index: ', index);

      // if collison === true, halve width of both event divs, re-position
      if(collision(  $('#e-'+index) , $('#e-'+(index + 1)) )) {

        $('#e-'+index).css('width', $('#e-'+index).width() / 2);
        $('#e-'+(index+ 1)).css('width', $('#e-'+(index+ 1)).width() / 2).css('left', $('#e-'+(index + 1)).width());

        if(collision)
      }
    })
  }
})

Screenshots to help visualize :) 帮助可视化的截图:)

When two overlap, they have equal widths 当两个重叠时,它们具有相等的宽度

When three or more overlap, things go wrong 当三个或更多重叠时,事情就出错了

Any help would be greatly appreciated! 任何帮助将不胜感激! DW DW

After looking at the code, it seems overly complex to check the rendered elements for collision when you can work this out from the start and end times. 在查看代码之后,当您从开始和结束时间开始处理时,检查渲染的元素是否过于复杂似乎过于复杂。

The way I've done it, is to group events which collide in arrays like so: 我这样做的方法是将碰撞在数组中的事件分组,如下所示:

let collisions = [
      // only 1 event in this array so no collisions
      [{
        start: 30,
        end: 120
      }],
      // 3 events in this array which have overlapping times
      [{
        start: 300,
        end: 330
      }, {
        start: 290,
        end: 330
      }, {
        start: 300,
        end: 330
      }]
    ];

Then we iterate through each group of collisions, and create the elements with the appropriate width and positioning. 然后我们遍历每组碰撞,并创建具有适当宽度和位置的元素。

for (var i = 0; i < collisions.length; i++) {
  var collision = collisions[i];

  for (var j = 0; j < collision.length; j++) {
    var event = collision[j];

    let height = event.end - event.start;
    let top = event.start + 50;

    // 360 = max width of event
    let width = 360 / collision.length;

    // a lot of this could be moved into a css class
    // I removed the "display: inline-block" code because these are absolutely positioned. Replaced it with "left: (j * width)px"
    let div = $(`<div id=${'e-'+ (i + j)}>`).css('position', 'absolute').css('top', top)
    .css('height', height).css('width', width).css('left', (j * width) + 'px')
    .css('backgroundColor', arrayOfColors.shift()).addClass('event')
    .text('New Event').css('fontWeight', 'bold');

    // append event div to parent container
    $('#events').append(div);
  }
}

 //********************************************************************** // // TITLE - Thought Machine Coding Challenge, Single Day Calendar // AUTHOR - DOUGLAS WISSETT WALKER // DATE - 21/04/2016 // VERSION - 0.0.3 // PREVIOUS - 0.0.2 // //********************************************************************** let arr = [{ start: 30, end: 120 }, { start: 70, end: 180 }, { start: 80, end: 190 }, { start: 300, end: 330 }, { start: 290, end: 330 }, { start: 220, end: 260 }, { start: 220, end: 260 }, { start: 220, end: 260 }, { start: 220, end: 260 }, { start: 400, end: 440 }, { start: 20, end: 200 }]; let renderDay; $(document).ready(() => { renderDay = function(array) { $('.event').each(function(i, el) { $(el).remove(); }); // background colors for events let arrayOfColors = [ 'rgba(255, 153, 153, 0.75)', 'rgba(255, 204, 153, 0.75)', 'rgba(204, 255, 153, 0.75)', 'rgba(153, 255, 255, 0.75)', 'rgba(153, 153, 255, 0.75)', 'rgba(255, 153, 255, 0.75)' ] let collisions = mapCollisions(array); let eventCount = 0; // used for unique id for (let i = 0; i < collisions.length; i++) { let collision = collisions[i]; for (let j = 0; j < collision.length; j++) { let event = collision[j]; let height = event.end - event.start; let top = event.start + 50; // 360 = max width of event let width = 360 / collision.length; // a lot of this could be moved into a css class // I removed the "display: inline-block" code because these are absolutely positioned // Replaced it with "left: (j * width)px" let div = $("<div id='e-" + eventCount + "'>").css('position', 'absolute').css('top', top) .css('height', height).css('width', width).css('left', (j * width) + 'px') .css('backgroundColor', arrayOfColors.shift()).addClass('event') .text('New Event').css('fontWeight', 'bold'); eventCount++; // append event div to parent container $('#events').append(div); } } } renderDay(arr); }); // Sorry this is pretty messy and I'm not familiar with ES6/Typescript or whatever you are using function mapCollisions(array) { let collisions = []; for (let i = 0; i < array.length; i++) { let event = array[i]; let collides = false; // for each group of colliding events, check if this event collides for (let j = 0; j < collisions.length; j++) { let collision = collisions[j]; // for each event in a group of colliding events for (let k = 0; k < collision.length; k++) { let collidingEvent = collision[k]; // event which possibly collides // Not 100% sure if this will catch all collisions if ( event.start >= collidingEvent.start && event.start < collidingEvent.end || event.end <= collidingEvent.end && event.end > collidingEvent.start || collidingEvent.start >= event.start && collidingEvent.start < event.end || collidingEvent.end <= event.end && collidingEvent.end > event.start) { collision.push(event); collides = true; break; } } } if (!collides) { collisions.push([event]); } } console.log(collisions); return collisions; } 
 html, body { margin: 0; padding: 0; font-family: sans-serif; } #container { height: 100%; width: 100%; } #header-title { text-align: center; } #calendar { width: 400px; height: 620px; margin-top: 70px; } #events { position: absolute; top: 80px; left: 100px; width: 800px; height: 620px; } .event { box-shadow: 0 0 20px black; border-radius: 5px; } .hr-block { border-top: 2px solid black; height: 58px; margin: 0; padding: 0; margin-left: 100px; min-width: 360px; opacity: .5; } .hr-header { position: relative; top: -33px; left: -68px; } 
 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="css/styles.css"> <link rel="stylesheet" href="css/responsive.css"> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.3/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script> <script charset="UTF-8" src="js/moment.js"></script> <script charset="UTF-8" src="js/script2.js"></script> <title>Thought Machine Code Challenge</title> </head> <body> <div id="container"> <div class="header"> <h1 id="header-title"></h1> </div> <div id="calendar"> <div class="hr-block"> <h2 class="hr-header">09:00</h2> </div> <div class="hr-block"> <h2 class="hr-header">10:00</h2> </div> <div class="hr-block"> <h2 class="hr-header">11:00</h2> </div> <div class="hr-block"> <h2 class="hr-header">12:00</h2> </div> <div class="hr-block"> <h2 class="hr-header">13:00</h2> </div> <div class="hr-block"> <h2 class="hr-header">14:00</h2> </div> <div class="hr-block"> <h2 class="hr-header">15:00</h2> </div> <div class="hr-block"> <h2 class="hr-header">16:00</h2> </div> <div class="hr-block"> <h2 class="hr-header">17:00</h2> </div> <div class="hr-block"> <h2 class="hr-header">18:00</h2> </div> </div> </div> <div id="events"> </div> <script> document.getElementById("header-title").innerHTML = moment().calendar(); </script> </body> </html> 

working index, script and css files 工作索引,脚本和css文件

 //********************************************************************** // // TITLE - Thought Machine Coding Challenge, Single Day Calendar // AUTHOR - DOUGLAS WISSETT WALKER // DATE - 21/04/2016 // VERSION - 0.0.3 // PREVIOUS - 0.0.2 // //********************************************************************** let arr = [{start: 30, end: 120},{start: 300, end: 330},{start: 290, end: 330}]; let renderDay; $(document).ready(() => { renderDay = function(array) { $('.event').each(function(i, el) { $(el).remove(); }); // background colors for events let arrayOfColors = [ 'rgba(255, 153, 153, 0.75)', 'rgba(255, 204, 153, 0.75)', 'rgba(204, 255, 153, 0.75)', 'rgba(153, 255, 255, 0.75)', 'rgba(153, 153, 255, 0.75)', 'rgba(255, 153, 255, 0.75)' ] // iterate through each event time array.forEach((eventTimes, index) => { // define event height and top position on calendar let height = eventTimes.end - eventTimes.start; let top = eventTimes.start + 50; // max width of event let width = 360; // create event div let div = $(`<div id=${'e-'+index}>`).css('position', 'absolute').css('top', top) .css('height', height).css('width', width).css('display', 'inline-block') .css('backgroundColor', arrayOfColors.shift()).addClass('event') .text('New Event').css('fontWeight', 'bold'); // append event div to parent container $('#events').append(div); }) // JQuery on each, check if collision $('.event').each(function(index, value) { // statement to break out on final loop if(index === $('.event').length - 1) return; console.log('at index: ', index); // if collison === true, halve width of both event divs, re-position if(collision( $('#e-'+index) , $('#e-'+(index + 1)) )) { $('#e-'+index).css('width', $('#e-'+index).width() / 2); $('#e-'+(index+ 1)).css('width', $('#e-'+(index+ 1)).width() / 2).css('left', $('#e-'+(index + 1)).width()); } }) } }) // collision function to return boolean // attribute: http://stackoverflow.com/questions/14012766/detecting-whether-two-divs-overlap function collision($div1, $div2) { let x1 = $div1.offset().left; let y1 = $div1.offset().top; let h1 = $div1.outerHeight(true); let w1 = $div1.outerWidth(true); let b1 = y1 + h1; let r1 = x1 + w1; let x2 = $div2.offset().left; let y2 = $div2.offset().top; let h2 = $div2.outerHeight(true); let w2 = $div2.outerWidth(true); let b2 = y2 + h2; let r2 = x2 + w2; if (b1 < y2 || y1 > b2 || r1 < x2 || x1 > r2) return false; return true; } // render events using renderDay(arr) in console 
 html, body { margin: 0; padding: 0; font-family: sans-serif; } #container { height: 100%; width: 100%; } #header-title { text-align: center; } #calendar { width: 400px; height: 620px; margin-top: 70px; } #events { position: absolute; top: 80px; left: 100px; width: 800px; height: 620px; } .event { box-shadow: 0 0 20px black; border-radius: 5px; } .hr-block { border-top: 2px solid black; height: 58px; margin: 0; padding: 0; margin-left: 100px; min-width: 360px; opacity: .5; } .hr-header { position: relative; top: -33px; left: -68px; } 
 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="css/styles.css"> <link rel="stylesheet" href="css/responsive.css"> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.3/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script> <script charset="UTF-8" src="js/moment.js"></script> <script charset="UTF-8" src="js/script2.js"></script> <title>Thought Machine Code Challenge</title> </head> <body> <div id="container"> <div class="header"> <h1 id="header-title"></h1> </div> <div id="calendar"> <div class="hr-block"><h2 class="hr-header">09:00</h2></div> <div class="hr-block"><h2 class="hr-header">10:00</h2></div> <div class="hr-block"><h2 class="hr-header">11:00</h2></div> <div class="hr-block"><h2 class="hr-header">12:00</h2></div> <div class="hr-block"><h2 class="hr-header">13:00</h2></div> <div class="hr-block"><h2 class="hr-header">14:00</h2></div> <div class="hr-block"><h2 class="hr-header">15:00</h2></div> <div class="hr-block"><h2 class="hr-header">16:00</h2></div> <div class="hr-block"><h2 class="hr-header">17:00</h2></div> <div class="hr-block"><h2 class="hr-header">18:00</h2></div> </div> </div> <div id="events"> </div> <script> document.getElementById("header-title").innerHTML = moment().calendar(); </script> </body> </html> 

events not rendering, you need to run: 事件未呈现,您需要运行:

renderDay([{start:30, end:120, start: 60, end: 120}]) in console 在控制台中的renderDay([{start:30,end:120,start:60,end:120}])

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM