Один из подходов заключается в следующем.
const addTask = () => {
const D = document,
timeline = D.querySelector('ol.timeline'),
taskElement = D.createElement('li'),
span = D.createElement('span'),
div = D.createElement('div');
span.classList.add('connection');
div.textContent = timeline.children.length + 1;
taskElement.append(span, div);
timeline.appendChild(taskElement);
}
document.querySelector('#addEntry').addEventListener('click', addTask);
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
button {
display: block;
width: 80vw;
margin: 0.5em auto;
height: 2em;
line-height: 2em;
}
:root {
--timeline_connector_position: 30%;
--timeline_margin: auto;
--timeline_gapSize: 0.2rem 0rem;
--timeline_centre: 5vw;
--timeline_width: 90vw;
--timeline_padding: 0.5rem;
--timeline_taskBackground: #fff;
--timeline_taskBackground_even: #f90;
--timeline_taskBackground_odd: limegreen;
--timeline_taskBorder: 1px solid #000;
--timeline_taskBorder_even: 1px solid #000;
--timeline_taskBorder_odd: 1px solid #000;
--timeline_color: brown;
}
ol,
li {
list-style-type: none;
}
ol.timeline {
display: grid;
width: var(--timeline_width);
margin: var(--timeline_margin);
grid-template-columns: 1fr var(--timeline_centre) 1fr;
grid-gap: var(--timeline_gapSize);
background: linear-gradient( to right, transparent, transparent calc(50% - var(--timeline_centre)/6), var(--timeline_color) calc(50% - var(--timeline_centre)/6), var(--timeline_color) calc(50% + var(--timeline_centre)/6), transparent calc(50% + var(--timeline_centre)/6), transparent);
}
li {
display: grid;
grid-gap: var(--timeline_gapSize);
}
li:nth-child(odd) {
grid-area: auto / 1 / auto / span 2;
grid-template-columns: 1fr var(--timeline_centre);
grid-template-areas: "text connection";
}
li:nth-child(even) {
grid-area: auto / 2 / auto / span 2;
grid-template-columns: var(--timeline_centre) 1fr;
grid-template-areas: "connection text";
}
li>div {
background-color: var(--timeline_taskBackground);
padding: var(--timeline_padding);
grid-area: text;
}
li:nth-child(odd)>div {
background-color: var(--timeline_taskBackground_odd);
border: var(--timeline_taskBorder_odd, --timeline_taskBorder);
}
li:nth-child(even)>div {
background-color: var(--timeline_taskBackground_even);
border: var(--timeline_taskBorder_even, --timeline_taskBorder);
}
span.connection {
grid-area: connection;
background: linear-gradient( to bottom, transparent, transparent calc(var(--timeline_connector_position) - 3px), var(--timeline_color) calc(var(--timeline_connector_position) - 3px), var(--timeline_color) calc(var(--timeline_connector_position) + 3px), transparent calc(var(--timeline_connector_position) + 3px), transparent);
background-size: 50%;
background-repeat: no-repeat;
}
li:nth-child(odd)>span.connection {
background-position: left;
}
li:nth-child(even)>span.connection {
background-position: right;
}
<button id="addEntry">Add a new entry</button>
<ol class="timeline">
<li>
<span class="connection"></span>
<div>1 Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aut explicabo quas esse, odio accusantium minus. Asperiores mollitia est, iusto corrupti quibusdam ipsa, assumenda nam id animi quod, nesciunt placeat. Perferendis!</div>
</li>
<li>
<span class="connection"></span>
<div>2</div>
</li>
<li>
<span class="connection"></span>
<div>3</div>
</li>
<li>
<span class="connection"></span>
<div>4</div>
</li>
<li>
<span class="connection"></span>
<div>5</div>
</li>
<li>
<span class="connection"></span>
<div>6</div>
</li>
<li>
<span class="connection"></span>
<div>7</div>
</li>
<li>
<span class="connection"></span>
<div>8</div>
</li>
<li>
<span class="connection"></span>
<div>9</div>
</li>
<li>
<span class="connection"></span>
<div>10</div>
</li>
</ol>
JS Fiddle demo .
В приведенной выше демонстрации мы размечаем временную шкалу и ееследующие записи:
<!-- use of an ordered list for the timeline itself: -->
<ol class="timeline">
<!-- each timeline entry is nested within and <li> element: -->
<li>
<!-- this could have been replaced by a pseudo-element and, perhaps
should have been; its use is purely presentational and serves to
connect the 'task' to the 'timeline' graphic: -->
<span class="connection"></span>
<!-- the <div> is used to wrap the text of the timeline entry: -->
<div>1 Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aut explicabo quas esse, odio accusantium minus. Asperiores mollitia est, iusto corrupti quibusdam ipsa, assumenda nam id animi quod, nesciunt placeat. Perferendis!</div>
</li>
<!-- other entries omitted for brevity -->
</ol>
Причина, по которой мы вложили записи временной шкалы, заключается в основном в том, что подсетка CSS пока не поддерживается в большинстве браузеров, и это простое средство, с помощью которого мы можем сместить<li>
записей соответственно, как показано на CSS - -
/* I've used quite a lot of CSS variables for purposes of theming,
retain or discard at your pleasure! */
:root {
--timeline_connector_position: 30%;
--timeline_margin: auto;
--timeline_gapSize: 0.2rem 0rem;
--timeline_centre: 5vw;
--timeline_width: 90vw;
--timeline_padding: 0.5rem;
--timeline_taskBackground: #fff;
--timeline_taskBackground_even: #f90;
--timeline_taskBackground_odd: limegreen;
--timeline_taskBorder: 1px solid #000;
--timeline_taskBorder_even: 1px solid #000;
--timeline_taskBorder_odd: 1px solid #000;
--timeline_color: brown;
}
ol.timeline {
display: grid;
/* Using some of the defined CSS custom properties to set
the width and margin: */
width: var(--timeline_width);
margin: var(--timeline_margin);
/* Here we define the three-column layout, comprising of
column 1: 1 fractional unit,
column 2: defined by the --timeline_centre custom property,
column 3: 1 fractional unit: */
grid-template-columns: 1fr var(--timeline_centre) 1fr;
grid-gap: var(--timeline_gapSize);
/* This background defines the 'timeline' vertical stroke: */
background: linear-gradient(
/* the gradient moves from left to right: */
to right,
/* is transparent at the start of the gradient: */
transparent,
/* remains transparent until the point calculated by the
CSS calc function; here we're calculating a pixel-value of
50% width of the element, minus the width defined by the
--timeline_centre value divided by six (the six is arbitrary
but provided a good-enough visual in the JS Fiddle and Snippet,
adjust to taste): */
transparent calc(50% - var(--timeline_centre)/6),
/* at the same point as above the colour changes to the colour
defined by the --timeline_color custom property: */
var(--timeline_color) calc(50% - var(--timeline_centre)/6),
/* the change here is that we add, rather than subtract the value
in order that the --timeline_color spans across the 50% mark: */
var(--timeline_color) calc(50% + var(--timeline_centre)/6),
/* at the same point that the --timeline_color ends the transparent
value resumes; this creates hard colour stops rather than
fading gradients: */
transparent calc(50% + var(--timeline_centre)/6),
transparent
);
}
li {
display: grid;
grid-gap: var(--timeline_gapSize);
}
li:nth-child(odd) {
/* here the odd <li> elements are placed with the syntax of:
row-start / column-start / row-end / column-end
in this case the row is placed into its naturally-occuring
row ('auto'), starts in the first column of the grid (1), ends
in the naturally-occurring row ('auto') and spans two columns
('span 2'): */
grid-area: auto / 1 / auto / span 2;
/* we define a two-column grid, of 1 fractional unit, and one
column equal to the size defined by the --timeline_centre
custom property: */
grid-template-columns: 1fr var(--timeline_centre);
/* we name the two columns of this two-column grid: */
grid-template-areas: "text connection";
}
li:nth-child(even) {
/* exactly as above, but the even <li> elements start
in the second column instead of the first: */
grid-area: auto / 2 / auto / span 2;
/* exactly as above but the columns are reversed in order
that the var(--timeline_centre)/'connection' column
overlaps the column of the same width in the parent
grid: */
grid-template-columns: var(--timeline_centre) 1fr;
grid-template-areas: "connection text";
}
li>div {
/* here we use some of the theming options to style the
element: */
background-color: var(--timeline_taskBackground);
padding: var(--timeline_padding);
/* positioning the timeline entry text in the grid
area identified by the name 'text': */
grid-area: text;
}
li:nth-child(odd)>div {
background-color: var(--timeline_taskBackground_odd);
border: var(--timeline_taskBorder_odd, --timeline_taskBorder);
}
li:nth-child(even)>div {
background-color: var(--timeline_taskBackground_even);
border: var(--timeline_taskBorder_even, --timeline_taskBorder);
}
span.connection {
grid-area: connection;
background: linear-gradient( to bottom, transparent, transparent calc(var(--timeline_connector_position) - 3px), var(--timeline_color) calc(var(--timeline_connector_position) - 3px), var(--timeline_color) calc(var(--timeline_connector_position) + 3px), transparent calc(var(--timeline_connector_position) + 3px), transparent);
background-size: 50%;
background-repeat: no-repeat;
}
/* here we position the connection background in order that it
visually connects the timeline entry text to the timeline
itself: */
li:nth-child(odd)>span.connection {
background-position: left;
}
li:nth-child(even)>span.connection {
background-position: right;
}