在移动端开发中,我们往往需要按照设计稿去高度还原效果图,并且实现交互方式,下面看一下一个标签的表现形式: select
在iOS中的表现:
但是在安卓上并不是这样的效果
交互表现得差异性需要优化,那么问题来了,怎么优化。如何去保持同一标签可以表现相同得样式和交互方式。其实这也是前端的重要工作之一:兼容性和适配,也对应着招聘的要求之一:高度还原设计稿;言归正传,看到select不同的表现。我们如何实现这个替换呢?用过element-ui,iview等框架的小伙伴都知道,他们为了保持统一的表现,放弃了select,option;而是封装了自己的el-select 或者Select;
以上框架都实现了select的封装,但是在开放中,我们如果没有使用它们自身的框架,而是我们项目本身的框架,用起来很是费劲;大家都知道前端开发现在最火的是组件化开发,组件就是组件,可以快速移植和安装;最重要的是可以差异化这个组件,而且你也便于维护;安装其中的某个组件,你可能要花费30分钟去修改配置和调试,但是极有可能不会成功,这是很痛心、很扎心;
那么推荐下面一个最简单的替换组件,
这个组件昨天发布,对没有错就是我写的,支持 npm install yh-select ,目前只支持vue, 安装完成后,select option ,可以用一行标签搞定了,详情请看yh-select;(如果你想一起组队开发组件,那么请加微信xingguangbi,我们一起发布自己的组件)
修复不同的表现方式和交互方式,需要我们去替换产生差异的一些标签,用相同的标签去组装我们想要的效果:
下面分享一下我替换select的思路:
ul>li存放option的每一列,外面再来一个div包裹它,下拉的箭头实现,背景色去填充,不使用图片和字体图标,好处:尽量减少组件的依赖性,减少请求;
一个点击外部的指令
const clickoutsideContext = "@@clickoutsideContext"; export default { /* @param el 指令所绑定的元素 @param binding {Object} @param vnode vue编译生成的虚拟节点 */ bind(el, binding, vnode) { const documentHandler = function(e) { if (!vnode.context || el.contains(e.target)) { return false; } if (binding.expression) { vnode.context[el[clickoutsideContext].methodName](e); } else { el[clickoutsideContext].bindingFn(e); } }; el[clickoutsideContext] = { documentHandler, methodName: binding.expression, bindingFn: binding.value }; setTimeout(() => { document.addEventListener("click", documentHandler); }, 0); }, update(el, binding) { el[clickoutsideContext].methodName = binding.expression; el[clickoutsideContext].bindingFn = binding.value; }, unbind(el) { document.removeEventListener( "click", el[clickoutsideContext].documentHandler ); } };
核心组件如下:
<template> <div v-clickoutside="handleClose" class="select-wrapper" @click.prevent="selF" :class="{'active':selFlag}" :style="{'z-index':min}" > <div class="inners">{{models[names]}}</div> <div class="arrow" @click.stop="selF"></div> <ul v-show="selFlag"> <li :class="{'active':models.value==item.value}" v-for="(item,index) in options" :key="index" :value="item[val]" @click.stop="sels(item)" >{{item[names]}}</li> </ul> </div> </template> <script> import clickoutside from "./close"; export default { directives: { clickoutside }, model: { prop: "sel", event: "change" }, props: ["options", "sel", "val", "names"], data() { return { selFlag: false, models: "", min: 0 }; }, methods: { selF() { this.min = 100; this.selFlag = !this.selFlag; }, init() { this.models = this.sel; }, sels(item) { this.models = item; this.selFlag = !this.selFlag; this.$emit("change", item); }, handleClose() { this.min = 0; this.selFlag = false; } }, mounted() { this.init(); } }; </script> <style scoped> .select-wrapper { 120px; border: 1px solid #999; padding: 2px 30px 0 5px; font-size: 12px; height: 18px; position: relative; z-index: -1; } .inners { padding-right: 30px; overflow: hidden; } .select-wrapper .arrow { position: absolute; top: 50%; right: 10px; z-index: 30; transform: translateY(-50%); border-top: 5px solid #999; border-left: 5px solid transparent; border-right: 5px solid transparent; } .active { box-shadow: 0 0 3px #1e90ff; } .select-wrapper ul { list-style: none; margin: 0; padding: 0; fit-content; text-align: left; position: absolute; max-height: 160px; overflow-y: auto; bottom: 0; transform: translateY(101%); left: 0; z-index: 10; background: #fff; border: 1px solid #1e90ff; min- 100%; } .select-wrapper li { padding: 0 45px 0 0; background: #fff; } .select-wrapper li:hover { background: #1e90ff; color: #fff; } .select-wrapper li.active { background: #1e90ff; color: #fff; } </style>
源码地址:我要下载源码 (如果喜欢,请记得star一下);
最后提供一个问题解答的机会,交流的微信群,可以一起交流学习,如果你想进入请加微信:xingguangbi(备注交流解答);