3

前置

本文需要对CSS,Vue,ElementUI有基本的了解。

本文以ElementUI提供的导航菜单组件为基础。

本文希望能在此组件基础上实现以下内容:

  1. 中间一段空白把导航栏分为左右两个部分
  2. 在导航栏上加上一个搜索框,但不被 el-menu-item 的样式污染。

先研究清楚ElementUI的css样式

创建一个简单的网页,在其中使用ElementUI的导航菜单组件。

用Chrome之类的工具对网页检查(F12/Ctrl+Shift+I)后,从 Elements 标签页中定位到导航菜单组件在网页中最后的实现元素。在检查工具下方有 Styles 工具会显示选中的元素的相关样式。提取其中对元素位置有影响以及我不太熟悉的属性。

el-menu

提取后发现有这些和元素位置相关的设置

margin: 0;
padding-left: 0;
margin-block-start: 1em;
margin-block-end: 1em;
margin-inline-start: 0px;
margin-inline-end: 0px;
padding-inline-start: 40px;

border-bottom: solid 1px #color;
border-right: none;

display:block;
position: relative;
-webkit-box-direction: normal;

list-style:none;

//使用了::before和::after创建了伪元素(注意,不是伪类。)
::before {
    display: table;
    content: "";
}

::after {
    display: table;
    content: "";
    clear: both;
}

对于出现在以上代码中的但不熟悉不确认作用的属性,以其为关键词(记得加引号以避免搜索引擎错误解析)加css进行搜索,基本上就能找到满意的答案。

margin-block-start 等属性的作用 —— 影响不大

::before 和 ::after —— 伪类
clear:both
搞明白了这里为什么要使用一个::after的伪元素,可以预测该元素下的子元素全都是浮动元素。因此如果放任不管的话,会造成浮动元素的高度不被父元素计算而导致父元素高度为零直接崩掉。所以使用这样一个伪类,由于这个伪类不能允许左右有浮动元素,所以它会使自己的上外边界碰着浮动元素(理想状况下应该排成一行)的那一行的下边界,而这一行的高度(也即浮动元素的高度)就会被计算为父元素的高度了。实际上如果这个伪类自己有高度比如60px,浮动元素那一行的最大高度为60px,则父元素的高度就会是120px了。

box-direction —— 影响不大

在解决了有问题的样式属性后发现,其实整个 el-menu 的最终实现很简单:使用positive:relative但并不带有left top 这一类设置的属性,所以说元素还是留在原地。这里设置positiverelative的理由应当是辅助之后的子元素即导航栏内部的 item 的定位(position:absolute时相对的元素若是position:static这一默认值是会被无视的)。

el-menu-item

float: left;
position: relative;
display: list-item;

margin: 0;
padding: 0 20px;
height: 60px;
line-height: 60px;
font-size:14px;
box-sizing: border-box;
text-align: -webkit-match-parent;

white-space: nowrap;
cursor: pointer;

float:leftdisplay:list-item同时使用第一时间可能想不明白,但经过自己创建更简单的样例进行测试,可以这么理解:float:left先执行确定下自己的位置,然后执行position:relative,即relative的相对定位相对的“原本的位置”是float:left之后的位置。而之类由于又没有设置top left 之类的属性,可以认为这是为了在其内部再放其它组件的话辅助定位用的。

display: list-item —— 从官方文档来看意思就是说配置这个list-item这个display属性的元素表现出<ul>的子元素<li>的样式,从而可以为它配置list-style,并且这是在没有<ul>这种display的父元素的基础上的。实际上它的父元素也确实是display:block

white-space —— white-space CSS 属性是用来设置如何处理元素中的空白。

cursor —— cursor 属性规定要显示的光标的类型(形状)。

那么最基本的两个组件最终生成的元素的CSS已经分析完了。动手试试我们一开始的目标吧。

目标

  1. 中间一段空白把导航栏分为左右两个部分
  2. 在导航栏上加上一个搜索框,但不被 el-menu-item 的样式污染。

目标1

我的第一个想法是创建一个 el-menu-item 给它宽度但是不给它内容同时让它disable="true"

<el-menu-item index="1" v-show="!logined">淘宝网</el-menu-item>
<el-menu-item index="" disabled="true" id="el-menu-item-placeholder1"></el-menu-item>
<el-menu-item index="2">登录</el-menu-item>
<el-menu-item index="3">注册</el-menu-item>

#el-menu-item-placeholder1 {
    width: 80%;
}

在此状态下创建的导航栏中间确实有了很大一块空白作为区隔,但仍然有如下问题:

  • 鼠标悬停在空白上时因为:disabled="true"导致鼠标会变成一个禁止通行的图标。
  • 页面缩小到1000px左右时就已经会让右边的“登录”“注册”新开一行,把整个布局弄得很糟糕。

对于第一个问题,可以通过cursor属性解决?设置cursor: default;于我们自定义的作为占位符的空白格 el-menu-item 样式中,就解决了。

对于第二个问题,三个组件总共宽度为218px,占据20%的宽度,则要求100%宽度大于1090px才会不崩盘。所以可以设置 el-menu 的类的样式解决这个问题

.el-menu {
    min-width: 1090px;
}
#el-menu-item-placeholder1 {
    width: 80%;
    max-width: 80%;
    cursor: default;
}

目标2

在解决目标1的基础上,其实已经有了目标2的思路:设置一个:disable="true"的 el-menu-item 来放置一个 el-input 输入框。对这个 el-menu-item 标记 id 属性,然后通过id选择器设置cursor:default

实际测试时发现整个输入框被导航栏的背景色给灰蒙蒙地上了一层。显然这一副作用是由于将起禁用而导致的。(对比不禁用时的输入框很正常)。所以用检查工具查看此时的元素,发现其class属性多了一个is-disabled,从而引入了 ElementUI 自带的样式中的 opacity: 0.25 才导致的。那通过id选择器进一步将其覆盖即可。

最终代码如下:

<el-menu-item index="1" v-show="!logined">淘宝网</el-menu-item>
<el-menu-item :disabled="true" id="el-menu-item-placeholder1"></el-menu-item>
<el-menu-item id="el-menu-item-searchinput-container" :disabled="true">
    <el-input v-model="input" placeholder="搜索栏"></el-input>
</el-menu-item>

<el-menu-item index="2">登录</el-menu-item>
<el-menu-item index="3">注册</el-menu-item>

.el-menu {
    min-width: 1090px;
}
#el-menu-item-placeholder1 {
    width: 80%;
    max-width: 80%;
    cursor: default;
}
#el-menu-item-searchinput-container {
    cursor: default;
    opacity: 1;
}

最初的两个目标基本都实现了。

最后

存一下自己写的东西

<template>
  <el-menu
    :default-active="activeIndex"
    class="el-menu-demo"
    mode="horizontal"
    background-color="#6e2142"
    text-color="#ffd692"
    active-text-color="#ffd692"
    @select="handleSelect"
  >
    <el-menu-item index="1" v-show="!logined">淘宝网</el-menu-item>
    <el-menu-item :disabled="true" id="el-menu-item-placeholder1"></el-menu-item>
    <el-menu-item id="el-menu-item-searchinput-container" :disabled="true">
        <el-input v-model="input" placeholder="搜索栏"></el-input>
    </el-menu-item>

    <el-menu-item index="2">登录</el-menu-item>
    <el-menu-item index="3">注册</el-menu-item>


  </el-menu>
</template>

<script>
export default {
  data() {
    return {
      activeIndex: "1",
      logined: false,
      input: null,
    };
  },
  methods: {
    handleSelect(key, keyPath) {
      console.log(key, keyPath);
    }
  }
};
</script>

<style scoped>
    .el-menu {
        min-width: 800px;
        padding-left: 20px;
    }
    #el-menu-item-placeholder1 {
        width: 60%;
        max-width: 60%;
        cursor: default;
    }
    .el-menu > .el-menu-item {
        min-width: 5%;
        padding: 0 5px;
        box-sizing: content-box;
        text-align: center;
    }

    #el-menu-item-searchinput-container {
        cursor: default;
        opacity: 1;
        margin: 0 20px;
    }

</style>

经过这次经历,我算是有点理解到是人在用组件,而不是组件在用人。组件虽然说是一个个已经封装好的成熟的,但绝大部分样式最终实现还是依靠CSS。因此如果能抓住其最终实现的CSS样式进行解析,就能自己进一步自定义化组件了。

分析组件时要注意样式变化时对应元素的前后变化,比如被 disabled 的 el-menu-item 使得白白净净很好看的内部的 el-input 蒙上了一层背景色,这个时候首先看 el-menu-item 是否多出和 disabled 这件事有关的样式,然后去看 el-input 继承的样式而不必关心原本的样式,因为原本的表现是正常的。

是人用组件,而不是组件用人。


阳光号
129 声望5 粉丝