Я работаю над элементом календаря, в котором дневные события используют строку сетки для охвата указанного периода времени.
Тем не менее, мне кажется, что я борюсь с установкой ширины событий. По умолчанию они занимают всю ширину дня, если это единственное событие в это время. И если в какой-то момент они являются совпадающими событиями, они соответственно уменьшат свою ширину, чтобы все они могли поместиться.
Моя проблема возникает, когда у меня одновременно происходит группа событий, которая генерирует несколько столбцов, а затем в тот же день происходит событие, не имеющее совпадающих событий. Это событие теперь ограничено шириной столбцов, созданных на основе событий, произошедших ранее в тот же день.
См. изображение для контекста: Изображение аннотированной демонстрации
См. ниже рабочую демонстрацию.
(function() {
function init() {
let calendarElement = document.getElementsByClassName('calendar')[0],
events = calendarElement.getElementsByClassName('calendar__event');
_positionEventsOnGrid(events);
}
function _positionEventsOnGrid(events) {
Array.from(events).forEach(event => {
let gridRow = event.getAttribute('data-grid-row');
if (gridRow) {
let gridRowSpan = event.getAttribute('data-grid-row-span');
event.style.gridRow = gridRowSpan ? `${gridRow} / span ${gridRowSpan}` : gridRow;
}
});
}
init();
})();body {
margin: 0;
block-size: 100vh;
inline-size: 100vw;
}
.calendar {
$block: &;
inline-size: 100%;
block-size: 100%;
display: grid;
grid-template-columns: 2.5rem 1fr;
grid-template-rows: auto 1fr;
}
.calendar__dayNames {
grid-row: 1;
grid-column: 2;
display: flex;
}
.calendar__dayName {
flex-grow: 1;
flex-basis: 0;
}
.calendar__schedule {
grid-row: 2;
grid-column: 1/span 2;
display: grid;
grid-template-columns: 2.5rem 1fr;
border-block-start: 1px solid #000;
}
.calendar__timeline {
display: grid;
grid-template-rows: repeat(19, 1.375rem);
gap: 0 0.625rem;
grid-column: 1/ span 2;
grid-row: 1;
position: relative;
}
.calendar__timelineItem {
display: flex;
align-items: center;
border-block-end: 1px dotted #222;
}
.calendar__timelineItem:nth-child(even) {
border-block-end: 1px solid #000;
}
.calendar__timelineItem:nth-child(odd):after {
display: inline;
content: attr(data-time);
}
.calendar__dayEventsContainer {
position: relative;
grid-row: 1;
grid-column: 2;
display: flex;
}
.calendar__dayEvents {
display: grid;
grid-template-rows: repeat(19, 1.375rem);
gap: 0 0.625rem;
border-inline-start: 1px solid #000;
flex-grow: 1;
flex-basis: 0;
}
.calendar__event {
background-color: #346DA8;
border: none;
}<div class = "calendar">
<div class = "calendar__dayNames">
<div class = "calendar__dayName">Monday</div>
<div class = "calendar__dayName">Tuesday</div>
<div class = "calendar__dayName">Wednesday</div>
<div class = "calendar__dayName">Thursday</div>
<div class = "calendar__dayName">Friday</div>
</div>
<div class = "calendar__schedule">
<div class = "calendar__timeline">
<div class = "calendar__timelineItem" data-time = "09:00"></div>
<div class = "calendar__timelineItem" data-time = "09:15"></div>
<div class = "calendar__timelineItem" data-time = "09:30"></div>
<div class = "calendar__timelineItem" data-time = "09:45"></div>
<div class = "calendar__timelineItem" data-time = "10:00"></div>
<div class = "calendar__timelineItem" data-time = "10:15"></div>
<div class = "calendar__timelineItem" data-time = "10:30"></div>
<div class = "calendar__timelineItem" data-time = "10:45"></div>
<div class = "calendar__timelineItem" data-time = "11:00"></div>
<div class = "calendar__timelineItem" data-time = "11:15"></div>
<div class = "calendar__timelineItem" data-time = "11:30"></div>
<div class = "calendar__timelineItem" data-time = "11:45"></div>
<div class = "calendar__timelineItem" data-time = "12:00"></div>
<div class = "calendar__timelineItem" data-time = "12:15"></div>
<div class = "calendar__timelineItem" data-time = "12:30"></div>
<div class = "calendar__timelineItem" data-time = "12:45"></div>
<div class = "calendar__timelineItem" data-time = "13:00"></div>
<div class = "calendar__timelineItem" data-time = "13:15"></div>
<div class = "calendar__timelineItem" data-time = "13:30"></div>
</div>
<div class = "calendar__dayEventsContainer">
<div class = "calendar__dayEvents">
<button type = "button" class = "calendar__event" data-grid-row = "1" data-grid-row-span = "6"></button>
<button type = "button" class = "calendar__event" data-grid-row = "1" data-grid-row-span = "3"></button>
<button type = "button" class = "calendar__event" data-grid-row = "1" data-grid-row-span = "1"></button>
<button type = "button" class = "calendar__event" data-grid-row = "10" data-grid-row-span = "1"></button>
</div>
<div class = "calendar__dayEvents">
</div>
<div class = "calendar__dayEvents">
<button type = "button" class = "calendar__event" data-grid-row = "1" data-grid-row-span = "6"></button>
<button type = "button" class = "calendar__event" data-grid-row = "1" data-grid-row-span = "1"></button>
</div>
<div class = "calendar__dayEvents">
</div>
<div class = "calendar__dayEvents">
<button type = "button" class = "calendar__event" data-grid-row = "1" data-grid-row-span = "1"></button>
</div>
</div>
</div>
</div>Я попробовал несколько вещей: Grid-column: 1/-1, но это будет относиться ко всем событиям и, следовательно, к неправильному форматированию одновременных событий.
Grid-auto-flow: плотный, однако в моем примере это, похоже, не помогло.
Grid-template-columns: повторение(auto-fit, minmax()), но минимальное значение как часть minmax должно быть фиксированного размера.
Мне интересно, нужно ли мне расширить функцию PositionEventsOnGrid(), чтобы она проверяла, можно ли применить «grid-column: 1/-1» к конкретным событиям. Если это не совпадает с другими событиями того времени. Однако я чувствую, что это может быть довольно сложно, поэтому решил спросить, есть ли более простой способ.
Я хочу, чтобы отдельные события охватывали всю продолжительность дня, независимо от того, происходят ли одновременные события раньше или позже в течение дня.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Столбцы сетки в вашем решении генерируются неявно, поскольку ваш CSS явно не задает макет столбца. См. ответ @Michael Benjamin здесь.
Проблема в том, что числовые индексы (1/-1) работают только с явными сетками. Другими словами, сетки с определенными столбцами и строками. Вы используете неявную сетку, в которой столбцы и строки генерируются автоматически по мере необходимости.
event.style.gridColumn = 1 / -1;, примененный к элементу .calendar__event, растягивается по всем столбцам, если вы установили grid-template-columns: repeat() в правило CSS для .calendar__dayEvents контейнера. Это похоже на ваш комментарий «минимальное значение как часть минимального максимума должно быть фиксированного размера».
Мне интересно, нужно ли мне расширить функцию PositionEventsOnGrid(), чтобы она проверяла, можно ли применить «grid-column: 1/-1» к конкретным событиям. Если это не совпадает с другими событиями того времени. Однако я чувствую, что это может быть довольно сложно, поэтому решил спросить, есть ли более простой способ.
Я не нашел способа обойти это. К счастью, существуют решения SO, которые могут группировать перекрывающиеся временные или целочисленные интервалы. В своих компонентах календаря я применил/адаптировал функцию из GitHub, которую опубликовал в связанном сообщении SO. Адаптация применяется к приведенному ниже фрагменту кода.
Следующий фрагмент кода выполняет следующее:
data-type.JS-исполнение
event.style.gridRow.window функции getComputedStyle(), как предусмотрено в этом посте SO: подсчет столбцов в адаптивной сетке..calendar__dayEvents каждого дня.
и установите непересекающиеся элементы событий на event.style.gridColumn = 1 / -1;.event.style.gridColumn = 1 / span ${gridColumnCount}; вместо установки количества столбцов сетки в значение
grid-template-columns сетка событий
контейнер. (См. комментарии во фрагменте кода.)(function() {
function init() {
//const calendarElement = document.getElementsByClassName('calendar')[0];
//const events = calendarElement.getElementsByClassName('calendar__event');
//_positionEventsOnGrid(events);
const dayEventsElems = document.querySelectorAll('.calendar__dayEvents');
_positionEventsOnGrid2(dayEventsElems);
}
/*
function _positionEventsOnGrid(events) {
Array.from(events).forEach(event => {
let gridRow = event.getAttribute('data-grid-row');
if (gridRow) {
let gridRowSpan = event.getAttribute('data-grid-row-span');
event.style.gridRow = gridRowSpan ? `${gridRow} / span ${gridRowSpan}` : gridRow;
}
});
}
*/
function _positionEventsOnGrid2(dayEventsElems) {
if (!dayEventsElems) {
return;
}
dayEventsElems.forEach(dayEventElem => {
const events = dayEventElem.querySelectorAll('.calendar__event');
// if there are no event elements in the
// '.calendar__dayEvents' element then exit function.
if (events.length === 0) {
return;
}
// Grid layout using "implicit grid, where columns and rows
// are automatically generated, as needed."
// Source: https://stackoverflow.com/a/75250349
// Also see: https://stackoverflow.com/a/73624201
const overlappingGroups = getOverlappingEventGroups(events);
overlappingGroups.forEach(group => {
group.forEach(event => {
event.style.gridRow = `${event.startRow} / ${event.endRow}`;
});
});
// After the grid is laid out, count the number of columns
// created using the built-in 'gridComputedStyle()'.
// See the function 'getGridRowColCount()' below.
const gridElem = events[0].closest('.calendar__dayEvents');
// https://www.javascripttutorial.net/javascript-return-multiple-values
const [gridRowCount, gridColumnCount] = getGridRowColCount(gridElem);
gridElem.style.gridTemplateColumns = `repeat(${gridColumnCount}, 1fr)`;
overlappingGroups.forEach(group => {
// If the event group does not contain one entry,
// then exit function.
if (group.length !== 1) {
return;
}
// Otherwise, explicitly set the 'gridColumn'
// CSS style property to '1 / -1' so that the event
// element spans all columns in the 'gridElem' grid.
group[0].style.gridColumn = `1 / -1`;
// Or set the span value and remove the code above that
// sets the CSS property 'gridTemplateColumns'.
//group[0].style.gridColumn = `1 / span ${gridColumnCount}`;
});
});
}
// Source: https://stackoverflow.com/questions/55204205/a-way-to-count-columns-in-a-responsive-grid
function getGridRowColCount(grid) {
const gridComputedStyle = window.getComputedStyle(grid);
// get number of grid rows
const gridRowCount = gridComputedStyle.getPropertyValue("grid-template-rows").split(" ").length;
// get number of grid columns
const gridColumnCount = gridComputedStyle.getPropertyValue("grid-template-columns").split(" ").length;
//console.info(gridRowCount, gridColumnCount);
// https://www.javascripttutorial.net/javascript-return-multiple-values
return [gridRowCount, gridColumnCount];
}
/*
https://stackoverflow.com/a/75486209
Format of intervals array from:
https://gist.githubusercontent.com/blasten/acfaafc8247e37abf23f/raw/c71f42428e34dbb7ff2d1b5e932fdf4152fe1ad2/group-intervals.js
const intervals = [
[2, 5],
[5, 6],
[3, 4],
[7, 8],
[6.5, 9],
[10, 11.5]
];
The function 'getOverlappingEventGroups()' returns a
modified intervals array format containing '.calendar__event'
DOM elements with custom 'startRow' and 'endRow' properties.
*/
function getOverlappingEventGroups(events) {
const eventIntervals = [];
events.forEach(event => {
const gridRow = event.getAttribute('data-grid-row');
const gridRowSpan = event.getAttribute('data-grid-row-span');
if (isNaN(gridRow) || isNaN(gridRowSpan)) {
return;
}
const startRow = parseInt(gridRow, 10);
const endRow = startRow + parseInt(gridRowSpan, 10);
event.startRow = startRow;
event.endRow = endRow;
eventIntervals.push(event);
});
const eventGroups = groupOverlapingEventIntervals(eventIntervals);
//console.info("Overlapping event groups: ", eventGroups);
return eventGroups;
}
// Source: https://gist.githubusercontent.com/blasten/acfaafc8247e37abf23f/raw/c71f42428e34dbb7ff2d1b5e932fdf4152fe1ad2/group-intervals.js
function groupOverlapingEventIntervals(eventIntervals) {
eventIntervals.sort((a, b) => a.startRow - b.startRow);
const groups = [
[eventIntervals[0]]
];
let j = 0;
let end = eventIntervals[0].endRow;
for (let i = 1; i < eventIntervals.length; i++) {
if (eventIntervals[i].startRow <= end) {
if (eventIntervals[i].endRow > end) {
end = eventIntervals[i].endRow;
}
groups[j].push(eventIntervals[i]);
} else {
groups.push([eventIntervals[i]]);
j++;
end = eventIntervals[i].endRow;
}
}
return groups;
}
init();
})();body {
margin: 0;
block-size: 100vh;
inline-size: 100vw;
}
.calendar {
inline-size: 100%;
block-size: 100%;
display: grid;
grid-template-columns: 2.5rem 1fr;
grid-template-rows: auto 1fr;
}
.calendar__dayNames {
grid-row: 1;
grid-column: 2;
display: flex;
}
.calendar__dayName {
flex-grow: 1;
flex-basis: 0;
}
.calendar__schedule {
grid-row: 2;
grid-column: 1/span 2;
display: grid;
grid-template-columns: 2.5rem 1fr;
border-block-start: 1px solid #000;
}
.calendar__timeline {
display: grid;
grid-template-rows: repeat(19, 1.375rem);
gap: 0 0.625rem;
grid-column: 1/ span 2;
grid-row: 1;
position: relative;
}
.calendar__timelineItem {
display: flex;
align-items: center;
border-block-end: 1px dotted #222;
}
.calendar__timelineItem:nth-child(even) {
border-block-end: 1px solid #000;
}
.calendar__timelineItem:nth-child(odd):after {
display: inline;
content: attr(data-time);
}
.calendar__dayEventsContainer {
position: relative;
grid-row: 1;
grid-column: 2;
display: flex;
}
.calendar__dayEvents {
display: grid;
grid-template-rows: repeat(19, 1.375rem);
gap: 0 0.625rem;
border-inline-start: 1px solid #000;
flex-grow: 1;
flex-basis: 0;
}
.calendar__event {
background-color: #346DA8;
border: none;
}
/* Color-coded event elements */
.calendar__event[data-type=aaa] {
background-color: rgb(243 165 97 / 80%);
}
.calendar__event[data-type=bbb] {
background-color: rgb(80 142 77 / 80%);
}
.calendar__event[data-type=ccc] {
background-color: rgb(228 53 53 / 80%);
}
.calendar__event[data-type=ddd] {
background-color: rgb(96 177 218 / 80%);
}<div class = "calendar">
<div class = "calendar__dayNames">
<div class = "calendar__dayName">Monday</div>
<div class = "calendar__dayName">Tuesday</div>
<div class = "calendar__dayName">Wednesday</div>
<div class = "calendar__dayName">Thursday</div>
<div class = "calendar__dayName">Friday</div>
</div>
<div class = "calendar__schedule">
<div class = "calendar__timeline">
<div class = "calendar__timelineItem" data-time = "09:00"></div>
<div class = "calendar__timelineItem" data-time = "09:15"></div>
<div class = "calendar__timelineItem" data-time = "09:30"></div>
<div class = "calendar__timelineItem" data-time = "09:45"></div>
<div class = "calendar__timelineItem" data-time = "10:00"></div>
<div class = "calendar__timelineItem" data-time = "10:15"></div>
<div class = "calendar__timelineItem" data-time = "10:30"></div>
<div class = "calendar__timelineItem" data-time = "10:45"></div>
<div class = "calendar__timelineItem" data-time = "11:00"></div>
<div class = "calendar__timelineItem" data-time = "11:15"></div>
<div class = "calendar__timelineItem" data-time = "11:30"></div>
<div class = "calendar__timelineItem" data-time = "11:45"></div>
<div class = "calendar__timelineItem" data-time = "12:00"></div>
<div class = "calendar__timelineItem" data-time = "12:15"></div>
<div class = "calendar__timelineItem" data-time = "12:30"></div>
<div class = "calendar__timelineItem" data-time = "12:45"></div>
<div class = "calendar__timelineItem" data-time = "13:00"></div>
<div class = "calendar__timelineItem" data-time = "13:15"></div>
<div class = "calendar__timelineItem" data-time = "13:30"></div>
</div>
<div class = "calendar__dayEventsContainer">
<div class = "calendar__dayEvents">
<button type = "button" class = "calendar__event" data-grid-row = "1" data-grid-row-span = "6"></button>
<button type = "button" class = "calendar__event" data-grid-row = "1" data-grid-row-span = "3"></button>
<button type = "button" class = "calendar__event" data-grid-row = "1" data-grid-row-span = "1"></button>
<button type = "button" class = "calendar__event" data-grid-row = "10" data-grid-row-span = "1"></button>
</div>
<div class = "calendar__dayEvents">
<button type = "button" class = "calendar__event" data-grid-row = "3" data-grid-row-span = "7" data-type = "bbb"></button>
<button type = "button" class = "calendar__event" data-grid-row = "12" data-grid-row-span = "3" data-type = "aaa"></button>
<button type = "button" class = "calendar__event" data-grid-row = "14" data-grid-row-span = "2" data-type = "ccc"></button>
</div>
<div class = "calendar__dayEvents">
<button type = "button" class = "calendar__event" data-grid-row = "1" data-grid-row-span = "6"></button>
<button type = "button" class = "calendar__event" data-grid-row = "1" data-grid-row-span = "1"></button>
</div>
<div class = "calendar__dayEvents">
<button type = "button" class = "calendar__event" data-grid-row = "1" data-grid-row-span = "6" data-type = "aaa"></button>
<button type = "button" class = "calendar__event" data-grid-row = "1" data-grid-row-span = "3" data-type = "bbb"></button>
<button type = "button" class = "calendar__event" data-grid-row = "1" data-grid-row-span = "1" data-type = "aaa"></button>
<button type = "button" class = "calendar__event" data-grid-row = "2" data-grid-row-span = "1" data-type = "ccc"></button>
<button type = "button" class = "calendar__event" data-grid-row = "2" data-grid-row-span = "4" data-type = "bbb"></button>
<button type = "button" class = "calendar__event" data-grid-row = "10" data-grid-row-span = "1" data-type = "ddd"></button>
<button type = "button" class = "calendar__event" data-grid-row = "14" data-grid-row-span = "3" data-type = "ddd"></button>
</div>
<div class = "calendar__dayEvents">
<button type = "button" class = "calendar__event" data-grid-row = "1" data-grid-row-span = "1"></button>
</div>
</div>
</div>
</div>Обновление (26.09.2023)
Добавлено изображение с аннотациями в качестве альтернативного описания выполнения кода.
.calendar__dayEvents div-контейнеры (то есть каждый столбец дня недели, выделенный желтым)..calendar__event элементы с перекрывающимися интервалами (обведены красными рамками)..calendar__event в контейнере .calendar__dayEvents div/столбце дня недели..calendar__event в перекрывающихся группах только с 1 записью .calendar__event..calendar__dayEvents и установите элемент .calendar__event в перекрывающихся группах только с одной записью .calendar__event, чтобы охватить все столбцы (через style.gridColumn = 1 / -1).
Пожалуйста, добавьте Минимально воспроизводимый пример прямо в свой вопрос. Пожалуйста, не указывайте нам код, связанный с другим веб-сайтом. Пожалуйста, отредактируйте свой вопрос и нажмите кнопку на панели инструментов, которая выглядит как
[<>], чтобы добавить к вашему вопросу фрагмент кода, а не только блоки кода. Также измените SCSS на скомпилированный вывод CSS, чтобы фрагмент мог работать здесь.