javascript 在FullCalendar.js中制作悬停相交效果

dxxyhpgq  于 2023-05-05  发布在  Java
关注(0)|答案(1)|浏览(133)

我想在时间轴日历中制作一个交叉点悬停效果。
我用CSS在日历和交叉点悬停效果上做了另一个表。
它工作正常,但如果我们有一个事件,有一个描述在多行(或多个事件在同一时间),行高是不同的,我的悬停效果不工作OK -它重叠,如下图所示。代码可在https://codepen.io/timotejmedved/pen/vYVepaQ?editors=0110上获得

document.addEventListener('DOMContentLoaded', function () {
  var calendarEl = document.getElementById('calendar');

  var calendar = new FullCalendar.Calendar(calendarEl, {
    headerToolbar: {
      left: 'today prev,next',
      center: 'title',
      right: 'resourceTimelineDay,resourceTimelineWeek'
    },
    aspectRatio: 1.6,
    initialView: 'resourceTimelineDay',
    resourceGroupField: 'building',
    slotDuration: '00:15:00',
    resources: [
      { id: '1', title: 'Resource A' },
      { id: '2', title: 'Resource B' },
      { id: '3', title: 'Resource C' },
      { id: '4', title: 'Resource A' },
      { id: '5', title: 'Resource B' },
      { id: '6', title: 'Resource C' },
      { id: '11', title: 'Resource A' },
      { id: '21', title: 'Resource B' },
      { id: '31', title: 'Resource C' },
      { id: '41', title: 'Resource A' },
      { id: '51', title: 'Resource B' },
      { id: '61', title: 'Resource C' },
      { id: '111', title: 'Resource A' },
      { id: '211', title: 'Resource B' },
      { id: '311', title: 'Resource C' },
      { id: '411', title: 'Resource A' },
      { id: '511', title: 'Resource B' },
      { id: '611', title: 'Resource C' },

      { id: '19', title: 'Resource A' },
      { id: '29', title: 'Resource B' },
      { id: '39', title: 'Resource C' },
      { id: '49', title: 'Resource A' },
      { id: '59', title: 'Resource B' },
      { id: '69', title: 'Resource C' },
      { id: '191', title: 'Resource A' },
      { id: '291', title: 'Resource B' },
      { id: '319', title: 'Resource C' },
      { id: '419', title: 'Resource A' },
      { id: '519', title: 'Resource B' },
      { id: '619', title: 'Resource C' },
      { id: '1191', title: 'Resource A' },
      { id: '2191', title: 'Resource B' },
      { id: '3191', title: 'Resource C' },
      { id: '4191', title: 'Resource A' },
      { id: '5191', title: 'Resource B' },
      { id: '6191', title: 'Resource C' },
    ],
    events: [
      { resourceId: '1', start: '2023-05-02T02:00:00', end: '2023-05-02T07:00:00', title: 'event 1' },
      { resourceId: '2', start: '2023-05-02T05:00:00', end: '2023-05-02T09:00:00', title: 'event 2' },
      { resourceId: '3', start: '2023-05-02T03:00:00', end: '2023-05-02T08:00:00', title: 'event 3' },
      { resourceId: '4', start: '2023-05-02T06:00:00', end: '2023-05-02T12:00:00', title: 'event 4' },
      { resourceId: '5', start: '2023-05-02T08:00:00', end: '2023-05-02T13:00:00', title: 'event 5' },
      { resourceId: '6', start: '2023-05-02T10:00:00', end: '2023-05-02T14:00:00', title: 'event 6' },
      { resourceId: '1', start: '2023-05-02T13:00:00', end: '2023-05-02T18:00:00', title: 'event 7' },
      { resourceId: '2', start: '2023-05-02T10:00:00', end: '2023-05-02T14:00:00', title: 'event 8' },
      { resourceId: '3', start: '2023-05-02T08:00:00', end: '2023-05-02T12:30:00', title: 'event 9' }
    ],

    eventContent: function (arg) {

      var event = arg.event;

      var customHtml = '';

      customHtml += "<span class='r10 font-xxs font-bold' style='overflow: hidden;'>" + event.title + "</span>";

      customHtml += "<span class='r10 highlighted-badge font-xxs font-bold'> <br/> jkaSDHLKJahsdlkjAHS</span>";

      return { html: customHtml }
    },

    eventDidMount: function (info) {

    },



    viewDidMount: function (info) {
      var rowHeights = document.querySelectorAll('.fc-timeline-body  .fc-scrollgrid-sync-table  tbody tr');
  
      var numResources = document.querySelectorAll('table.fc-datagrid-body tbody tr').length;

      var resourcesHeights = document.querySelectorAll('table.fc-datagrid-body tbody tr')

      console.log(resourcesHeights.item(1))
      console.log(resourcesHeights.values())

      var tt = resourcesHeights.item(0);

      const compStyles = window.getComputedStyle(tt);
      var height = compStyles.getPropertyValue("height");
      console.log(height)

      // create new table element
      var hoverTable = document.createElement('table');
      hoverTable.classList.add('hover-table');

      // get the main table element
      var mainTable = document.querySelector('.fc-timeline-body table');

      console.log("maintable");
      console.log(mainTable.offsetHeight * 3);

      //var cellWidth = mainTable.querySelector('td').offsetWidth;
      //var rowHeight = mainTable.querySelector('tr').offsetHeight;
      var cellWidth = 30;
      // var rowHeight = 34.4;
      var rowHeight = 34;
      //console.log(rowHeight)

      // set the position, width, and height of the hover table
      hoverTable.style.position = 'absolute';
      hoverTable.style.top = mainTable.offsetTop + 'px';
      hoverTable.style.left = mainTable.offsetLeft + 'px';
      hoverTable.style.width = (cellWidth * 3) + 'px';
      hoverTable.style.height = (rowHeight * 3) + 'px';
      hoverTable.style.border = 'none';
      hoverTable.style.borderRadius = '0';
      hoverTable.style.boxShadow = 'none';
      hoverTable.style.backgroundColor = 'transparent';
      hoverTable.style.borderWidth = '0';


      // add rows and cells to the hover table  
      for (var i = 0; i < numResources + 1; i++) {

        var row = document.createElement('tr');

        if (i == 0) {
          row.style.height = '32.5 px';
        } else {
          row.style.height = rowHeight + 'px';
        }

        for (var j = 0; j < 48; j++) {
          var cell = document.createElement('td');
          cell.style.width = cellWidth + 'px';
          row.appendChild(cell);
        }
        hoverTable.appendChild(row);
      }

      // add the hover table to the DOM
      mainTable.parentNode.insertBefore(hoverTable, mainTable.nextSibling);

      // add event listeners to the cells of the hover table
      var hoverCells = hoverTable.querySelectorAll('td');
      hoverCells.forEach(function (cell) {
        cell.addEventListener('mouseenter', function () {
          cell.classList.add('hover');
        });
        cell.addEventListener('mouseleave', function () {
          cell.classList.remove('hover');
        });
      });
    }

  });

  calendar.render();
});
html, body {
  margin: 0;
  padding: 0;
  font-family: Arial, Helvetica Neue, Helvetica, sans-serif;
  font-size: 14px;
}

#calendar {
  max-width: 1100px;
  margin: 40px auto;
}

/*.fc-timeline-slot-lane:hover{
  background-color: lightgray;
}

.fc-timeline-lane:hover{
  background-color: lightgray;
}*/

.fc-time-grid-container .fc-slats td:hover,
.fc-time-grid-container .fc-major td:hover {
  background-color:red;
}

.hover{
  background-color: red;
}

.overlay {
  position: absolute;
  z-index: 1;
  top: 0;
  left: 0;
  pointer-events: none;
}

.overlay-cell {
  position: absolute;
  pointer-events: auto;
  transition: background-color 0.2s;
}

.overlay-cell.hover {
  background-color: rgba(0, 0, 0, 0.1);
}

.hover-table{
  background-color: grey;
 
}



.hover-table {
  overflow: hidden;
}

.hover-table tr:hover {
  background-color: rgba(215, 215, 215, 0.29);
}

.hover-table td, th {
  position: relative;
}
.hover-table td:hover::after,
.hover-table th:hover::after {
  content: "";
  position: absolute;
  background-color: rgba(215, 215, 215, 0.29);
  left: 0;
  top: -5000px;
  height: 10000px;
  width: 100%;
  z-index: -1;
}

/*
.hover-table td:first-child {
  background-color: rebeccapurple;
 
}

.hover-table tr:first-child {
  background-color: rebeccapurple;
}*/

.hover-table td {
 
  border: none;
}



/*način 2 za row hover*/
/*
.hover-table {
  pointer-events: none;
  background-color: transparent;
}

.fc-timeline-slots{
  pointer-events: none;
  background-color: transparent;
}

.fc-timeline-body  .fc-scrollgrid-sync-table  tbody tr:hover{
  background-color: red;
}
*/
<script src="https://cdn.jsdelivr.net/npm/fullcalendar-scheduler@6.1.6/index.global.min.js"></script>
<div id='calendar'></div>

我尝试了不同的方法,例如尝试获取每行的clientHeight属性,但我没有获取当前值。然后我尝试使用鼠标位置。我也试过使用pointer-events: none;,但是我只能显示行悬停,而不能同时显示列和行。我还尝试了mouseenter事件,但由于fullcalendar在行元素之上有列,所以我只得到列的hover元素。

7nbnzgx9

7nbnzgx91#

列突出显示可以通过将CSS应用于悬停的.fc-timeline-slot-lane元素来完成:

.fc-timeline-slot-lane:hover {
  background-color: lightgray;
}

但是对于行,我们不能使用相同的:hover选择器,因为上面的.fc-timeline-slot-lane覆盖了行,所以我们需要添加一些JavaScript来帮助我们。我们可以重写viewDidMount来帮助我们:

/**
 * Hover row styling.
 */
.fc-timeline-lane.hover {
  background-color: lightgray;
}
viewDidMount: function ({ el }) {
  // Get the `clientX` and `clientY` from the `mousemove` event object. These
  // correspond to the mouse position in the webpage.
  const handle = ({ clientX, clientY }) => {
    // Get all elements that are at the mouse position.
    const elements = document.elementsFromPoint(clientX, clientY);
    // Get the horizontal row element that might be under the mouse.
    const lane = elements.find(element => element.matches('.fc-timeline-lane'));
    // If the `lane` does not have a `hover` class or if there is no
    // `.fc-timeline-lane` element under the mouse:
    if (!lane?.classList.contains('.hover')) {
      // Try to find the `.fc-timeline-lane` that has the `hover` class and
      // and remove it.
      el.querySelector('.fc-timeline-lane.hover')?.classList.remove('hover');
      // Add the `hover` class to our found `.fc-timeline-lane` element (if
      // any).
      lane?.classList.add('hover');
    }
  };

  // Get the container that has the columns and row elements.
  const timeSlots = el.querySelector('.fc-timeline-slots');
  // When the mouse starts to hover over the timeline, listen for mouse
  // movement.
  timeSlots.addEventListener('mouseenter', () => {
    timeSlots.addEventListener('mousemove', handle);
  });
  // When the mouse no longer hovers over the timeline, stop listening for 
  // mouse movement.
  timeSlots.addEventListener('mouseleave', () => {
    timeSlots.removeEventListener('mousemove', handle);
  });
}

对于单元格的红色矩形,我们可以使用列,并使用更多JavaScript调整其背景,以定位红色矩形以匹配我们悬停的行:

.fc-timeline-slot-lane:hover {
  background: lightgray linear-gradient(red, red) no-repeat left var(--top, 0px) / 100% var(--height, 0px);
}

CSS变量--top--height将从JavaScript传递过来:

viewDidMount: function ({ el }) {
  const handle = ({ clientX, clientY }) => {
    const elements = document.elementsFromPoint(clientX, clientY);
    const lane = elements.find(element => element.matches('.fc-timeline-lane'));
    // Get the vertical slot we're hovering over, if any.
    const slot = elements.find(element => element.matches('.fc-timeline-slot-lane'));
    if (!lane?.classList.contains('.hover')) {
      el.querySelector('.fc-timeline-lane.hover')?.classList.remove('hover');
      lane.classList.add('hover');
    }
    // If not hovering over a slot, or this slot does not already have
    // background coordinates:
    if (!slot?.style.getPropertyValue('--top')) {
      // Remove background coordinates from a different vertical slot (if any).
      el.querySelector('.fc-timeline-slot-lane[style]')?.removeAttribute('style');
    }
    // If we're hovering over a vertical and a horizontal slot:
    if (slot && lane) {
      // Get the position-parent-relative `top` and `height` of the horizontal
      // slot.
      const { offsetTop, offsetHeight } = lane;
      // Set the CSS variables to these.
      slot.style.setProperty('--top', `${offsetTop}px`);
      slot.style.setProperty('--height', `${offsetHeight}px`);
    }
  };

  const timeSlots = el.querySelector('.fc-timeline-slots');
  timeSlots.addEventListener('mouseenter', () => {
    timeSlots.addEventListener('mousemove', handle);
  });
  timeSlots.addEventListener('mouseleave', () => {
    timeSlots.removeEventListener('mousemove', handle);
  });
}

这里是所有放在一起:

document.addEventListener('DOMContentLoaded', function () {
  var calendarEl = document.getElementById('calendar');

  var calendar = new FullCalendar.Calendar(calendarEl, {
    headerToolbar: {
      left: 'today prev,next',
      center: 'title',
      right: 'resourceTimelineDay,resourceTimelineWeek'
    },
    aspectRatio: 1.6,
    initialView: 'resourceTimelineDay',
    resourceGroupField: 'building',
    slotDuration: '00:15:00',
    resources: [
      { id: '1', title: 'Resource A' },
      { id: '2', title: 'Resource B' },
      { id: '3', title: 'Resource C' },
      { id: '4', title: 'Resource A' },
      { id: '5', title: 'Resource B' },
      { id: '6', title: 'Resource C' },
      { id: '11', title: 'Resource A' },
      { id: '21', title: 'Resource B' },
      { id: '31', title: 'Resource C' },
      { id: '41', title: 'Resource A' },
      { id: '51', title: 'Resource B' },
      { id: '61', title: 'Resource C' },
      { id: '111', title: 'Resource A' },
      { id: '211', title: 'Resource B' },
      { id: '311', title: 'Resource C' },
      { id: '411', title: 'Resource A' },
      { id: '511', title: 'Resource B' },
      { id: '611', title: 'Resource C' },

      { id: '19', title: 'Resource A' },
      { id: '29', title: 'Resource B' },
      { id: '39', title: 'Resource C' },
      { id: '49', title: 'Resource A' },
      { id: '59', title: 'Resource B' },
      { id: '69', title: 'Resource C' },
      { id: '191', title: 'Resource A' },
      { id: '291', title: 'Resource B' },
      { id: '319', title: 'Resource C' },
      { id: '419', title: 'Resource A' },
      { id: '519', title: 'Resource B' },
      { id: '619', title: 'Resource C' },
      { id: '1191', title: 'Resource A' },
      { id: '2191', title: 'Resource B' },
      { id: '3191', title: 'Resource C' },
      { id: '4191', title: 'Resource A' },
      { id: '5191', title: 'Resource B' },
      { id: '6191', title: 'Resource C' },
    ],
    events: [
      { resourceId: '1', start: '2023-05-02T02:00:00', end: '2023-05-02T07:00:00', title: 'event 1' },
      { resourceId: '2', start: '2023-05-02T05:00:00', end: '2023-05-02T09:00:00', title: 'event 2' },
      { resourceId: '3', start: '2023-05-02T03:00:00', end: '2023-05-02T08:00:00', title: 'event 3' },
      { resourceId: '4', start: '2023-05-02T06:00:00', end: '2023-05-02T12:00:00', title: 'event 4' },
      { resourceId: '5', start: '2023-05-02T08:00:00', end: '2023-05-02T13:00:00', title: 'event 5' },
      { resourceId: '6', start: '2023-05-02T10:00:00', end: '2023-05-02T14:00:00', title: 'event 6' },
      { resourceId: '1', start: '2023-05-02T13:00:00', end: '2023-05-02T18:00:00', title: 'event 7' },
      { resourceId: '2', start: '2023-05-02T10:00:00', end: '2023-05-02T14:00:00', title: 'event 8' },
      { resourceId: '3', start: '2023-05-02T08:00:00', end: '2023-05-02T12:30:00', title: 'event 9' }
    ],

    eventContent: function (arg) {

      var event = arg.event;

      var customHtml = '';

      customHtml += "<span class='r10 font-xxs font-bold' style='overflow: hidden;'>" + event.title + "</span>";

      customHtml += "<span class='r10 highlighted-badge font-xxs font-bold'> <br/> jkaSDHLKJahsdlkjAHS</span>";

      return { html: customHtml }
    },

    eventDidMount: function (info) {

    },



    viewDidMount: function ({ el }) {
      const handle = ({ clientX, clientY }) => {
        const elements = document.elementsFromPoint(clientX, clientY);
        const lane = elements.find(element => element.matches('.fc-timeline-lane'));
        const slot = elements.find(element => element.matches('.fc-timeline-slot-lane'));
        if (!lane?.classList.contains('.hover')) {
          el.querySelector('.fc-timeline-lane.hover')?.classList.remove('hover');
          lane?.classList.add('hover');
        }
        if (!slot?.style.getPropertyValue('--top')) {
          el.querySelector('.fc-timeline-slot-lane[style]')?.removeAttribute('style');
        }
        if (slot && lane) {
          const { offsetTop, offsetHeight } = lane;
          slot.style.setProperty('--top', `${offsetTop}px`);
          slot.style.setProperty('--height', `${offsetHeight}px`);
        }
      };
      
      const timeSlots = el.querySelector('.fc-timeline-slots');
      timeSlots.addEventListener('mouseenter', () => {
        timeSlots.addEventListener('mousemove', handle);
      });
      timeSlots.addEventListener('mouseleave', () => {
        timeSlots.removeEventListener('mousemove', handle);
      });
    }

  });

  calendar.render();
});
html, body {
  margin: 0;
  padding: 0;
  font-family: Arial, Helvetica Neue, Helvetica, sans-serif;
  font-size: 14px;
}

#calendar {
  max-width: 1100px;
  margin: 40px auto;
}

/*.fc-timeline-slot-lane:hover{
  background-color: lightgray;
}

.fc-timeline-lane:hover{
  background-color: lightgray;
}*/

.fc-time-grid-container .fc-slats td:hover,
.fc-time-grid-container .fc-major td:hover {
  background-color:red;
}

.hover{
  background-color: red;
}

.overlay {
  position: absolute;
  z-index: 1;
  top: 0;
  left: 0;
  pointer-events: none;
}

.overlay-cell {
  position: absolute;
  pointer-events: auto;
  transition: background-color 0.2s;
}

.overlay-cell.hover {
  background-color: rgba(0, 0, 0, 0.1);
}

.hover-table{
  background-color: grey;
 
}



.hover-table {
  overflow: hidden;
}

.hover-table tr:hover {
  background-color: rgba(215, 215, 215, 0.29);
}

.hover-table td, th {
  position: relative;
}
.hover-table td:hover::after,
.hover-table th:hover::after {
  content: "";
  position: absolute;
  background-color: rgba(215, 215, 215, 0.29);
  left: 0;
  top: -5000px;
  height: 10000px;
  width: 100%;
  z-index: -1;
}

/*
.hover-table td:first-child {
  background-color: rebeccapurple;
 
}

.hover-table tr:first-child {
  background-color: rebeccapurple;
}*/

.hover-table td {
 
  border: none;
}



/*način 2 za row hover*/
/*
.hover-table {
  pointer-events: none;
  background-color: transparent;
}

.fc-timeline-slots{
  pointer-events: none;
  background-color: transparent;
}

.fc-timeline-body  .fc-scrollgrid-sync-table  tbody tr:hover{
  background-color: red;
}
*/

.fc-timeline-slot-lane:hover {
  background: lightgray linear-gradient(red, red) no-repeat left var(--top, 0px) / 100% var(--height, 0px);
}

.fc-timeline-lane.hover {
  background-color: lightgray;
}
<script src="https://cdn.jsdelivr.net/npm/fullcalendar-scheduler@6.1.6/index.global.min.js"></script>
<div id='calendar'></div>

相关问题