关于微信小程序的一些想法
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
img
README.md

README.md

小程序项目随笔, 记录一下一些想法, 遇到的问题和遗忘的知识


Markdown基本语法
实用帖 | 如何为 Markdown 文件自动生成目录?


1. 开发工具

本人使用的是vsCode编写小程序的, 安装Vetur和minapp插件以支持微信小程序. (vsCode的less编译真好用).

2. 小程序的写法

首先嘛, 小程序和vue.js非常相似, 但也不尽相同, 本人有使用vue的经验, 并没有十分仔细的完整阅读微信小程序的文档, 以至于有很对问题明明文档里有直接的解决办法, 我还在抓破脑袋的瞎想. 幸亏我老哥也是做小程序的, 每次问他都是拿文档贴我脸上, 嘿嘿嘿.

3. 电池栏的适配

当ui不想使用微信自带的导航栏时, 自己写就得做电池栏的适配了.
这个statusBarHeight就是电池栏的高度了, 注意单位是px, 往里写rpx的时候要*2. 微信小程序的rpx单位是以苹果6 dpr2为基准算的, 会根据机型自动调整, 暂时未发现有什么大问题.

wx.getSystemInfo({
  success: function (res) {  
    if (res.statusBarHeight == undefined) {  
      wx.setStorageSync('BGGetSystem',res.statusBarHeight);
    } else {  
      wx.setStorageSync('BGGetSystem',res.statusBarHeight);
    }
  }
})  

4. 小程序里的data

微信小程序里读取是this.data.$name, 存储是this.setData({$name: '$data'}).
vue里读取和存储都是this.$name,
react里读取是this.state.$name, 存储是this.setState({$name: '$data'}).
另外有一点儿要注意的是, 当你想写一个公共方法, 需要传入setData的键名时, 直接将参数放在键的位置是不行的, 你需要加一个中括号. 记得把调用页面的this一起穿.

this.setData({
  [$name]: $value
})

如果你有一个

data: {
  list: {
    list1: [1],
    list2: [2],
    list3: [3],
  }
},

想只给list1赋值怎么办?加个引号即可

this.setData({
  'list.list1': [1, 2, 3]
})
data: {
  _list: [1, 2, 3]
}
this.setData({
  '_list[0]': [1, 2, 3]
})

小程序赋值代码片段

5. 关于if和hidden的条件渲染

微信小程序里的wx:ifhidden是同vue里的v-ifv-show类似.
wx:ifv-if完全相同, 是控制是否加载这个元素的.
hiddenv-show类似, 只是简单的控制元素display值, 所以当你对此元素有display上设置的话, hidden就无效了.
另外需要注意的是, hidden值为真是隐藏, v-show值为真是显示.
react里的条件渲染是在render时, 直接通过js控制的.
微信有一个神奇的标签<block></block>, 他是一个空标签, 主要用在wx:if, hidden, wx:for上. 在渲染的时候就不需要多一个<view></view>标签了.

6. 关于for循环渲染

微信小程序的wx:forv-for类似.
值得注意的是, 微信小程序默认的循环value是item, 下标是index, 可以直接使用;
而vue里需要你自己指定value和下标.

微信小程序

<view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName">
  {{idx}}: {{itemName.message}}
</view>

vue

<ul id="example-2">
  <li v-for="(item, index) in items">
    {{ parentMessage }} - {{ index }} - {{ item.message }}
  </li>
</ul>

react

<ul>
  {
    this.state.list.map(function (item, i) {
      return (
        <li key={i}>
          <div>{item.value}</div>
        </li>
    )})
  }
</ul>

7. 元素上绑定方法, 以及绑定data

在这一点上微信小程序和vue的差别就已经不小了.
在代码中可以看见, 小程序绑定方法使用的是bind:tap(bindtap),
而vue里是v-on:click="doSomething"(@click="doSomething").
在微信小程序里, 你需要data-$name, 然后在js里通过e来获取. 这时有一点儿要注意, 获取的值要在currentTarget里找, 不要使用其他的键名, 虽然都可一看到有一样的键值对, 但是会出现各种问题.

  • 注意啦currentTarget.dataset.$name里, 他的$name并不是你写在元素里的名字, 会全部变成小写字母.

vue就是普通的函数参数啦, 没什么好说的.
至于react, html+js, 妥妥的.

微信小程序

<view class="thinkCard" wx:for="{{investmentList}}" wx:key="{{item.investmentId}}" data-investmentId="{{item.investmentId}}" bind:tap="goThinkDetail">
  ***
</view>
goThinkDetail: function (e) {
  let investmentId = e.currentTarget.dataset.investmentid;
},

vue

<ul>  
  <li v-for="i, index in data.content" @click="captionClick(index)"> 
    <img class="yuan" src="../static/yuan.png"/>  
    {{ i.caption }}  
  </li>  
</ul>

react

<button onClick={this.handleClick}>按钮</button>

8. 关于微信里的js语法

微信小程序已经可以很好的支持es6(es2015), 多多使用es6的语法吧.
promise可以帮你解决好多麻烦的问题.
但是微信暂时没有支持es7及以上. 有些麻烦的异步问题非常想用asyncawiat解决怎么办?

微信小程序开发用 JS ES8 async/await 解决异步调用

regenerator-runtime 引入, 直接就可以使用asyncawiat啦! 除了在哪里用就都要引入之外, 暂时没什么缺点.
需要注意的是, 虽然本人使用async/await时没有出现过问题. 但是在使用es6 forof的时候出现过莫名其妙的解析失败, 将其换成forin之后就可以正常使用. 有此前车之鉴, 我的想法是, 尽量少用?

9. wx.request

我们的小程序其中有一个获取11位秘钥的接口, 直接获取发现json格式自动截取了10位. 这种情况下, 给request设置一下接受格式为text, 就可以解决了.

wx.request({
  url: "https://*",
  dataType: 'text',
  responseType: 'text',
  header: { 'content-type': 'text/html; charset=utf-8' },
  success: function (res) {
    console.log('factor', res);
  }
});

10. 倒计时

看这里!

微信小程序---完整的验证码获取倒计时效果 ---根据手机号是否符合要求进行判断

倒计时的原理就是通过一个计时器改变data状态, 然后渲染到页面里. 根据自己的具体需求, 加些验证, 改变提示文字即可.

11. 事件

事件分类

类型 触发条件 最低版本
touchstart 手指触摸动作开始
touchmove 手指触摸后移动
touchcancel 手指触摸动作被打断,如来电提醒,弹窗
touchend 手指触摸动作结束
tap 手指触摸后马上离开
longpress 手指触摸后,超过350ms再离开,如果指定了事件回调函数并触发了这个事件,tap事件将不被触发 1.5.0
longtap 手指触摸后,超过350ms再离开(推荐使用longpress事件代替)
transitionend 会在 WXSS transition 或 wx.createAnimation 动画结束后触发
animationstart 会在一个 WXSS animation 动画开始时触发
animationiteration 会在一个 WXSS animation 一次迭代结束时触发
animationend 会在一个 WXSS animation 动画完成时触发
touchforcechange 在支持 3D Touch 的 iPhone 设备,重按时会触发 1.9.90

11.1. 事件绑定和冒泡

小程序的事件同样会有冒泡. 事件绑定的写法同组件的属性,以 key、value 的形式。

  • key 以bindcatch开头,然后跟上事件的类型,如bindtapcatchtouchstart。自基础库版本 1.5.0 起,在非原生组件中,bindcatch后可以紧跟一个冒号,其含义不变,如bind:tapcatch:touchstart
  • value 是一个字符串,需要在对应的 Page 中定义同名的函数。不然当触发事件的时候会报错。

bind事件绑定不会阻止冒泡事件向上冒泡,catch事件绑定可以阻止冒泡事件向上冒泡。

如在下边这个例子中,点击 inner view 会先后调用handleTap3handleTap2(因为tap事件会冒泡到 middle view,而 middle view 阻止了 tap 事件冒泡,不再向父节点传递),点击 middle view 会触发handleTap2,点击 outer view 会触发handleTap1

<view id="outer" bindtap="handleTap1">
  outer view
  <view id="middle" catchtap="handleTap2">
    middle view <view id="inner" bindtap="handleTap3"> inner view </view>
  </view>
</view>

11.2. 长按和单次点击功能不一样

如果是一个长按复制和单机跳转怎么办呢? 你会发现, 单纯的使用text组件的长按复制和bind:tap, 长按的时候会触发tap事件.

11.2.1. longpress

<view class="view" bind:longpress="copy" data-text='{{text}}' bind:tap="call">{{text}}</view>
call(e) {
  console.log('call',e);
},
//长按复制
copy(e) {
  var that = this;
  console.log('copy',e);
  let text = e.currentTarget.dataset.text
  wx.setClipboardData({
    data: text,
    success: function (res) {
      wx.showToast({
        title: '复制成功',
      });
    }
  });
},

此时长按只会触发copy事件, 不会触发call.

11.2.2. bindtouchstart和bindtouchend

属性bindtouchstartbindtouchend出现了, 是触碰开始和触碰结束, 判断开始到结束的时间, 来触发不同的方法即可.

<view data-text="{text}" bindtouchstart="mytouchstart" bindtouchend="mytouchend" bind:tap="tapEvent">
</view>
//按下事件开始  
mytouchstart: function (e) {
  let that = this;
  that.setData({
    touch_start: e.timeStamp
  })
},
//按下事件结束  
mytouchend: function (e) {
  let that = this;
  that.setData({
    touch_end: e.timeStamp
  })
},
//判断跳转还是复制文本
tapEvent: function (e) {
  let that = this;
  //触摸时间距离页面打开的毫秒数  
  var touchTime = that.data.touch_end - that.data.touch_start;
  let text = e.currentTarget.dataset.text
  //如果按下时间大于350为长按  
  if (touchTime > 350) {
    this.copy(text)
  } else {
    this.goTaskList()
  }
},
//长按复制
copy: function (text) {
  var that = this;
  wx.setClipboardData({
    data: text,
    success: function (res) {
      wx.showToast({
        title: '复制成功',
      });
    }
  });
},

12. input和button样式的大坑

微信小程序里, input和button有一大堆的默认样式, 相当坑. 当你需要高度的自定义一个按钮, 就会让你头疼. 其中的伪类样式有一个border边框.

button {
  margin-left: 0;
  margin-right: 0;
  padding-left: 0;
  padding-right: 0;
  box-sizing: content-box;
  background-color: #FFFFFF;
}
button::after {
  border: none;
}
input {
  min-height: 0;
  outline: none;
  border: none;
  list-style: none;
}

13. template模板与component组件

微信小程序template模板与component组件的区别和使用

13.1. template模板

WXML提供模板(template), 可以在模板中定义代码片段, 然后在不同的地方调用.
在一个wxml模板里, 给template起一个name, 这个name将会在调用模板时, 声明这个模板是谁(名字). 调用时使用一个<import src='..'></import>标签引入. 每个模板有一个单独的作用域, 在模板中使用的数据, 要通过data="{{$data}}传入, 否则无法调用.

子模板

<template name="msgItem">
  <view>
    <text> {{index}}: {{msg}} </text>
    <text> Time: {{time}} </text>
  </view>
</template>

父page引入

<import src="../../templates.wxml"></import>

父page使用

<template is="msgItem" data="{{...item}}"/>

向子模板传入item

Page({
  data: {
    item: {
      index: 0,
      msg: 'this is a template',
      time: '2016-09-15'
    }
  }
})

WXSS可以使用@import语句可以导入外联样式表,@import后跟需要导入的外联样式表的相对路径,用;表示语句结束. 而js, 就和平常一样引入就行了. 调用时记得传入this.

13.2. component组件

component组件的写法基本和page页面一样, 简单用法参考官方文档即可.
我就用到的地方说几句.

  • 子组件的wxss不会自动引入app.wxss的公共样式, 需要手动引入.
  • component组件里, 是有他自己的生命周期的, ready(){} 比较常用, 他是在组件生命周期函数,在组件布局完成后执行. 更多的生命周期参考文档.
  • 父子组件通信可就麻烦了, 像vue一样, 子组件不可以直接向父组件传值. 需要在父组件里向子组件绑定一个方法, 子组件在执行自己的方法之后, 调用这个方法, 通过this.triggerEvent('add-collect-project', myEventDetail)给父组件传值.
  • 绑定的方法和vue一样, 不支持驼峰写法, 要用 - 相连.

父组件wxml

<!-- 列表 -->
<my-projectList bind:add-collect="addCollectProject" list="{{hotList[hotProjectGroup]}}"></my-projectList>

父组件js

/**收藏
* @param  {} e
*/
addCollectProject: function (e) {
  console.log(e.detail.index);
},

字组件wxml

<!-- 列表 -->
<view data-index="{{index}}" bind:tap="collectProject">
  <image class="img" src="" />
</view>

子组件js

/**收藏
  * @param  {} e
  */
collectProject: function (e) {
  console.log(e);
  let index = e.currentTarget.dataset.index;
  var myEventDetail = { index }; // detail对象,提供给事件监听函数
  this.triggerEvent('add-collect', myEventDetail)
},  

14. input与textarea

微信小程序里的input和textarea是原生组件, 小程序里的原生组件层级是最高的, 不可被覆盖. 并且无法在 scroll-view、swiper、picker-view、movable-view 中使用. 会产生各种莫名其妙的问题, 例如在滚动时诡异的表现.

14.1. input

普通的 input 输入框, 看似没有这些问题, 那是小程序做了一定的处理. 大概的原理就是, 在非输入状态下, input 只是一个普通的元素, 点击的时候就会变成正常的 input .
这样解决了原生组件的样式问题, 但是却带来了一个新的bug, 聚焦和非聚焦状态下的 input 表现不一致, 切换时会出现文字跳动的问题, 这个bug暂时无解.
获取 input 文字内容有两种方法 bindinputbindblur . 在具体使用 input 时, bindblur有这样一个表现, 在 input 输入文字后, 直接点击 button 进行操作, bindblur 会不触发. 所以在登陆或者搜索等操作时, 还是使用 bindinput 获取吧.
input 有5种类型, 其中4种type和1个password.

type值

说明
text 文本输入键盘
number 数字输入键盘
idcard 身份证输入键盘
digit 带小数点的数字键盘

type可以直接改变键盘布局(大部分情况下).
passwordtrue时, 会将输入内容会表现为实心圆, 输入法会切换为英文状态(安卓)|英文输入(ios). 这时候要注意, 如果你更改password属性为false, 会变为普通的text类型, 这时候是可以输入中文的, 所以最好添加一个中文验证.

if (/[\u4e00-\u9fa5]/.test(PWNum)) {
    that.setData({
      PWFlag: false,
    });
    Tip('密码不能有中文');
  } 

confirm-type可以设置键盘右下角按钮的文字,仅在type='text'时生效.

confirm-type 有效值:

说明
send 右下角按钮为“发送”
search 右下角按钮为“搜索”
next 右下角按钮为“下一个”
go 右下角按钮为“前往”
done 右下角按钮为“完成”
  • 注:confirm-type的最终表现与手机输入法本身的实现有关,部分安卓系统输入法和第三方输入法可能不支持或不完全支持.

bindconfirm点击完成按钮时触发, 写法和bindtap相同, 可以接收一个e: event.detail = {value: value}.
微信小程序的placeholder样式通过placeholder-class属性, 接收一个class类名placeholder-style属性, 使用行内样式.
input还有value属性, 输入框的内容; disabled属性, 是否禁用等.

14.2. textarea

textarea多行文本输入的使用方法基本和input一致, 但是有一点, 小程序并没有对齐进行额外的处理, 所以在非聚焦情况下, textarea的层级永远最高, 还会出现诡异的滚动情况.

  • tip: 不建议在多行文本上对用户的输入进行修改,所以 textarea 的 bindinput 处理函数并不会将返回值反映到 textarea 上.

为了能有更好的表现效果, 在有滚动和遮罩的场景, 建议非聚焦情况下使用view做一个展示效果, 通过wx:if切换viewtextarea, 更改focus属性为true获取焦点.

14.3. css文字换行

提到了文字输入, 补充一下文字换行的css小知识.

你真的了解word-wrap和word-break的区别吗?

文字换行css有两个属性word-wrap: break-wordword-break: break-all.
简单来说, 当一个单词(特指英文单词)过长, 一行放不下后, 浏览器(包括微信小程序)会另起一行来放置这个单词. 然而, 当第二行还是放不下怎么办? 对不起, 单词就超出去啦!
word-wrap: break-word会使所有的'', 都带上超出换行的属性, 直到他不再超出. 并不会改变默认的另起一行效果.
word-break: break-all就是简单粗暴的, 以每一个字母来当做换行的标识, 这时候就不存在超出另起一行的表现了. 换句话来说就是, 已经没有单词这个概念了, 每一个字母就是一个单词.
word-break: break-all除了opera外,其他都支持.

14.4. 文字超出省略

微信小程序的文字超出省略就简单了, 他是webkit内核的, 直接使用常规的就OK.

// 一行文字省略
.ellipsis {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
// 两行文字省略
.twoEllipsis {
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
// 三行文字省略
.threeEllipsis {
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
}

还有一种使用纯css的兼容写法.在逛git的时候, 看见happylindz大佬的纯 CSS 实现多行文字截断, 真是太佩服了. 以前从未想到过float可以这样用.

14.5. 文字两端对齐

小技巧|CSS如何实现文字两端对齐

css有一个文字两端对齐的属性text-align: justify, 它可以在多行文字(中英数字符号交错)导致一行不能很好的占满时, 实现两端对齐的效果. 但是有个问题是, 在一行没有占满时, 这个属性就不起作用了.
想要让它起效, 需要添加一个行内空标签.

<div>添加一个行内空标签就有效了!<i></i></div>
div i{
  display:inline-block;
  /*padding-left: 100%;*/
  width:100%;
}
  • padding-left: 100%和width:100%都可以达到效果,选用其一即可

也可改用after、before伪元素

div::after {
  content: " ";
  display: inline-block;
  width: 100%;
}

14.6. inline-block元素设置overflow:hidden属性导致相邻行内元素向下偏移

inline-block元素设置overflow:hidden属性导致相邻行内元素向下偏移

常用的解决方法是为上述inline-block元素添加vertical-align: bottom.

15. 又爱又恨的scroll-view

说起scroll-view , 那可真是让人又爱又恨, 爱它封装了好多方法, 使用就能解决大部分问题, 甚至scroll-view 组件还能触发ios的Q弹特效. 恨它bug无数, 不知什么时候就一头栽坑里爬不出来.

贴张属性表

属性名 类型 默认值 说明
scroll-x Boolean false 允许横向滚动
scroll-y Boolean false 允许纵向滚动
upper-threshold Number / String 50 距顶部/左边多远时(单位px,2.4.0起支持rpx),触发 scrolltoupper 事件
lower-threshold Number / String 50 距底部/右边多远时(单位px,2.4.0起支持rpx),触发 scrolltolower 事件
scroll-top Number / String 设置竖向滚动条位置(单位px,2.4.0起支持rpx)
scroll-left Number / String 设置横向滚动条位置(单位px,2.4.0起支持rpx)
scroll-into-view String 值应为某子元素id(id不能以数字开头).设置哪个方向可滚动,则在哪个方向滚动到该元素
scroll-with-animation Boolean false 在设置滚动条位置时使用动画过渡
enable-back-to-top Boolean false iOS点击顶部状态栏、安卓双击标题栏时,滚动条返回顶部,只支持竖向
bindscrolltoupper EventHandle 滚动到顶部/左边,会触发 scrolltoupper 事件
bindscrolltolower EventHandle 滚动到底部/右边,会触发 scrolltolower 事件
bindscroll EventHandle 滚动时触发,event.detail = {scrollLeft, scrollTop, scrollHeight, scrollWidth, deltaX, deltaY}
  • 使用竖向滚动时,需要给<scroll-view>一个固定高度,通过 WXSS 设置 height.

15.1. 回到顶部和锚点跳转

使用scroll-top="0"即可实现回到顶部的功能, 想要动画过渡添加scroll-with-animation="{{true}}".
锚点跳转, scroll-into-view="{{intoView}}"直接填入需要跳转的元素id即可实现, 注意此时的元素id不加'#'.

15.2. 竖向滚动问题

文档中有提, scroll-view需要有一个高度, 才可以属性滚动, 这个高度是指scroll-view这个组件的显示高度. 但是我遇到过一个问题, 在iphone8(具体ios版本号记不得了)里, 设置height: 100%不能触发竖向滚动, 目前我是给scroll-view添加一个min-height:*rpx来解决问题.
我遇到了了一个在scroll-view的父元素是flex1自适应元素的时候, 要给该元素添加一个display: flex, 否则无法触发滚动问题. 不知是我的写法有误还是什么.

15.3. 获取滚动高度问题

使用bindscroll既可获取'scrollTop'等属性, 但是它有一个问题, 就是反应特别慢, 特别是开发工具里(真机还好), 有时你滚了半天了, 才堪堪触发效果. 使用js判断在只在条件达到时setData可以好上那么一点儿. 获取 SelectorQuery 对象实例可以在真机上效果稳定.

SelectorQuery wx.createSelectorQuery()

const query = wx.createSelectorQuery()
query.select('#the-id').boundingClientRect()
query.selectViewport().scrollOffset()
query.exec(function(res){
  res[0].top       // #the-id节点的上边界坐标
  res[1].scrollTop // 显示区域的竖直滚动位置
})

15.4. 上拉加载

bindscrolltolower可以轻松的实现上拉加载数据的功能, 只需在触发bindscrolltolower进行'pageNO++'重新请求拼接结果即可.

微信小程序之下拉加载和上拉刷新
微信小程序实战篇-下拉刷新与加载更多

15.5. 在有遮罩层时, 可以滚动

如果出现在遮罩弹出的时候, scroll-view滚动穿透的问题, 在弹出时设置scroll-xscroll-yfalse即可.

16. 路由

路由

微信小程序的路由具有10层的限制, 超出会出问题.
在需要的时候, 可以wx.reLaunch(Object object)清除. 但是不要滥用.
我在使用路由的时候, 遇到过一个诡异的情况, 在从tabbar页切换到其它页面时, 莫名的加载了另一个tabbar页, wx.navigateBack(Object object)也可正常返回. 但是被额外加载的tabbar页scroll-viewscroll-top失效了, 不得已使用reLaunch切换, 算是解决了问题.

17. z-index(层叠上下文)

著名的七阶层叠水平

深入理解 CSS 属性 z-index

z-index层叠上下文小计(代码片段)

摘自MDN

文档中的层叠上下文由满足以下任意一个条件的元素形成:

z-index不是写上去就会如你所想, 大在上, 小的在下. z-index会寻找一个参照, 来比较大小.

默认的层叠上下文元素只有根元素(html, 小程序里是page). 所以, 当一个dom树没有形成局部层叠上下文时, 总会以根元素为基准对比.

<view class='box'>
  <view class="one">1</view>
  <view class="two">2</view>
</view>
.one {
  z-index: 2;
  position: absolute;
  width: 100rpx;
  height: 50rpx;
  background: pink;
}
.two {
  z-index: 1;
  position: absolute;
  width: 300rpx;
  height: 100rpx;
  background: skyblue;
}

eg1

如此一个z-index, class1在class2之上无误.

<view class='box'>
  <view class="three">
    <view class='five'>5</view>
    3
  </view>
  <view class="four">4</view>
</view>
.three {
  z-index: 3;
  position: absolute;
  width: 300rpx;
  height: 150rpx;
  background: orangered;
}
.five {
  z-index: 5;
  position: absolute;
  width: 200rpx;
  height: 100rpx;
  background: yellow;
}
.four {
  z-index: 4;
  position: absolute;
  top: 0;
  width: 100rpx;
  height: 200rpx;
  background: green;
}  

eg2

但是如果你有这么一个结构的wxml(html), z-index为class5的的元素并不会在class4之上, 因为它的父元素的z-index为3, 在4之下. 这是z-index:3给class3的view创建了一个局部层叠上下文, 其中的层叠元素会以class3位基准. class3和class4是一个级别的, 他们才会进行对比.

<view class='box'>
  <view class="six">
    <view class='eight'>8</view>
    6
  </view>
  <view class="seven">7</view>
</view>
.six {
  position: absolute;
  width: 300rpx;
  height: 150rpx;
  background: orange;
}
.eight {
  z-index: 8;
  position: absolute;
  width: 200rpx;
  height: 100rpx;
  background: purple;
}
.seven {
  z-index: 7;
  position: absolute;
  top: 0;
  width: 100rpx;
  height: 200rpx;
  background: yellowgreen;
}

eg3

此时class6和class7在同一个层叠上下文: 根元素(page)里, 所以他们会直接进行比较.

18. css格式化上下文, 伪类, 变量, 命名规范, reset

谈谈一些有趣的CSS题目(11~15)

19. switch的一个用法

var num = 25;
switch (true) {
  case num < 0: 
    alert("Less than 0.");
    break;
  case num >= 0 && num <= 10: 
    alert("Between 0 and 10.");
    break;
  case num > 10 && num <= 20: 
    alert("Between 10 and 20.");
    break;
  default: 
    alert("More than 20.");
}
//More than 20.