position
position 指定一个元素在文档中的定位方式,使元素脱离当前的文档流,可以自由地在一定区域内移动。如果上级元素没有 relative,就以窗口作为定位范围,如果上级中有一个元素是 relative,就以它的范围作为最大的定位区域(子绝父相)。
display
display 设置块或者内联元素以及子元素的布局,例如 flex 布局、grid 布局。display 不会让元素脱离文档流,而是改变文档流中元素的排列方式。
float
float 指定一个元素应沿其父元素的左侧或右侧放置,允许该父元素内其他元素,如文本和内联元素环绕它。
简单示例
下面是使用了 float: left
的效果,最直观的是“编辑”和“回复”按钮在同一水平上:
下面是没有使用浮动属性的效果,“编辑”和“回复”不在同一水平上:
“编辑”和“回复”按钮不在同一个容器下,而是各自处于一个容器中。编辑和回复两个业务可以独立分开,所以编辑了两个组件,但因为按钮不在同一个容器中,就有 GIF2 中上下排序的效果:
要强制性让这两个不处于同一个容器里的按钮在同一个水平上,虽然 position 可以做到,但比较难处理定位,而且绝对定位会让元素不占有任何位置,好像是浮在上面的一层。float 虽然也脱离文档流,但是在它最终的位置上还是占据着位置:
单纯地脱离文档流还不行,需要清除浮动,不能让它有流动性,也就是不能让下一个元素围绕着上一个元素:
.clear::after {
content: "";
height: 0;
clear: both;
display: block;
visibility: hidden;
}
在结束浮动的节点中添加上面的 clear 就可以取消浮动效果:
完整代码:
<style>
.left {
float: left;
}
.right {
float: right;
}
.clear::after {
content: "";
height: 0;
clear: both;
display: block;
visibility: hidden;
}
.mb-2 {
margin-bottom: 10px;
}
</style>
<body>
<div id="app">
<comment class="mb-2" v-for="(item, index) in listing" :key="index" :item="item" />
</div>
</body>
<script>
Vue.component("comment", {
props: ["item"],
template: `
<div class="clear">
<div class="text">{{ item.text }}</div>
<div>
<edit-comment :item='item' />
<repl-comment :item='item' />
</div>
</div>
`
});
Vue.component("edit-comment", {
props: ["item"],
methods: {
edit() {
this.item.isEdit = !this.item.isEdit;
}
},
template: `
<div class='left'>
<textarea v-show="!item.isRepl && item.isEdit"></textarea>
<button v-show="!item.isRepl && !item.isEdit" @click='edit'>编辑</button>
<div v-show="!item.isRepl && item.isEdit">
<button @click='item.isEdit = !item.isEdit'>取消编辑</button>
<button @click='item.isEdit = !item.isEdit'>完成编辑</button>
</div>
</div>
`
});
Vue.component("repl-comment", {
props: ["item"],
methods: {
repl() {
this.item.isRepl = !this.item.isRepl;
}
},
template: `
<div class='left'>
<textarea v-show="item.isRepl && !item.isEdit"></textarea>
<button v-show="!item.isRepl && !item.isEdit" @click='repl'>回复</button>
<div v-show="item.isRepl && !item.isEdit">
<button @click='item.isRepl = !item.isRepl'>取消回复</button>
<button @click='item.isRepl = !item.isRepl'>完成回复</button>
</div>
</div>
`
});
const app = new Vue({
el: "#app",
data() {
return {
listing: [
{
isRepl: false,
isEdit: false,
text: "Hello1"
},
{
isRepl: false,
isEdit: false,
text: "Hello2"
},
{
isRepl: false,
isEdit: false,
text: "Hello3"
}
]
};
}
});
</script>
示例的意义
示例中把回复和编辑写作组件,效果上差点意思。这样做的目的是使得两个再逻辑上独立,本来回复和编辑都要走不同的 API,在同一个组件里写代码会变多,这应该属于模块化思想,具体可以阅读:重新思考 Vue 组件的定义。