在时间轴中嵌套/堆叠div元素

时间:2013-05-22 14:38:08

标签: html css html5 css3

更新了问题:

我想在时间轴中显示事件,如下图所示:

wanted result

事件数据项有两个属性:“开始时间”和“长度”,因此可以将数据想象成一个看起来像这样的表:

event name | start | length
-----------+-------+-------
1          |   0   | 200 
1.1        |  30   | 100 
1.1.1      |  40   |  50 
1.2        | 140   |  50 
2          | 205   | 100 
2.1        | 220   |  20 
2.2        | 250   |  20 
2.3        | 280   |  20 
...        | ...   | ...

数据有一些特点:

  • 事件名称可以是任意的(这里的数字仅用于说明目的)
  • 在另一事件的范围内开始和结束的事件是那些事件的子事件。
    →在上表中,“1.1”将是“1”的子事件。
  • 孩子可以参加儿童活动。
  • 事件永远不会以下列方式重叠......

    +---------+                 +---------+
    |    A    |                 |    C    |
    +---------+                 +---------+
        +---------+   or   +---------+ 
        |    B    |        |    D    | 
        +---------+        +---------+ 
    

    ...因为B将是A的孩子,但它必须在A结束之前结束。 C在D内开始,因此D将是C的父级(并且必须显示在C之上),但是存在与A和B相同的问题。

  • 没有明确的嵌套信息,但隐含的嵌套信息由开始时间和长度提供。
  • 事件按开始时间递增排序。

因此,只有一种方式可以显示事件。


目前我正在尝试这样做:x-position设置为margin-left,但任何其他属性都可以。 div按x值排序:

<div class="container">
    <div class="item" style="margin-left:0; width: 200px;">
        1
    </div>
    <div class="item" style="margin-left:30px; width: 100px;">
        1.1
    </div>
    <div class="item" style="margin-left:40px; width: 50px;">
        1.1.1
    </div>
    <div class="item" style="margin-left:140px; width: 50px;">
        1.2
    </div>
    <div class="item" style="margin-left:205px; width: 100px;">
        2
    </div>
    <div class="item" style="margin-left:220px; width: 20px;">
        2.1
    </div>
    <div class="item" style="margin-left:250px; width: 20px;">
        2.2
    </div>
    <div class="item" style="margin-left:280px; width: 20px;">
        2.3
    </div>
</div>
.container {
    padding: 0.5rem 0 1.5rem 0;
    background-color: #EEE;
    border: 1px solid #888;
    overflow-x: auto;
}

.item {
    height: 2rem;
    margin-bottom: -1rem;
    font-size: 60%; font-family: sans-serif;
    background-color: #BBB;
    border: 1pt solid #EEE;
    overflow: hidden;
}

这是当前结果(在这种情况下,事件名称是数字,但它们可以是任意字符串):

result

每行只有一个div,因此每个后续div都低于其前任。

任何想法如何使用纯CSS执行此操作而无需计算元素的y位置?

示例代码可在此处找到:http://jsfiddle.net/hiddenbit/PvBjt/

5 个答案:

答案 0 :(得分:0)

您可以修复每个元素上带有负上边距的垂直偏移。

http://jsfiddle.net/PvBjt/1/

margin-top:-##px;

答案 1 :(得分:0)

我已将您的.item更改为position: absolute;并给出了子项的类以及像素top值:

.item {
  position: absolute;
}
.sub-item {
   top:35px;
}
.sub-sub-item {
  top: 55px;
}

<强> Updated fiddle

答案 2 :(得分:0)

即使要求非常模糊,也要在环中投掷我的2个想法。

首次尝试

我为大多数元素添加了margin-top;等级x.x得到margin-top:20px,等级x.x.x获得margin-top:40px。与@YaMo相似的方法。

Demo

<强> HTML

<div class="container">
    <div class="item" style="margin-left:0; width: 200px;">1</div>
    <div class="item" style="margin-left:30px; margin-top:20px; width: 100px;">1.1</div>
    <div class="item" style="margin-left:40px; margin-top:40px; width: 50px;">1.1.1</div>
    <div class="item" style="margin-left:140px; margin-top:20px; width: 50px;">1.2</div>
    <div class="item" style="margin-left:205px; width: 100px;">2</div>
    <div class="item" style="margin-left:220px; margin-top:20px; width: 20px;">2.1</div>
    <div class="item" style="margin-left:250px; margin-top:20px; width: 20px;">2.2</div>
    <div class="item" style="margin-left:280px; margin-top:20px; width: 20px;">2.3</div>
</div>

<强> CSS

.item {
    position:absolute; /* just add to existing properties */
}

第二次尝试

元素的一些实际结构允许更清晰的HTML和更通用的CSS。我无法想出通用规则的唯一部分是项1.2的宽度。这当然取决于能否改变HTML结构。

Demo

<强> HTML

<div class="container">
    <ul>
        <li class="item">1
            <ul>
                <li class="item">1.1
                    <ul>
                        <li class="item">1.1.1</li>
                    </ul>
                </li>
                <li class="item">1.2</li>
            </ul>
        </li>
        <li class="item">2
            <ul>
                <li class="item">2.1</li>
                <li class="item">2.2</li>
                <li class="item">2.3</li>       
            </ul>
        </li>
    </ul>
</div>

<强> CSS

.container {
    padding: 0.5rem 0 1.5rem 0;
    background-color: #EEE;
    border: 1px solid #888;
}

.item {
    height:2rem;
    background-color: #BBB;
    border: 1pt solid #EEE;
    display:inline-block;
    vertical-align:top;
    min-width:20px;
}

li:only-child {
    min-width:50px;
}

ul {
    margin:0;
    padding:0;
    list-style-type:none;
}

.container > ul > li {
    display:inline-block;
    vertical-align:top;
    font-size: 60%; font-family: sans-serif;
}

ul li:first-child {
    margin-left:20px;
}

ul li:last-child {
    margin-right:10px;
}

答案 3 :(得分:0)

似乎无法仅使用CSS布局div。因此我使用JavaScript制作了以下解决方案。

遍历数据列表并动态添加div。使用绝对定位:

  • left设置为开始时间
  • for top堆栈数据结构用于跟踪隐式父项

<强> HTML:

<div id="container"></div>

<强> JavaScript的:

var data = [
    ["1",      0, 200 ],
    ["1.1",   30, 100 ],
    ["1.1.1", 40,  50 ],
    ["1.2",  140,  50 ],
    ["2",    205, 100 ],
    ["2.1",  220,  20 ],
    ["2.2",  250,  20 ],
    ["2.3",  280,  20 ]
];

var endStack = [];
var maxStackHeight = 0;
var container = document.getElementById("container");

data.forEach(function(row) {
    // create div
    var div = document.createElement("div");
    div.className = "item";
    div.appendChild(document.createTextNode(row[0]));
    var x = row[1]; var w = row[2];
    div.style.left = x.toString() + "px";
    div.style.width = w.toString() + "px";

    // get level of div
    for (var i = endStack.length-1; i >= 0; i--) {
        if (x > endStack[i]) {  
            // current event starts after current top-of-stack ends → remove
            endStack.pop();  
        } else {
            // current event is child of current top-of-stack event
            break;
        }
    }

    // set y position based on stack height
    div.style.top = (endStack.length).toString() + "rem";

    // append to container
    container.appendChild(div);

    // push end of added event to stack
    endStack.push(x + w);

    // set maximum stack height 
    maxStackHeight = Math.max(maxStackHeight, endStack.length);
});

// set container height based on maximum stack height 
container.style.height = (maxStackHeight + 1.1).toString() + "rem";

<强> CSS:

#container {
    background-color: #EEE;
    border: 0.1rem solid #888;
    overflow-x: auto;
    position: relative;
}

.item {
    position: absolute;
    height: 2rem;
    font-size: 60%; font-family: sans-serif;
    background-color: #BBB;
    border: 0.1rem solid #EEE;
    overflow: hidden;
}

Demo

答案 4 :(得分:0)

以下是使用my other answer和JavaScript中的CSS创建结构化<ul>事件的替代解决方案。

Demo

<强>的JavaScript

window.onload=function(){
  var events = [
    { name: '1', start: 0, length: 300},
    { name: '1.1', start: 30, length: 100},
    { name: '1.1.1', start: 40, length: 50},
    { name: '1.2', start: 140, length: 50},
    { name: '1.3', start: 200, length: 50},
    { name: '2', start: 305, length: 100},
    { name: '2.1', start: 320, length: 20},
    { name: '2.2', start: 350, length: 20},
    { name: '2.3', start: 380, length: 20},
    { name: '3', start: 410, length: 50}
  ];

  var html = [];
  var containerEventEnd = [];
  var prevEventEnd = Infinity;

  for (var i = 0; i < events.length; i++) {
    var e = events[i];
    var eventEnd = e.start + e.length;

    if (e.start > containerEventEnd[1]) {
      containerEventEnd.shift();
      html.push('</ul>');
    }

    if (eventEnd > containerEventEnd[0]) {
      containerEventEnd.shift();
      containerEventEnd.unshift(eventEnd);
    }

    if (e.start > prevEventEnd) {
      html.push('</li>');
    } else {
      html.push('<ul>');
      containerEventEnd.unshift(eventEnd);
    }

    html.push('<li class="item">' + e.name); // every item is just an <li>
    prevEventEnd = eventEnd;
  }

  document.getElementById('container').innerHTML = html.join('\n');
};

<强> CSS

#container {
    padding: 0.5rem 0 1.5rem 0;
    background-color: #EEE;
    border: 1px solid #888;
}

.item {
    height:2rem;
    background-color: #BBB;
    border: 1pt solid #EEE;
    display:inline-block;
    vertical-align:top;
    min-width:20px;
}

li:only-child {
    min-width:50px;
}

ul {
    margin:0;
    padding:0;
    list-style-type:none;
}

#container > ul > li {
    display:inline-block;
    vertical-align:top;
    font-size: 60%; font-family: sans-serif;
}

ul li:first-child {
    margin-left:20px;
}

ul li:last-child {
    margin-right:10px;
}

HTML

<div id="container"></div>