将绝对布局转换为使用浮动布局

23

我正在进行的一个项目需要一些建议,非常感谢您的任何帮助。

目标:

制作一个拖放式CMS,允许用户在网格上绘制元素并将它们移动到所需位置。更改以JSON格式记录,并在用户按发布按钮时转换为HTML/CSS。生成的HTML应该干净且灵活(即适用于高度/长度变化的内容)。该系统应能够处理创建电子商务网站以及简单信息网站。

问题:

在HTML中实现拖放系统的逻辑方法是使用设置了宽度和高度的absolute定位;但这种方法不适用于最终的网站,因为内容可能是可变长度的,而绝对定位的元素会脱离文档流,不知道周围的元素。

解决方案:

创建一个系统,将绝对定位的元素转换为floated元素。

例子:

在CMS系统中,用户通过在网格上绘制盒子来创建以下布局:

  1. 固定高度的标题
  2. 可变高度的导航
  3. 固定高度的图像
  4. 可变高度的主要内容页面
  5. 可变高度的已访问项目列表
  6. 固定高度的页脚

绝对布局:

Absolute layout

HTML/CSS如下:

body {
    background-color: #999999;
    font-family: verdana, arial, helvetica, sans-serif;
    font-size: 70%;
    margin: 15px 0;
    padding: 0;
}
#mainContainer {
    background-color: #FFFFFF;
    height: 500px;
    margin: 0 auto;
    position: relative;
    width: 916px;
}
.contentBlock {
    border: 1px solid orange;
    box-sizing: border-box;
    color: orange;
    font-size: 2em;
    text-align: center;
}
.contentBlock:after {
    content: "";
    display: inline-block;
    height: 100%;
    vertical-align: middle;
}
#contentBlock1 {
    height: 120px;
    left: 0;
    position: absolute;
    top: 0;
    width: 916px;
}
#contentBlock2 {
    height: 100px;
    left: 0;
    position: absolute;
    top: 140px;
    width: 136px;
}
#contentBlock3 {
    height: 100px;
    left: 0;
    position: absolute;
    top: 260px;
    width: 136px;
}
#contentBlock4 {
    height: 220px;
    left: 156px;
    position: absolute;
    top: 140px;
    width: 604px;
}
#contentBlock5 {
    height: 220px;
    left: 780px;
    position: absolute;
    top: 140px;
    width: 136px;
}
#contentBlock6 {
    height: 120px;
    left: 0;
    position: absolute;
    top: 380px;
    width: 916px;
}
<div id="mainContainer">
    <div class="contentBlock" id="contentBlock1">1</div>
    <div class="contentBlock" id="contentBlock2">2</div>
    <div class="contentBlock" id="contentBlock3">3</div>
    <div class="contentBlock" id="contentBlock4">4</div>
    <div class="contentBlock" id="contentBlock5">5</div>
    <div class="contentBlock" id="contentBlock6">6</div>
</div>

用户现在点击发布按钮,布局将转换为使用float而不是absolute定位。由于如果2或4中的内容扩展,它们将超过/在3和6上方/下方,因此结果HTML不能使用absolute定位。 Float使元素保持在流中并相互感知,因此以下内容适用于2和4的动态内容: 浮动布局: Floated layout HTML / CSS可能如下所示:

body {
    background-color: #999999;
    font-family: verdana, arial, helvetica, sans-serif;
    font-size: 70%;
    margin: 15px 0;
    padding: 0;
}
#mainContainer {
    background-color: #FFFFFF;
    margin: 0 auto;
    width: 916px;
}
.contentBlock {
    border: 1px solid orange;
    box-sizing: border-box;
    color: orange;
    font-size: 2em;
    text-align: center;
}
.contentBlock:after {
    content: "";
    display: inline-block;
    height: 100%;
    vertical-align: middle;
}
#contentBlock1 {
    margin-bottom: 20px;
    height: 120px;
}
#contentBlock2 {
    height: 100px;
    margin-bottom: 20px;
    width: 136px;
}
#contentBlock3 {
    height: 100px;
    margin-bottom: 20px;
    width: 136px;
}
#contentBlock4 {
    float: left;
    height: 220px;
    margin-bottom: 20px;
    margin-left: 20px;
    width: 604px;
}
#contentBlock5 {
    float: left;
    height: 220px;
    margin-bottom: 20px;
    margin-left: 20px;
    width: 136px;
}
#contentBlock6 {
    clear: left;
    height: 120px;
}
#contentContainer1 {
    float: left;
    width: 136px;
}
<div id="mainContainer">
    <div class="contentBlock" id="contentBlock1">1</div>
    <div id="contentContainer1">
        <div class="contentBlock" id="contentBlock2">2</div>
        <div class="contentBlock" id="contentBlock3">3</div>
    </div>
    <div class="contentBlock" id="contentBlock4">4</div>
    <div class="contentBlock" id="contentBlock5">5</div>
    <div class="contentBlock" id="contentBlock6">6</div>
</div>

用户不需要理解浮动元素的工作原理,所以这个过程在发布更改时需要自动进行。

这个例子非常简单,但更高级的布局也需要处理。

其他CMS系统的做法:

据我所知,大多数CMS系统要么将用户固定到一个设置好的模板中,要么使用JavaScript来设置绝对定位元素的高度/位置(我想避免这种方法)。

我的问题:

  • 是否有可能制定一组规则将绝对布局转换为浮动布局?
  • 如果有,是否有任何现有的CMS可以实现这样的功能?
  • 对于解决这个问题,有什么其他的建议吗?

谢谢。


1
你曾经想过使用flexbox吗?http://css-tricks.com/snippets/css/a-guide-to-flexbox/ - psdpainter
1
@Mark 虽然浏览器支持是一个问题,但 Flexbox 已被考虑。 - Hidden Hobbes
2
我知道在Dojo中有一个名为Maqetta的工具,可以做类似于这样(可视化编辑器)的事情。它们使用相对宽度和高度。请参见http://maqetta.org/ 注意该项目的活跃开发已经停止了几年。 - GuyT
1
@GuyT 差一点就成功了!我看到了流/绝对按钮,以为它正是我需要的,但不幸的是,它只允许你在新元素添加到布局时切换,而不能将现有的绝对布局主动转换为流布局。感谢您的建议。 - Hidden Hobbes
1
@HiddenHobbes 没错。它只能用于创建新接口,不支持将绝对布局转换为相对布局。但是您可以添加HTML并进行编辑/扩展。我认为这可能是继续的基础.. ;) 祝你好运! - GuyT
显示剩余6条评论
7个回答

6

第一点:我认为将绝对布局转换为浮动布局不是最好的方法。你应该从一开始就考虑浮动布局,并提示/教用户根据此构建布局。

第二点:我认为你想要构建的工具需要用户学习如何使用它。这意味着你需要让它足够简单,以至于不熟悉HTML/CSS概念的人也能使用它。因此,你需要基于简单易懂的概念来使用该工具,使用户可以构建外观,然后你在其后生成代码。

我能想到的概念有:

  • 块/容器
  • 高度/宽度
  • 外边距/内边距

现在,你可以使用这些概念来使用户能够创建具有以下属性的块:内容宽度/高度、外边距/内边距的列数。然后,用户可以使用这些属性无限嵌套块并在其中拖放内容。

以下是在示例中的操作步骤:

页眉:

用户创建一个块并指定以下属性:

  • width:100%
  • height:80px (可以通过拖动元素边框来完成)
  • 2列(一个用于标志,一个用于菜单)

主体:

用户在页眉下创建一个新的块,并指定以下属性:

  • width:100%
  • 高度自适应
  • 3列(col 1和3:15%宽度,col 2:70%宽度)

页脚

新的块与页眉具有相同的属性。

用户可以在每个块/列内部再次开始,并使用相同的属性嵌套新的块/列

生成代码:

你知道每个块的列数及其宽度,因此可以轻松地为每个块创建div,并使用浮动/宽度将它们并排摆放。至于高度:用户可以设置固定高度,他不难理解内容可能会溢出。因此,如果他不想要这种情况,他必须选择“自适应”高度(height:auto;css中)。

结论:

这是一个非常普遍和简单的概念。主要的工作将在UI设计和提示/教用户使用你的工具来构建布局的方式上。谨记你为谁设计以及他们在不同情况下会如何反应。使用你最好的UI技能来提示/教导用户走向正确方向。


1
谢谢你的建议,你提出了一些有趣的想法,可以从不同的角度来解决这个问题。 - Hidden Hobbes

4

有一个名为Weebly的网站创建工具,具有您寻找的相同功能。它是免费的,所以您可以查看了解更多其特性。

您提出的问题非常模糊,因此我将答案分成几个部分:

1- 对于拖放功能:

这正是您要找的: Gridster

您可以让用户在保持约束的同时调整框的大小。

2- 如果您正在寻找干净的CSS框架:

3- 如果您正在寻找覆盖整个页面(垂直和水平)的流体布局:

html,
body {height:100%; margin:0px; padding:0px;}
.wrapper {position:relative; height:100%; width:100%; display:block;}
.header {position:relative; height:22%; width:100%; display:inline-block; margin-bottom:3%; background:red;}
.footer {position:relative; height:22%; width:100%; display:inline-block; margin-top:3%; background:green;}
.content {position:relative; height:50%; width:100%; display:inline-block;}
.content .left_sidebar {float:left; width:17%; height:100%; position:relative; margin-right:3%; background:yellow;}
.content .right_sidebar {float:right; width:17%; height:100%; position:relative; margin-left:3%; background:purple;}
.content .middle {float:left; width:60%; height:100%; position:relative; background:cyan;}

/**
 * @info Clearfix: clear all the floated elements
 */
.clearfix:after {visibility:hidden; display:block; font-size:0; content:" "; clear:both; height:0;}
.clearfix {display:inline-table;}
 
/**
 * @hack Display the Clearfix as a block element
 * @hackfor Every browser except IE for Macintosh
 */
   /* Hides from IE-mac \*/
   * html .clearfix {height:1%;}
   .clearfix {display:block;}
   /* End hide from IE-mac */
<div class="wrapper">
    <div class="header">Header</div>
    <div class="content">
        <div class="left_sidebar">Left Sidebar</div>
        <div class="middle">Middle</div>
        <div class="right_sidebar">Right Sidebar</div>
        <div class="clearfix"></div>
    </div>
    <div class="footer">Footer</div>
</div>

请记住,这样做将破坏移动设备上的用户体验。

感谢您抽出时间回答。我之前看过Weebly,但它是一种将您绑定到页面模板的CMS之一,我的目标是试图摆脱这种限制,让用户制作自己的模板。我之前也看过Gridster,虽然它适用于绝对定位元素,但我需要一种将结果HTML转换为浮动布局的方法。您说我的问题不够清晰,有什么我可以做来使它更加明确吗? - Hidden Hobbes
我的意思是,“含糊不清”,你需要更详细地解释一下,你是需要使用JS实现拖放功能,还是使用CSS布局浮动元素。 - Wissam El-Kik
我并不是在寻找其中的任何一种! ;) 我已经开发了自己的拖放系统,它使用绝对定位 - 目前为止一切顺利 - 但是绝对定位对于最终网站来说并不好,因此我正在寻找一种将这种绝对布局转换为浮动布局的方法。我的问题的主要目的是找出是否可能以及是否已经在其他地方完成了这项工作。 - Hidden Hobbes
在我看来,你应该使用浮动布局,因为你可能需要适配移动设备。绝对定位真的很糟糕。所以首先,你应该修复你的拖放系统以构建浮动布局。如果你正在使用像jQuery UI Draggable/Droppable/Sortable这样的好的库,那么这实际上非常容易做到,而且你应该像任何体面的CSS Grid(Bootstrap、Foundation等)一样使用12列网格。你不会有任何问题将你的代码转换成为移动响应式设计,并且你也不会有关于你的浮动布局的任何问题。 - Wissam El-Kik

4
“是否可以制定一组规则将绝对布局转换为浮动布局?”“不是不可能,但实现起来非常困难。”“如果可以,是否有任何现有的CMS可以做到这一点?”“据我所知没有。”“对于解决此问题的其他建议?”“我觉得更容易将布局视为一堆行和列,适当地浮动。因此,对于此布局:(图片),我会生成类似于这个的HTML标记(为了理解而简化):”
<div class="box">Content 1</div>
<div class="row">
    <div class="col">
        <div class="box">Content 2</div>
        <div class="box">Content 3</div>
    </div>
    <div class="col">
        <div class="box">Content 4</div>
    </div>
    <div class="col">
        <div class="box">Content 5</div>
    </div>
</div>
<div class="box">Content 6</div>

您需要提供一个用户界面,用户可以:
  1. 添加内容
  2. 添加列包装器
  3. 在列包装器内添加列
您可以对行、列和/或内容元素进行命名,并使用CSS调整它们的宽度。这里是一个概念验证:

$(function() {
    $(".insertable").draggable({ revert: "invalid" });
    $(".insertzone").droppable({ activeClass: "acceptable", drop: handleInsert, accept: ".insertable-box, .insertable-row" });
    $(".removezone").droppable({ activeClass: "acceptable", drop: handleRemove, accept: ".removable" });
    function handleInsert(event, ui) {
        ui.draggable.css({ left: 0, top: 0 });
        var $div = $("<div class='removable'></div>").appendTo(this).draggable({ revert: "invalid" });
        if (ui.draggable.hasClass("insertable-box")) {
            $div.addClass("box").text("Lorem ipsum dolor sit amet.");
        }
        if (ui.draggable.hasClass("insertable-row")) {
            $div.addClass("row").droppable({ activeClass: "acceptable", drop: handleInsert, greedy: true, accept: ".insertable-col" }); ;
        }
        if (ui.draggable.hasClass("insertable-col")) {
            $div.addClass("col").addClass(ui.draggable.find("select").val()).droppable({ activeClass: "acceptable", drop: handleInsert, greedy: true, accept: ".insertable-box, .insertable-row" });
        }
    }
    function handleRemove(event, ui) {
        ui.draggable.remove();
    }
});
/* INTERFACE */
body { font: medium/1 monospace; }
select { font: inherit; margin: -1em 0; border: 0; padding: 0; }
.insertzone { margin: 1em 0; box-shadow: 0 0 .25em #CCC; }
.removezone { margin: 1em 0; box-shadow: 0 0 .25em #CCC; }
.insertable { cursor: move; display: inline-block; padding: 1em 4em; background-color: #CCC; }
.removable { cursor: move; }
.acceptable { background-color: #FEA !important; }
.insertzone .box { background-color: #EFD; }
.insertzone .row { background-color: #FEE; }
.insertzone .col { background-color: #FFD; }
.insertzone .box:after { display: block; padding: 1em; text-align: center; content: "box"; color: #CCC; margin-bottom: -1em; }
.insertzone .row:after { display: block; padding: 1em; text-align: center; content: "row"; color: #CCC; }
.insertzone .col:after { display: block; padding: 1em; text-align: center; content: "col"; color: #CCC; min-width: 8em; }
.insertzone:after { display: block; padding: 1em; text-align: center; content: "Drag here to insert"; }
.removezone:after { display: block; padding: 1em; text-align: center; content: "Drag here to remove"; }
/* LAYOUT */
.box { margin: 1em 0; padding: 1em; }
.row { margin: 1em 0; }
.row:after { display: block; clear: both; content: ""; }
.col { float: left; }
.col > * { margin-left: .5em; margin-right: .5em; }
.col:first-child > * { margin-left: 0; }
.col:last-child > * { margin-right: 0; }
.col > *:first-child { margin-top: 0; }
.col > *:last-child { margin-bottom: 0; }
.col-10 { width: 10%; }
.col-20 { width: 20%; }
.col-30 { width: 30%; }
.col-40 { width: 40%; }
.col-50 { width: 50%; }
.col-60 { width: 60%; }
.col-70 { width: 70%; }
.col-80 { width: 80%; }
.col-90 { width: 90%; }
<link rel="stylesheet" href="//code.jquery.com/ui/1.9.2/themes/smoothness/jquery-ui.css">
<script src="//code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="//code.jquery.com/ui/1.9.2/jquery-ui.min.js"></script>

<div class="insertzone"></div>
<div class="removezone"></div>
<div>
    <div class="insertable insertable-box">box</div>
    <div class="insertable insertable-row">row</div>
    <div class="insertable insertable-col">col
        <select>
            <option value="col-10">10%</option>
            <option value="col-20">20%</option>
            <option value="col-30">30%</option>
            <option value="col-40">40%</option>
            <option value="col-50" selected>50%</option>
            <option value="col-60">60%</option>
            <option value="col-70">70%</option>
            <option value="col-80">80%</option>
            <option value="col-90">90%</option>
        </select>
    </div>
</div>

这是使用这个工具设计的您的布局:


有趣的行使用,这种方法将有助于解决我目前正在进行的工作中遇到的一些问题。感谢您的建议。 - Hidden Hobbes
@HiddenHobbes:我已经添加了一个例子,请检查。 - Salman A

3
这是我想设计一个自动化系统的假代码:
(主要是伪代码)
  1. 获取用户屏幕上正在查看的window.widthwindow.height
  2. 使用简单的公式为每个元素计算百分比:

    var elWidth = (element.width / window.width) * 100
    var elHeight = (element.height / window.height) * 100

  3. 将所有非100%宽度的元素以display:inline-block;行内元素的方式呈现

如果您设计了一个良好的UI来提取每个嵌套DIV和一种“磁性”网格来对齐项目,则这应该是一个很好的起点。

你觉得呢?


display: inline-block; 是使用 float 的有效替代方案(http://jsfiddle.net/HiddenHobbes/5sd7p4qr/)。你提出的(网格和嵌套)实际上与我目前所掌握的非常相似,尽管我正在努力解决嵌套逻辑问题;当我认为我已经想出了一个好的规则集时,总是会发现另一种我没有考虑到的情况破坏了布局! - Hidden Hobbes
如果您在“设计阶段”中成功捕获了每个嵌套的DIV,那么使用display:inline-block进行布局就相对简单了(请注意margin-left: -4px)。 请查看更新后的fiddle:http://jsfiddle.net/5sd7p4qr/1/ - domokun

3
将绝对定位转换为浮动布局的问题在于,你会花费很多精力去做一些可能无法良好转换的事情。最好是从一开始就使用浮动元素。虽然你不希望用户理解浮动,但想想Microsoft Word中的图像工作方式——用户将图像拖到他们想要的位置,然后可以设置文本环绕它的方式。这与浮动元素并没有太大的区别,并且比尚未经过可能或可能不完全有效的翻译过程的东西更能准确地呈现最终结果。
例如:将一个元素拖到页面上,它占据100%的宽度。再将另一个元素拖到页面上,它会位于第一个元素下面并占据相同的宽度。然后,您更改两个元素的“wrap”样式,使它们向左浮动,页面更新以显示用户最终将获得的内容。虽然您牺牲了超级灵活的定位,但却获得了更好的用户体验。
总之,听起来你正在预测用户的需求,认为绝对定位是唯一足够灵活的解决方案,我的建议是:不要构建用户没有要求的功能。给他们一个可构建的拖放UI,如果他们需要更多功能,则在那时再解决。他们可能永远不会提出要求,而你将避免头痛。

也许你说得有道理,我一直在思考“我能做到这个吗?”而不是“我应该做这个吗?”我很想知道大家对此的普遍看法。 - Hidden Hobbes

3
  1. 看一看网格(不是数据网格)框架,有很多好的想法,也许你可以在不进行任何自定义的情况下使用其中一些。
  2. 再次考虑用户输入的绝对定位,但我认为它不适用于CMS。

谢谢建议,使用网格系统是个好点子,因为我决定使用960网格系统的修改版本。 - Hidden Hobbes

3
我认为你需要小心:根据你的描述,你试图使用绝对定位和大小构建一个所谓的所见即所得界面,最终却成了非所见即所得;它的宽度和位置会因屏幕大小而异。
你已经遇到了现代响应式设计的一些基本问题之一:网站不再是所见即所得了。几乎不可能用单个图像传达精心设计的移动站点的所有不同外观。
你的用户将不得不遵守与移动设计师相同的约束,即元素必须在不同的屏幕尺寸之间优雅地流动。这通常意味着将内容限制在网格或单一平面上(例如列或行),或者进行两次设计;一次为移动版,一次为桌面版。
话虽如此,我见过的少数几个与绝对定位和大小元素一起使用的网格系统之一是Masonry Grid。它可以接受任何大小的元素,并尝试最佳地拼合它们,而不会破坏页面的流程。它通常被用于杂志或作品集网站上,那里很少出现长篇写作内容。

有一个良好的规则系统和特殊元素,可以创建负空间(即全宽分隔线、'空'块、全高度侧栏),您可以为用户提供工具,使用Masonry Grid对一切进行重新排列,从而使网站看起来相当不错。


忘了提到我最喜欢的一个不常见的网格:对齐网格:http://justifygrid.com/。它使用文本: 对齐属性来无缝地对齐不同大小(但不是高度)的元素。如果您愿意限制用户在整个页面中使用相同高度的元素,那么对齐网格将使用户能够轻松调整元素的大小而不费吹灰之力。 - Ucinorn
我刚刚发现了这个网站:http://semantic.gs/ 看起来它旨在接受修复和百分比列宽。 - Ucinorn
感谢您的回答,将用户锁定在网格中似乎是正确的方法,因为它在保持一些结构的同时还允许用户灵活性。 - Hidden Hobbes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接