Ответ 1
Задайте свойство CSS с вертикальным выравниванием для этих верхних ячеек таблицы;
td {
vertical-align:top;
}
https://developer.mozilla.org/en-US/docs/Web/CSS/vertical-align
Я создаю планировщик календаря, где я надеюсь добавить несколько задач в одну строку сотрудника. Всякий раз, когда я пытаюсь добавить несколько задач на один и тот же промежуток времени, промежутки больше не выстраиваются в линию. Вот пример того, как это выглядит сейчас: . Какова была бы лучшая практика добавления задач в столбцы того же дня, если бы такая задача, как "Slaughter them", была аналогичной?
Скрипт HTML:
<script>
var sysDate = new Date();
var sysDay = new Date();
var sysMonth = new Date();
var dayCount = sysDay.getDay();
var weekdays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
let employee;
let typeofKey;
let empArry = [];
let x = 1;
let y = 0;
let trInc= 0;
var drawTable = '<table id = wholeTable>';
drawTable += "<thead>";
drawTable += "<tr>";
drawTable += "<th style='color:white;'>Employee Name<\/th>";
let today = new Date();
let dd = today.getDate();
let mm = today.getMonth()+1; //January is 0!
let mmN = mm;
let yyyy = today.getFullYear();
let oneWeekAhead = new Date();
let nextWeek = (today.getDate()+10) + 10;
/* If next week falls into the next month, print correctly */
if (nextWeek > 30 && (mmN == 9 || mmN == 4 || mmN == 6 || mmN == 11)) {
mmN++;
nextWeek -= 30;
}
else if (nextWeek > 31 && (mmN == 1 || mmN == 3 || mmN == 5 || mmN == 7 || mmN == 8 || mmN == 10 || mmN == 12)) {
mmN++;
nextWeek -= 31;
}
else if (nextWeek > 28 && mmN == 2) {
mmN++;
nextWeek -= 28;
}
/* Formatting of the dates at the top of the table */
if(dd < 10) {
dd = '0'+ dd;
}
if(nextWeek < 10) {
nextWeek = '0' + nextWeek;
}
if(mmN < 10) {
mmN = '0' + mmN
}
if(mm < 10) {
mm = '0' + mm;
}
let edate = yyyy + mmN + nextWeek;
/* Finds the logged earliest and latest dates */
let startDate1 = yyyy.toString() + mm.toString() + dd.toString();
let startDate = parseInt(startDate1);
let endDate = parseInt(edate);
let startDateN = 20180501;
let endDateN = 20180531;
/* Change the strings of dates to ints for calculation of start and end date of the table */
if( localStorage.length > 0){
for (var key in localStorage) {
typeofKey = (typeof localStorage[key]);
if (typeofKey == 'string' || typeofKey instanceof String ){
emp1 = JSON.parse(localStorage.getItem(key));
if ("Task" in emp1) {
for (let i = 0; i < emp1.Task.length; i++) {
startDateN = parseInt(emp1.Task[i]['Task Start Date'].substr(0,4) + emp1.Task[i]['Task Start Date'].substr(5,2) + emp1.Task[i]['Task Start Date'].substr(8,2));
endDateN = parseInt(emp1.Task[i]['Task End Date'].substr(0,4) + emp1.Task[i]['Task End Date'].substr(5,2) + emp1.Task[i]['Task End Date'].substr(8,2));
if(endDateN > endDate) {
endDate = endDateN;
}
if(startDateN < startDate) {
startDate = startDateN;
}
}
}
}
}
}
let numStr = null;
let numStrDay = null;
let finalDay = null;
let finalDayF = null;
let colCount = 0;
/* Correctly print the months and days at the top of the table */
for (let i = startDate; i <= endDate +1; i++) {
numStr = (i.toString()).substr(4,2);
numStrDay = (i.toString()).substr(6,2);
if(numStr == '09' || numStr == '04' || numStr == '06' || numStr == '11') {
if(numStrDay == '31') {
i += 69;
}
else {
finalDay = i.toString()
finalDayF = (finalDay.substr(0,4)) + "-" + (finalDay.substr(4,2)) + "-" + (finalDay.substr(6,2));
drawTable += "<th class = days id = days" + x + '-' + y + ">" + finalDayF + "</th>";
colCount++;
}
}
else if(numStr == '01' || numStr == '03' || numStr == '05' || numStr == '07' || numStr == '08' || numStr == '10' || numStr == '12') {
if(numStrDay == '32') {
i += 68;
}
else {
finalDay = i.toString()
finalDayF = (finalDay.substr(0,4)) + "-" + (finalDay.substr(4,2)) + "-" + (finalDay.substr(6,2));
drawTable += "<th class = days id = days" + x + '-' + y + ">" + finalDayF + "</th>";
colCount++;
}
}
else if(numStr == '02') {
if(numStrDay == '29') {
i += 71;
}
else {
finalDay = i.toString()
finalDayF = (finalDay.substr(0,4)) + "-" + (finalDay.substr(4,2)) + "-" + (finalDay.substr(6,2));
drawTable += "<th class = days id = days" + x + '-' + y + ">" + finalDayF + "</th>";
colCount++;
}
}
else {
finalDay = i.toString()
finalDayF = (finalDay.substr(0,4)) + "-" + (finalDay.substr(4,2)) + "-" + (finalDay.substr(6,2));
drawTable += "<th class = days id = days" + x + '-' + y + ">" + finalDayF + "</th>";
colCount++;
}
x++;
}
drawTable += "</tr>";
drawTable += "</thead>";
drawTable += '<tbody class="dragscroll">';
//drawTable += "<tr id =" + trInc + ">";
//trInc++;
//counters for the employee and date rows/col
x=0;
y=1;
// counter for the main table
let z =1;
for (var key in localStorage) {
typeofKey = (typeof localStorage[key]);
//cols of the employee names
if(typeofKey == 'string' || typeofKey instanceof String ){
drawTable += "<tr id =" + trInc + ">";
trInc++;
employee = JSON.parse(localStorage.getItem(key));
drawTable += "<td class = employ id =emp" + x + '-' + y + ">" + employee['Employee Name'] + "</td>";
// rows and cols of the main table and date
for (let j = 0; j < colCount; j++) {
drawTable += "<td class =" + z + '-' + y + "></td>";
z++;
}
// set z to one to start the main tables x at 1 for off by one error
z=1;
//reset x for each row
x=0;
drawTable += '</tr>';
y++;
}
}
drawTable += '<tr>';
var noRows = 14 - localStorage.length;
for(; noRows >= 0; noRows--){
drawTable += "<td class = employ id =emp" + x + '-' + y + ">" + "" + "</td>";
// rows and cols of the main table and date
for (let j = 0; j < colCount; j++) {
drawTable += "<td class =" + z + '-' + y + "></td>";
z++;
}
// set z to one to start the main tables x at 1 for off by one error
z=1;
//reset x for each row
x=0;
drawTable += '</tr>';
y++;
}
drawTable += "</tbody>";
drawTable += "</table>";
document.write(drawTable);
</script>
CSS:
table {
/* border: 0.0625em solid black; */
table-layout: fixed;
position: relative;
width: auto;
overflow: hidden;
border-collapse: collapse;
box-shadow: 0 0 20px rgba(0,0,0,0.1);
}
html, body{
height: 100%;
width: 100%;
background: linear-gradient(45deg, #e1e1e1, #f6f6f6);
}
/*thead*/
thead {
position: relative;
display: block; /*seperates the header from the body allowing it to be positioned*/
width: 1535px;
overflow: visible;
/* border: 1px solid black; */
}
td, th {
padding: 0.75em 1.5em;
text-align: left;
}
thead th {
min-width: 140px;
max-width: 140px;
height: 35px;
text-align: center;
}
thead th:nth-child(1) { /*first cell in the header*/
position: relative;
display: float;
min-width: 140px;
background-color: #202020;
}
/*tbody*/
tbody {
position: relative;
display: block; /*seperates the tbody from the header*/
width: 1535px;
height: 475px;
overflow: scroll;
}
tbody td {
background-color: white;
min-width: 140px;
max-width: 140px;
border: 2px solid #474747;
white-space: nowrap;
}
tbody tr td:nth-child(1) { /*the first cell in each tr*/
position: relative;
/*display: block; seperates the first column from the tbody*/
height: 40px;
min-width: 140px;
max-width: 140px;
}
.dragscroll {
overflow-y: hidden;
margin-right: 0;
height: 600px;
}
.days {
background-color: #31bc86;
color: white;
text-align: center;
}
.employ {
background-color: #2ea879;
color: white;
text-align: center;
}
#taskDiv {
position: absolute;
border: 2px solid black;
}
#days, #emp{
background-color: #071833;
color: white;
}
::-webkit-scrollbar {
width: 20px;
}
/* Track */
::-webkit-scrollbar-track {
box-shadow: inset 0 0 5px black;
}
/* Handle */
::-webkit-scrollbar-thumb {
background: #071833;
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: #1caf8f
}
JS:
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
$(document).ready(function(){
let typeofKey;
let empArry = [];
let employee;
let myTable = document.getElementById('wholeTable');
let colFill = false;
let color = null;
//cols of the employee names
for (var i = 0; i < localStorage.length; i++){
empArry.push(localStorage.key(i))
}
// loop through the local storage and pull the data
for(let j = 0; j < empArry.length; j++){
color = getRandomColor();
employee = JSON.parse(localStorage.getItem(empArry[j]));
// If employee has any task
if("Task" in employee){
// while employee has task in his array
for(let taskIndex = 0; taskIndex < employee.Task.length; taskIndex++){
for(let k = 1; k < myTable.rows[0].cells.length; k++) {
if(myTable.rows[0].cells[k].innerHTML == employee.Task[taskIndex]["Task Start Date"]) {
colFill = true;
}
if(colFill == true) {
myTable.rows[j+1].cells[k].innerHTML += '<div style="background-color:' + color + '">' + employee.Task[taskIndex]["Task Name"] + '</br></div>';
}
if(myTable.rows[0].cells[k].innerHTML == employee.Task[taskIndex]["Task End Date"]) {
colFill = false;
}
}
}
}
}
});
JSFiddle: https://jsfiddle.net/py5gzw0b/1/#&togetherjs=eM8xgAd5eV
Задайте свойство CSS с вертикальным выравниванием для этих верхних ячеек таблицы;
td {
vertical-align:top;
}
https://developer.mozilla.org/en-US/docs/Web/CSS/vertical-align
Если вам нужно, чтобы каждый элемент находился на собственной строке [вместе со всеми идентичными задачами] и идеально выровнен, вам нужно создать строку 1 и вставить только задачи Slaughter, а затем создать строку 2, вставив только задачи Cry, Тогда это обеспечило бы, чтобы все они правильно совпадали друг с другом, хотя в результате это заняло бы больше места.
Другим решением, помимо создания строки для каждой задачи, было бы использование jQuery для поиска "максимальной вершины задачи" и установки всех экземпляров в этом верхнем. Поскольку они занимают одно и то же пространство, это должно быть выполнимым и более гибким, НО вы находитесь за какое-то удовольствие от css-позиционирования.
Чтобы быть более конкретным, вы должны:
1/Добавить уникальный идентификатор для каждой задачи: <div id_task="1" style=...
2/Петля на каждом экземпляре, чтобы найти максимальное верхнее значение:
maxTop=0;
$('[id_task=1]').each(function(){
newTop=$(this).position().top;
if(newTop>maxTop)maxTop=newTop;
});
3/Используйте maxTop
чтобы установить все id_task=1
на один и тот же верхний id_task=1
, возможно, также установите эти div соответственно: $('[id_task=1]').css({'top':maxTop,'position':'relative');
4/Наслаждайтесь тонкой настройкой этого, чтобы закрыть свою желаемую цель, это займет некоторое время ^^
Я сделал код быстро, как пример, там нет никакой гарантии.
Взгляните на это:
var cols = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun']
var employees = [
{
Name: 'Jason',
Task:[
{'Task Start Date':'Mon','Task End Date':'Thu','Task Name':'Do nothing'},
{'Task Start Date':'Thu','Task End Date':'Fri','Task Name':'Do a bit'},
{'Task Start Date':'Sat','Task End Date':'Sun','Task Name':'Do everything'}
]
},
{
Name: 'Timmy',
Task:[
{'Task Start Date':'Mon','Task End Date':'Sun','Task Name':'On vacation'},
{'Task Start Date':'Sat','Task End Date':'Sun','Task Name':'Still on vacation'},
]
}
]
function RowCompound(employee,columns){
var tasks = employee.Task
var spans = []
var max_rows = 1
var rowmap = []
var rows = []
function calcSpans(){
for(var i=0;i<tasks.length;i++){
var cspan = null
for(var u=0;u<columns.length;u++){
if(tasks[i]['Task Start Date']==columns[u]){
cspan = {row:0,start:u,task:tasks[i]}
}
if(cspan && tasks[i]['Task End Date']==columns[u]){
cspan.end = u
spans.push(cspan)
cspan = null
}
}
if(cspan){
cspan.end = columns.length-1
spans.push(cspan)
}
}
}
function solveConflicts(){
var conflict = true
while(conflict){
conflict = false
for(var i=0;i<spans.length;i++){
for(var u=i+1;u<spans.length;u++){
if(spans[i].row!=spans[u].row) continue
if(spans[i].start>spans[u].end || spans[i].end<spans[u].start) continue
conflict = true
max_rows = Math.max(max_rows,++spans[u].row + 1)
}
}
}
}
function createRowMap(){
for(var u=0;u<max_rows;u++){
var row = []
for(var i=0;i<columns.length;i++){
var empty = true
for(var k=0;k<spans.length;k++){
if(spans[k].row!=u) continue
if(i==spans[k].start){
empty = false
var span = spans[k].end-spans[k].start+1
row.push({task:spans[k].task,colspan:span,rowspan:1})
i += span-1
break
}
}
if(empty)
row.push({task:null,colspan:1,rowspan:1})
}
rowmap.push(row)
}
}
function buildDom(){
for(var i=0;i<rowmap.length;i++){
var row = document.createElement('tr')
for(var u=0;u<rowmap[i].length;u++){
if(rowmap[i][u].rowspan==0) continue
var cell = document.createElement('td')
cell.colSpan = rowmap[i][u].colspan
cell.rowSpan = rowmap[i][u].rowspan
if(rowmap[i][u].task){
cell.innerHTML = rowmap[i][u].task['Task Name']
cell.className = 'busy'
}
row.appendChild(cell)
}
rows.push(row)
}
var head = document.createElement('td')
head.rowSpan = max_rows
head.innerHTML = employee.Name
rows[0].prepend(head)
}
calcSpans()
solveConflicts()
createRowMap()
buildDom()
return rows
}
// example use
employees.forEach(function (emp){
var result_rows = RowCompound(emp,cols)
for(var i in result_rows){
document.getElementById('table').appendChild(result_rows[i])
}
})
table{
width: 100%;
border-collapse: collapse;
}
table td{
border: solid 1px #ccc;
width: 50px;
}
table td.busy{
background-color: dodgerblue;
color: white;
}
<table id="table">
<thead>
<th>Employee</th>
<th>Mon</th>
<th>Tue</th>
<th>Wed</th>
<th>Thu</th>
<th>Fri</th>
<th>Sat</th>
<th>Sun</th>
</thead>
</table>
Это скорее концептуальный ответ/идея, но я думаю, что вы могли бы использовать библиотеку макетов JavaScript в изотопах с режимом "вертикального" макета:
https://isotope.metafizzy.co/layout-modes.html
Вы можете отключить все виды анимаций, поэтому в этом смысле это будет ярлык для "позиционирования css", который @Nomis упоминает в ответе выше. Там также может быть интересна библиотека https://masonry.desandro.com того же человека.