CSS易忘细节汇总 & 简写属性
- 布局原则
- 所有标签都是盒子
- 行内(块)元素水平对齐
- 块元素水平居中
- 图片(行内块)底部空白缝隙问题
- 定位元素居中
- 定位对齐方式
- 单行文本,垂直居中
- 图片相邻文字垂直对齐方式
- 行高最终作用在文字上面,而非任何标签
- padding 和 border 影响盒子大小
- 根据字数不同生成长度不同的盒子区域(利用padding撑大盒子)
- 不确定尺寸或者内部元素个数是动态的父盒子不要指定固定的尺寸,应该由实际内容撑开。
- 行内(块)元素中间的空隙
- 浮动盒子内容动态时,无法指定父盒子高度,此时引起的父盒子高度塌陷问题
- 菜单一般使用li套a标签
- 外边距使用陷阱
- 容易出问题的点
- 过度和动画
- 文字原则
- 简写属性
- JS
- dayjs实现倒计时
- 请求后端图形验证码方案
- Vue
- 封装 async await 通用Promise异常处理
- uniapp
- swagger3 响应体为空的问题
- Mybatis一对多问题
布局原则
- 先行后列
- 先结构后样式
所有标签都是盒子
无论是行内元素,行内块元素还是块元素都是盒子。
区别主要在于能否直接指定宽高和默认排列方式。
因此,行内(块)元素同样能使用 margin padding 那些属性
行内(块)元素水平对齐
text-align: left/center/right
块元素水平居中
- 给块级元素设置宽度;不设置宽度,占据一整行,没有居中的概念
- 左右外边距设置为 auto;
margin 0 auto;
图片(行内块)底部空白缝隙问题
行内块元素默认与文字的基线对齐,因文字基线下面还有一小部分长度,因此基线以下的部分对齐到行内块这边就表现为下边有个缝隙。
此时可以使用 vertical-align: baseline/bottom/top/middle设置对齐方式
定位元素居中
使用 定位+margin负值
position: absolute;
top: 50%;
left: 50%;
margin-top: -50px;
margin-left: -100px;
width: 200px;
height: 100px;
使用 css函数 calc计算定位位置
position: absolute;
top: calc(50% - 50px);
left: calc(50% - 100px);
width: 200px;
height: 100px;
使用2d位移(推荐)
position: absolute;
top: 50%;
left: 50%;
transform: translate(-100%, -50%);
width: 200px;
height: 100px;
定位对齐方式
元素启用定位时,靠哪一边对齐,尺寸发生变化时,向另一个方向发生变化
单行文本,垂直居中
行高设置为所在盒子的高度 line-height = 父盒子高度
或者父盒子不设置高度,由子盒子的行高来撑起父盒子的高度。
注意C3盒子模型中文字垂直居中问题
由于C3盒子模型宽高,包括了边框;而文字行高是基于盒子内容而言的。
一次C3盒子模型中,要垂直居中,行高 = 盒子高度 - 上下边框的大小
图片相邻文字垂直对齐方式
图片默认与文字基线对齐,所谓基线,就是以前写拼音的中间那一条线。
可以通过图片的CSS属性 vertical-align 修改图片与文字的对齐方式
文字和图片不能浮动,如果有浮动,那么这个属性无法生效
vertical-align: bottom / baseline / middle / top
行高最终作用在文字上面,而非任何标签
如果文字在行内元素中,那么只会影响该元素的上下空间,而不会撑大元素本身(因为行内元素没有设置宽高一说)
如果文字在块元素(包括行内块)中,那么会撑大元素本身。
padding 和 border 影响盒子大小
此二者只影响传统盒模型下的盒子大小。
在此基础之上。
- padding只影响在对应方向上值定了尺寸的大小。
- border在水平方向只影响值定了宽度的盒子的宽度;而高度无论是否值定,都会影响
再加一点,在边框盒模型(目前用得更多)下,paddig只对指定了尺寸的盒子没效果,对于未指定尺寸的行内(块)依然有效
根据字数不同生成长度不同的盒子区域(利用padding撑大盒子)
- 将每个菜单盒子转为行内块,不要设置宽度
- 设置相同的padding值
- 在盒子内录入各自的菜单名称,让文字 和 padding 撑开盒子
不确定尺寸或者内部元素个数是动态的父盒子不要指定固定的尺寸,应该由实际内容撑开。
行内(块)元素中间的空隙
当行内(块)元素没有写在一行时,它们之间默认会有一个空隙。
解决办法有以下几种方法:
- 使用margin-left 或者 margin-right 负值让元素靠一起
- 将行内块元素的父元素font-size设置为0,然后在行内块内部再设置需要的字体大小
- 浮动
- 使用flex布局
- 源代码级别,将两个标签连接的字符(><SPAN>)放在同一行
- 源代码级别,用注释连接两行,在注释中换行
浮动盒子内容动态时,无法指定父盒子高度,此时引起的父盒子高度塌陷问题
标准流盒子内的盒子都浮动后,无法撑起父盒子的高度,导致后面的元素被浮动元素覆盖。
解决:
在子盒子浮动的最后 清除浮动
在最后一个元素浮动后,可能需要清除浮动,否则有可能影响后续的标准流盒子。
li 元素浮动 导致 box 盒子高度为0 ,进而影响了 footer 盒子,飘到 li 列表里面去了。
<div class="box">
<ul>
<li class="content">1</li>
<li class="content">2</li>
<li class="content">3</li>
<li class="content last">4</li>
</ul>
</div>
<div class="footer">footer</div>
.box li.content{
width: 24%;
height: 300px;
background-color: #96C02E;
float: left;
margin-right: 10px;
margin-bottom: 5px;
}
- 方式一:
追加带有清除浮动样式的块元素。
在 li 标签后加一个li标签
给追加的 li 赋予 清除浮动的属性<li class="clr"></li>
.clr{ clear: both; }
- 方式二
给浮动元素的父元素添加 overflow 属性,值可以是 hidden, auto, scroll 都能实现清除浮动 - 方式三
对浮动元素的父元素使用 after 伪元素清除浮动,其本质还是标签追加法
/* 方式三 使用 :after 伪元素,对父元素追加一个空的块元素*/
div.box::after{
content: ""; /*after 用于定义这个伪元素的内容*/
display: block; /*追加元素需要块元素,而伪元素默认为行内元素,故手动转块元素*/
clear: both;
}
/*鉴于低版本IE不支持 伪元素,出于兼容性考虑,为IE老版本加上*/
div.box{
*zoom:1;
}
菜单一般使用li套a标签
此时需要将a标签填满li,可以将a转为块级元素,并设置适当的宽高即可
a {
display: block;
width: 100px;
height: 30px;
background-color: #blue;
}
外边距使用陷阱
两个相邻盒子均设置相遇方向外边距,导致较小的外边距塌陷
两盒子外边距相遇时,不会叠加两个值,而是取其中较大的一个
解决:建议外边距只设一个方向
两个嵌套盒子同时设置,上外边距(margin-top)时,此时父元素会取二者较大的外边距,而子元素没有外边距效果
解决:
- 方式一:
给父元素设置一个边框(可以解决,但是边框粗细不能为0,否则无效。因此会影响整体布局,不建议使用) - 方式二:
不使用子元素的 margin-top ,改用父元素的 padding-top 实现,注意传统盒子模型下,可能会撑大父元素,影响整体布局 - 方式三:
给父元素设置 overflow:hidden; 属性 - 方式四:
给父元素设置(固定/绝对)定位或者浮动,这三种都是利用父元素脱离文档流实现的,但是不推荐,会影响整体布局 - 方式五:
改变父元素显示方式,简单试了下,改成 inline-block / table … - 方式六:
使用伪类元素实现,借鉴了方式五.test1::before{ content: ""; display: table; }
容易出问题的点
- 上下外边距相遇时,会忽略较小值,直接取较大值,而不是二者之和
- 嵌套父子盒子同时设置margin-top时,子元素无效,会计算到父元素上
- 盒子浮动后,变成行内块的特性
- UI上没有明显界限的多个盒子,可酌情划分
- 盒子内图片大小,只设置一边,若同时设置宽高,可能导致图片扭曲变形
- 不定长的导航栏或者 列表,在不确定的方向上不要设置固定的宽高,由实际内容撑开
过度和动画
过度和动画都能实现一些动态效果。
区别在于:
过度来去的效果是一样的;
而动画可以随着CSS的 附加/移除 实现慢入快出,加上class之后播放动画,移除class之后,立即消除效果。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
div {
width: 100%;
height: 50px;
background: purple;
top: -50px;
position: fixed;
}
.fix {
animation: top-move .3s linear forwards;
}
@keyframes top-move {
0% {
top: -50px
}
100% {
top: 0;
}
}
section {
width: 1200px;
height: 2000px;
background: pink;
overflow: hidden;
margin: 0 auto;
}
aside {
width: 100px;
height: 100px;
margin-top: 300px;
background: black;
}
</style>
</head>
<body>
<div></div>
<section>
<aside></aside>
</section>
<script>
const div = document.querySelector('div')
const aside = document.querySelector('aside')
const divHeight = div.offsetHeight
window.addEventListener('scroll', function (e) {
// console.log(typeof aside.offsetTop);
console.log(divHeight);
const scoll_top = document.documentElement.scrollTop
console.log(`aside.offsetTop = ${aside.offsetTop}, divHeight = ${scoll_top}`);
if (aside.offsetTop - divHeight <= scoll_top) {
console.log(0);
// div.style.height = 0
div.classList.add('fix')
} else {
div.classList.remove('fix')
// console.log(divHeight);
// div.style.visibility = 'visible'
// div.style.height = divHeight + 'px'
}
})
</script>
</body>
</html>
文字原则
盒子内不要直接写文字,基本按以下处理:
- 如果是单纯的文本,直接用 h1~h6(突出性文字) 或者 span(普通描述文字)
- 如果是单个带有点击效果的文字,用 a 标签包裹
- 如果是多个整齐排列的带有点击效果的文字,用 li > a 标签包裹
简写属性
font
有顺序,有可选属性
font: <font-style> <font-weight> font-size</line-height> font-family
line-height 可以为固定像素值或者纯数字(数字是相对于当前字体的倍数大小)
background
没有顺序,都是可选,至少写一个属性
background: <color> <image> <repeat> <attachment> <position>
border
盒子边框复合写法,同时定义边框大小,形状,颜色;不分顺序
border: border-width border-style border-color;
margin/padding
盒子内/外边距,第一个值从12点方向开始,一次顺时针。
- 4个值
依次表示上,右,下,左
# 上内边距 10px ,右内边距 20px,下内边距 30px,左内边距 40px
padding: 10px 20px 30px 40px;
- 3个值
依次表示上,右/左,下
# 上内边距10px 左右内边距 20px 下内边距 30px
padding: 10px 20px 30px;
- 2个值
依次表示 上/下,左/右
# 上下内边距10px 左右内边距 20px
padding: 10px 20px;
- 1个值
上下左右内边距都是一个值
上下左右内边距都是 10px
padding: 10px;
2d转换简写
先后顺序为:位移 → 旋转 → 缩放
transform: translate() rotate() scale()
由于位移会影响坐标,需要仔细考虑先后顺序。
CSS 有很多表示状态的伪类选择器,平时虽然用得可能没那么多,但要知道有
- :disabled :禁用时的样式(常用于各种表单组件)
- :checked:选中时的样式(常用于单选/复选框)
- ::placeholder:定义placeholder 默认提示内容的样式(这个选择器有两个 ::)
- :link
- :visited
- :hover
- :active
… 其他还有很多,具体可以查看
JS
鼠标进入离开事件与:hover的区别
鼠标经过,离开 事件注意和伪类选择器 :hover区分开:
经过和离开是独立的事件,分别有不同的动作,都有结果,结束后效果会保持。
而**:hover仅仅悬浮期间有效,一旦离开恢复元素原有样式**。
JS操作样式:推荐使用 classList 的方式,更便于维护。
mouseover mouseout VS mouseenter mouseleave
VS前后两种事件,业务含义一样,都是鼠标进入,离开的事件。
但是 mouseover mouseout 会触发捕获冒泡,在页面结构较为复杂时,随着鼠标的移动会有很多事件被触发,可能带来性能问题
因此,实际现在更推荐使用 mouseenter mouseleave 这两个事件,此二者不会触发捕获和冒泡。
详情见这里
JS事件流
事件流的传递并不依赖目标元素是否绑定了对应事件。
比如:
子元素没有绑定单击事件,父元素绑定了单击事件。
此时,单击了子元素,由于事件冒泡的原因,该事件依然冒泡给了父元素。
所以,结果就是:单击没有绑定事件的子元素,父元素的事件被触发。
事件委托
事件委托:常用于给一个列表批量绑定事件;将事件绑定给父元素,然后利用事件冒泡,由子元素来触发。
HTML标签自定义属性
HTML规范中,建议自定义属性统一命名规范:以 data- 开头;
JS的DOM对象中,统计将自定义属性封装到 dataset 属性中
const value = dom.dataset.key
JS获取页面特殊标签
通过一般获取方式document.querySelector 都能获取到;
对于一些特殊的标签,有更简洁的方式获取其dom对象;
- html标签:document.documentElement
- head标签:document.head
- body标签:document.body
事件循环
JS是单线程,对于异步操作会进入一个队列;待同步代码执行完毕;
再去任务队列拉取异步任务执行。
执行一个再去拉取,一直循环。
定时器和延迟器记得清除
JS获取单选复选框下拉框选中的值
- 单选/复选
const gender = document.querySelector('input[name="gender"]:checked')
gender.value
const province = document.querySelector('select[name="province"]')
province.value
JS中需要写分号的场景
- 立即执行函数
(function(){
// code
})();
- 直接以 [] 形式的数组开头的语句不是第一行时
const a = 1
// 直接以 [] 形式调用数组方法
[1,2].xxx
// 数组解构时
const arr = [1,2,3];
[first, second, third] = arr
非空运算符
为了防止对象为空时链式调用报错。可以使用 ?. 运算符,当前面的对象为空时,就不会执行后续调用。
obj1?.key1?.key2
obj?.fun1()?.func2()
前端预览图片
使用 URL.createObjectURL(file对象) 得到本地预览url
img.src = URL.createObjectURL(file对象)
JS url编码
escape 和 unescape
用于对普通字符串编解码
encodeURI 和 decodeURI
对整个url进行编码,忽略在url 有特殊含义的字符
encodeURIComponent 和 decodeURIComponent
同样是对整个url进行编码,对url特殊字符同样进行编码
dayjs实现倒计时
- date1.diff(date2, ‘second’) 计算得到两个时间相差的秒数
- dayjs.unix(秒数) .format(‘mm份ss秒’) 得到格式化后的字符串
请求后端图形验证码方案
-
后端以Base64形式返回,前端img标签直接以 src 接收data:image/png;base64,图片的Base64编码值
-
后端以 ContentType(“image/jpeg”) 或其他html支持的图片格式,以输出流的形式返回
out = response.getOutputStream(); ImageIO.write(bi, "jpg", out); out.flush();
前端直接将返回值,写入src属性
Vue
Vue事件函数同时接收渲染的参数和事件的参数
vue事件在触发时,有的参数是外部传入,有的是直接取渲染时的属性
如果要同时取这两者需要换一种写法。
item 是渲染时的对象,num则是事件触发时外部传入的参数(直接写的事件函数只能接收num一个参数)
@input="(num) => fn1(item, num)"
Vue自定义组件监听原生事件
在自定义组件上,默认是无法监听原生事件,Vue会认为这个事件是子组件内部的自定义事件。
想要监听原生事件需要加上一个指令修饰符:.native
<my-item @click.native="handleClick">
</my-item>
封装 async await 通用Promise异常处理
切记一点,这个函数处理的前提是拿到了Promise结果,如果在目标函数执行期间出现异常,未能拿到Promise对象是无法处理的,依然需要在自行 try - catch
export const asyncAwait = (promise) => {
try {
return promise
.then(res => ({ res, undefined }))
.catch(err => ({ undefined, err }))
} catch (err) {
return { undefined, err }
}
}
- 使用
const awaitRes = await asyncAwait(fn())
if (awaitRes.res) {
// 业务逻辑
}
- 无法处理的示例
const fn = () => {
// code ...,直接抛出异常
throw Error('系统异常')
}
uniapp
Vue 和 uniapp 部分生命周期执行顺序
Vue | uniapp |
---|---|
setup | |
beforeCreate | |
" | onLoad(非根组件触发) |
created | |
beforeMount | |
" | onLaunch(只在根组件触发) |
‘’ | onReady(非根组件触发) |
‘’ | onShow |
mounted |
并且 uniapp 会不断检测当前应用是否得到焦点:分别触发 :onShow 和 onHide 生命周期函数
uniapp 中同一个组件内,相同生命周期函数,可能会被后面的覆盖
uniapp 回调函数this指向
- 箭头函数
指向外层 this,在Vue2中可直接使用this 访问 data数据 - 变量形式或普通函数形式
指向当前回调函数
综上,要在回调函数中访问 data 中的数据,要么回调函数使用箭头函数形式,要么在当前函数内将所需的data数据本地化,或者直接将 this 重新指向本地变量。
示例:
popMenu(){
const arr = this.arr
// 或者直接将this指向本地变量
// const that = this
uni.showActionSheet({
title: '测试菜单',
alertText: '提示文字',
itemList: this.arr,
// 普通函数,通过本地的 arr变量访问
// success:function(res){
// console.log(arr, this)
// console.log(that.arr, this)
// }
// 普通函数,通过本地的 arr变量访问
success(res) {
console.log(arr, this)
}
// 箭头函数直接访问 data中数据
// success: (res) => {
// console.log(this.arr)
// }
})
}
uniapp绑定数组,数组发生增减元素,不触发渲染
如果直接在JS中增减数组中的元素,未触发页面实时渲染;需要重新个数组赋值。
uniapp微信小程序页面进入未渲染数据
进入首页,加载数据失败;
开发者模拟环境没问题;真机出现问题,无法加载出数据。
开发单纯的Vue应用通常将页面请求放到 :created或者mounted 生命周期函数中。
经过测试,uniapp 编译成微信小程序:这个请求需要放到 mounted 或者 onReady 生命周期函数中。
(但是官方文档中,推荐网络请求放到 onLoad 中。。。后续再看看)
uniapp 内置的scss 变量
CSS 变量 | 描述 | App | 小程序 | H5 |
---|---|---|---|---|
–status-bar-height | 系统状态栏高度 | 系统状态栏高度、nvue 注意见下 | 25px | 0 |
–window-top | 内容区域距离顶部的距离 | 0 | 0 | NavigationBar 的高度 |
–window-bottom | 内容区域距离底部的距离 | 0 | 0 | TabBar 的高度 |
由于小程序自带一个顶部区域;实际的显示区域是从导航条下面开始计算的,而H5中则是以页面顶部开始计算的。
因此,在需要使用顶部定位时,要使用 uniapp 内置的一个变量 –window-top;同样对于底部也是一样,需使用 –window-bottom 这个变量
- 在scss 中使用变量原生的语法就是直接使用变量名替换即可
- 定义变量
$highlight-color: #F90;
- 使用变量
.selected {
border: 1px solid $highlight-color;
}
- 使用uniapp 内置的scss 变量,使用 var(变量名) 的方式
height: var(--status-bar-height);
swagger3 响应体为空的问题
外层泛型不加 @Scheme
Mybatis一对多问题
通常会使用 collection 和 association 标签实现多表关联查询。
基本语法:
< collection property="实体类中自定义字段名(子查询中使用这个名称)" column="主查询结果集中的关联字段" ofType="多中的类型" select="子查询 select">< /collection>
通常 column 字段都是传递查询结果集中的一个字段到子查询中。
如果要传递多个字段到子查询,可以采用以下方式。
< collection property="实体类中自定义字段名" column="{id=id, userName=user_name}" ofType="多中的类型" select="子查询 select">< /collection>
userName:传入子查询中字段名
user_name:主查询中结果集中的字段名