yuuhei's Home


  • 首页

  • 归档

  • 标签

你不知道的JavaScript(下)读书笔记

发表于 2019-01-13

第二部分 ES6及更新版本

阅读全文 »

你不知道的JavaScript(中)读书笔记

发表于 2019-01-13

第一部分 类型和语法

阅读全文 »

你不知道的JavaScript(上)读书笔记

发表于 2019-01-13

第一部分 作用域和闭包

阅读全文 »

简单实现一个步骤条

发表于 2019-01-07

代码部分参考ElementUI的步骤条结构,如果作为组件,循环判断每个步骤的状态,加上对应的class即可。这里定义pass、current、future,每个状态都有不同的样式。这里是横版的,竖版的大同小异

特别要注意横线的处理,移位的方法

阅读全文 »

JavaScript高级程序设计笔记

发表于 2018-11-01

全局函数isNaN()的问题

isNaN()去执行一个非数字字符串,会返回true,注意。

因为isNaN()的本意是去判断这个参数是否”不是数值”,任何不能被转为数值的值都会导致这个函数返回true,注意,即首先对参数进行一次Number转换,返回为NaN的参数会被判断为NaN

1
2
3
4
5
6
alert(isNaN(NaN)); //true
alert(isNaN(10)); //false( 10 是一个数值)
alert(isNaN("10")); //false(可以被转换成数值 10)
alert(isNaN(true)); //false(Boolean可以被转换成数值 1)
alert(isNaN("blue")); //true(非数字字符串不能转换成数值)
alert(isNaN(undefined)); //true(undefined不能转换成数值)

解决方案有两种:
利用NaN是唯一一个与自身严格不相等的值:

1
2
3
function myIsNaN(value) { 
return value !== value;
}

在使用isNaN()之前先检查一下这个值是不是数字类型,这样就避免了隐式转换的问题:

1
2
3
function myIsNaN2(value) { 
return typeof value === 'number' && isNaN(value);
}

null进行数值转换为0,undefined进行数值转换为NaN

js将字符串转换为dom元素的方法,利用childNodes

1
2
3
4
5
6
7
function parseDom(arg) {
  var objE = document.createElement("div");
  objE.innerHTML = arg;
  return objE.childNodes[0];
};

var obj=parseDom('<div id="div_1" class="div1">Hello World!</div>');

注意:childNodes返回的是一个类似数组的list。所以如果是一个元素,要使用这个dom需要这样使用obj[0]。如果是多个同级的dom转换,可以这样使用obj[0]、obj[1]…

js获取元素的兄弟元素

1
2
3
4
5
6
7
8
9
10
var siblings = function (el) {
var arr = [];
var children = el.parentNode.children;
for (var i = 0, len = children.length; i < len; i++) {
if (children[i] !== el) {
arr.push(children[i]);
}
}
return arr;
}

new原理

使用构造函数,要创建新的实例,必须使用 new 操作符。以这种方式调用构造函数实际上会经历以下 4
个步骤:

  • 创建一个新对象;
  • 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);
  • 执行构造函数中的代码(为这个新对象添加属性);
  • 返回新对象。

实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var Person = function (name, age) {
this.name = name;
this.age = age;
}

var newConstructor = function (Foo, ...args) {
// 继承构造函数prototype的属性,有两种写法
// var obj = {};
// obj.__proto__ = Foo.prototype;
var obj = Object.create(Foo.prototype);
// 执行并绑定this到新对象
var k = Foo.apply(obj, args);
// 返回
if (typeof k === 'object') {
return k;
} else {
return obj;
}
}

var person = newConstructor(Person, 'yuuhei', 23);

JS工作经验总结(2)

发表于 2018-10-29

本文主要记录工作时JS问题或经验

new Array()的遍历问题

new Array(3)这样得出的稀疏数组不能够进行遍历,需要进行特殊的转义才能够遍历

webkitTransitionEnd监听Transition动画结束事件

css3的过渡属性transition,在动画结束时,也存在结束的事件:webkitTransitionEnd; 注意:transition,也仅仅有这一个事件。

1
$el.addEventListener('webkitTransitionEnd', handler, false);

css3的动画animation也能够监听事件,分别在开始和结束时都能够监听到animationstart,animationend

阻止冒泡问题

stopPropagation()方法既可以阻止事件冒泡,也可以阻止事件捕获

stopImmediatePropagation()和 stopPropagation()的区别在,后者只会阻止冒泡或者是捕获。 但是前者除此之外还会阻止该元素的其他事件发生,但是后者就不会阻止其他事件的发生

arguments的问题

arguments不是一个真正的数组,是一个类数组,无法使用shift等数组方法,只能使用Array.prototype.slice.apply(arguments)转换成真正的数组,或者使用[].shift.call(arguments)之类的方法来调用数组的方法

img作为数据统计的问题

img经常进行用于数据上报,做用户埋点什么的,消息已读什么的,就是将query放在图片的请求地址上就可以了。需要注意在使用img进行http请求时,img对象需要存储在闭包里,避免函数执行后,里面的变量在http请求完成前被销毁,导致发送不成功,会造成请求丢失的问题
例子(使用闭包封装img变量,避免销毁):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var tracker = (function() {
var imgs = [];
var url = 'https://www.baidu.com/img/bd_logo1.png';
return function(id, obj = {}) {
var query = '?id=' + id;
for (var i in obj) {
query += '&' + i + '=' + obj[i];
}
var img = new Image();
imgs.push(img);
img.src = url + query;
}
})();

tracker('TEST_TRACKER', {
name: 'yuuhei',
age: '24'
});
阅读全文 »

JS工作经验总结(1)

发表于 2018-06-20

本文主要记录工作时JS问题或经验

html里自定义属性的赋值和取出问题

给html元素写入data-type这样的格式,可以使用el.dataset.type进行相对应的访问;\
在不兼容dataset的情况下,可以使用更原始的el.getAttribute(name)进行取值,使用el.setAttribute(name, value)进行赋值

document.activeElement的应用(焦点问题)

document.activeElement.tagName可获得当前获得焦点的元素,document.activeElement.blur()使当前获得焦点的元素失焦

关闭webApp中的调起的系统键盘

window.nativeKeyboradCancel(),在webApp里取消调起系统的键盘,如果有的话

循环遍历父元素的方法

以下可以使用while一直遍历父元素:

1
2
3
4
5
6
7
8
9
function getTagName(ele) {
var parent = ele.parentElement;
var string = '';
while (parent !== null) {
string += parent.tagName + ' ';
parent = parent.parentElement;
}
return string;
}
阅读全文 »

Vue工作经验总结(1)

发表于 2018-03-19

本文主要记录工作时使用Vue时的问题或经验

关于使用v-model传值给组件的语法糖

template:

1
2
3
4
<currency-input v-model="price"></currentcy-input>
<!-- 上行代码是下行的语法糖
<currency-input :value="price" @input="changeInput"></currency-input>
-->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Vue.component('currency-input', {
// 这里emit给父组件的input事件是由于v-model的语法糖所创立的
template: `
<span>
<input ref="input" :value="value" @input="$emit('input', $event.target.value)">
</span>
`,
props: ['value'], // v-model后在组件里使用props的value来相应
})

var demo = new Vue({
el: '#demo',
data: {
price: 100,
}
/*methods: {
changeInput(val) {
this.price = val
}
}*/
})

这样使用v-model就可以不用像注释里一样,在组件emit一个自定义事件,在外面接住,这样写在里面修改,外面就能接收到传过来的value,并且将其改写,响应data

事件修饰符.native的使用场景

关于加了native和不加的区别,使用以下代码说明:

定义自定义组件vButton:

1
<button type="button" @click="clickHandler"></button>
1
2
3
4
5
6
7
8
export default {
name: 'vButton',
methods: {
clickHandler () {
this.$emit('vclick') // 触发 `vclick` 事件
}
}
}

引用自定义组件vButton时事件的触发情况:

1
<v-button @click="clickHandler" @vclick="vClickHandler">按钮</v-button>
1
2
3
4
5
6
7
8
9
10
11
12
import vButton from '@/components/Button'
export default {
components: { vButton },
methods: {
clickHandler () {
alert('onclick') // 此处不会执行 因为组件中未定义 `click` 事件
},
vClickHandler () {
alert('onvclick') // 触发 `vclick` 自定义事件
}
}
}

如果将上面模版改成:

1
<v-button @click.native="clickHandler" @vclick="vClickHandler">按钮</v-button>

那么两个事件都会执行,.native 修饰符就是用来注册元素的原生事件而不是组件自定义事件的

意思就是当你给一个vue组件绑定事件时候,要加上.native,如果是普通的html元素就不需要。

使用v-if、v-else-if、v-else处理显示

注意v-else-if和v-else前一兄弟元素必须有 v-if 或 v-else-if,v-else。链式调用可以更好的处理template显示逻辑

class条件判断绑定问题

:class={ active: isActive }的时候左边可以不加引号,也可以加上active这个class,右边是props或data或computed的数据

在template使用引用静态文件(img等)

vue,webpack中,template中可以使用~+alias配置名称,js里可以使用import和require引用静态图片地址

父子组件和非父子组件的传值问题

父子组件:使用@和$emit传值;\
非父子组件:eventBus或vuex。

filter使用技巧

filter可以的到第二,第三,以此类推个参数,在使用filter的时候使用filterA(arg1, agr2, …)传入,第一个参数就是值本身

vux如何通过v-model显示组件的问题

vux是通过v-model传入value作为props,然后value用v-if控制组件的显示与否

对象watch问题

有三种方法,推荐第二、三种方法,可以拿到oldValue,直接检测对象是拿不到oldValue的:

阅读全文 »

关于table-cell实现的各种布局

发表于 2018-03-02

display:table-cell指让标签元素以表格单元格的形式呈现,使元素类似于td标签。IE8+及现代版本的浏览器都支持此属性,IE6/7不支持(可用其他方法实现类似效果)。同样,display:table-cell属性也会被float,position:absolute等属性破坏效果,应避免同时使用。

设置了display:table-cell的元素:

  • 对宽度高度敏感
  • 对margin值无反应
  • 响应padding属性
  • 内容溢出时会自动撑开父元素

display:table-cell的几种用法

1.大小不固定元素的垂直居中

1
2
3
4
5
6
7
<div class="content">
<div style="padding: 50px 40px;background: #cccccc;color: #fff;"></div>
<div style="padding: 60px 40px;background: #639146;color: #fff;"></div>
<div style="padding: 70px 40px;background: #2B82EE;color: #fff;"></div>
<div style="padding: 80px 40px;background: #F57900;color: #fff;"></div>
<div style="padding: 90px 40px;background: #BC1D49;color: #fff;"></div>
</div>
1
2
3
4
5
6
7
8
9
10
.content {
display: table-cell;
padding: 10px;
border: 2px solid #999;
}

.content div {
display: inline-block;
vertical-align: middle;
}

补充一种多行文本垂直居中的方法:

1
2
3
4
<div class="wrap">
<div class="left">这边很少</div>
<div class="right">这边很多字···</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.wrap {
display: table;
margin: 0 auto;
height: 300px;
width: 300px;
border: 2px solid #0cf;
}

.left {
display: table-cell;
vertical-align: middle;
width: 100px;
}

.right {
display: table-cell;
vertical-align: middle;
width: 200px;
}

2.两列自适应布局

1
2
3
4
5
6
<div class="content">
<div class="left-box">
<img src="img/a1.jpg" width="70">
</div>
<div class="right-box">...</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.content {
display: table;
padding: 10px;
border: 2px solid #999;
}

.left-box {
float: left;
margin-right: 10px;
}

.right-box {
display: table-cell;
padding: 10px;
width: 3000px;
vertical-align: top;
border: 1px solid #ccc;
}

左边头像部分使用了float左浮动属性,左侧使用 display: table-cell则实现了两列自适应布局。至于.right-box中的width:3000px解释引用别人的:

display:table-cell 元素生成的匿名table默认table-layout:auto。宽度将基于单元格内容自动调整。当内容足够多将宽度完全撑开时,再让某个元素(例如关闭按钮)右侧定位就会有问题。所以设置width:3000px的用途是尽可能的宽的意思。

对于IE6/7,我们可以使用display: inline-block属性代替。

3.等高布局

1
2
3
4
<div class="content">
<div class="box1">我和右边等高</div>
<div class="box2">table表格中的单元格最大的特点之一就是同一行列表元素都等高。所以,很多时候,我们需要等高布局的时候,就可以借助display:table-cell属性。说到table-cell的布局,不得不说一下“匿名表格元素创建规则”</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.content {
display: table;
padding: 10px;
border: 2px solid #999;
}

.box1 {
display: table-cell;
width: 100px;
border: 1px solid #ccc;
}

.box2 {
display: table-cell;
border: 1px solid #ccc;
}

4.和inline-block组合使用

1
2
3
4
5
6
7
8
<div class="content">
<div class="left">
<div class="box">A</div>
</div>
<div class="right">
<div class="box">B</div>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.content {
display: table;
padding: 10px;
margin: 10px auto;
width: 1000px;
border: 2px solid #999;
}
.left {
display: table-cell;
text-align: left;
border: 1px solid #0cf;
}

.right {
display: table-cell;
text-align: right;
border: 1px solid #fc0;
}
.box {
display: inline-block;
width: 100px;
height: 100px;
border: 1px solid #ccc;
}

代码解释:A和B的父元素均设置了display:table-cell属性,所以
它们均匀占据设置了display:table的div元素。而A和B元素设置display:inline-block是为了让它们相应text-align的属性设置。

inline-block 是宽高margin设定有效,参与行内格式化上下文,在行内对齐时使用它自己的框底边为基线对齐位置

5.列表布局

1
2
3
4
5
6
7
8
9
<div class="content">
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.content {
padding: 10px;
margin: 10px auto;
border: 2px solid #999;
}

.content ul {
display: table;
width: 100%;
padding: 0;
}

.content ul li {
display: table-cell;
height: 100px;
line-height: 100px;
text-align: center;
border: 1px solid #ccc;
}

这类布局常用浮动布局(给每个li加上float:left属性)实现,但这样做有明显不足:

  • 需要清除浮动
  • 不支持不定高列表的浮动

display:table-cell可以代替浮动布局,但是其不是最好的方法。其他方法有待进一步学习!

最后,说说“匿名表格元素创建规则”:

CSS2.1表格模型中的元素,可能不会全部包含在除HTML之外的文档语言中。这时,那些“丢失”的元素会被模拟出来,从而使得表格模型能够正常工作。所有的表格元素将会自动在自身周围生成所需的匿名table对象,使其符合table/inline-table、table-row、table- cell的三层嵌套关系。

简单来讲,我们为一个元素设置了display:table-cell属性,而不将其父元素设置为display:table-row属性,浏览器会默认创建一个表格行。

关于浏览器重排和重绘和一个重绘案例

发表于 2018-01-24

什么是重排和重绘

浏览器下载完页面中的所有组件——HTML标记、JavaScript、CSS、图片之后会解析生成两个内部数据结构——DOM树和渲染树。

DOM树表示页面结构,渲染树表示DOM节点如何显示。DOM树中的每一个需要显示的节点在渲染树种至少存在一个对应的节点(隐藏的DOM元素disply值为none 在渲染树中没有对应的节点)。渲染树中的节点被称为“帧”或“盒”,符合CSS模型的定义,理解页面元素为一个具有填充,边距,边框和位置的盒子。一旦DOM和渲染树构建完成,浏览器就开始显示(绘制)页面元素。

当DOM的变化影响了元素的几何属性(宽或高),浏览器需要重新计算元素的几何属性,同样其他元素的几何属性和位置也会因此受到影响。浏览器会使渲染树中受到影响的部分失效,并重新构造渲染树。__这个过程称为重排__。完成重排后,浏览器会重新绘制受影响的部分到屏幕,__该过程称为重绘__。由于浏览器的流布局,对渲染树的计算通常只需要遍历一次就可以完成。但table及其内部元素除外,它可能需要多次计算才能确定好其在渲染树中节点的属性,通常要花3倍于同等元素的时间。这也是为什么我们要避免使用table做布局的一个原因。

并不是所有的DOM变化都会影响几何属性,比如改变一个元素的背景色并不会影响元素的宽和高,这种情况下只会发生重绘。

重排和重绘的代价究竟多大

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var times = 15000;

// code1 每次过桥+重排+重绘
console.time(1);
for(var i = 0; i < times; i++) {
document.getElementById('myDiv1').innerHTML += 'a';
}
console.timeEnd(1);

// code2 只过桥
console.time(2);
var str = '';
for(var i = 0; i < times; i++) {
var tmp = document.getElementById('myDiv2').innerHTML;
str += 'a';
}
document.getElementById('myDiv2').innerHTML = str;
console.timeEnd(2);

// code3
console.time(3);
var _str = '';
for(var i = 0; i < times; i++) {
_str += 'a';
}
document.getElementById('myDiv3').innerHTML = _str;
console.timeEnd(3);


// 1: 2874.619ms
// 2: 11.154ms
// 3: 1.282ms

重排何时发生

很显然,每次重排,必然会导致重绘,那么,重排会在哪些情况下发生:

  • 添加或者删除可见的DOM元素
  • 元素位置改变
  • 元素尺寸改变
  • 元素内容改变(例如:一个文本被另一个不同尺寸的图片替代)
  • 页面渲染初始化(这个无法避免)
  • 浏览器窗口尺寸改变

这些都是显而易见的,或许你已经有过这样的体会,不间断地改变浏览器窗口大小,导致UI反应迟钝(某些低版本IE下甚至直接挂掉),现在你可能恍然大悟,没错,正是一次次的重排重绘导致的!

渲染树变化的排队和刷新

思考下面代码:

1
2
3
4
var ele = document.getElementById('myDiv');
ele.style.borderLeft = '1px';
ele.style.borderRight = '2px';
ele.style.padding = '5px';

乍一想,元素的样式改变了三次,每次改变都会引起重排和重绘,所以总共有三次重排重绘过程,但是浏览器并不会这么笨,它会把三次修改“保存”起来(大多数浏览器通过队列化修改并批量执行来优化重排过程),一次完成!但是,有些时候你可能会(经常是不知不觉)强制刷新队列并要求计划任务立即执行。获取布局信息的操作会导致队列刷新,比如:

  • offsetTop, offsetLeft, offsetWidth, offsetHeight
  • scrollTop, scrollLeft, scrollWidth, scrollHeight
  • clientTop, clientLeft, clientWidth, clientHeight
  • getComputedStyle() (currentStyle in IE)

将上面的代码稍加修改:

1
2
3
4
5
6
7
var ele = document.getElementById('myDiv');
ele.style.borderLeft = '1px';
ele.style.borderRight = '2px';

// here use offsetHeight
// ...
ele.style.padding = '5px';

因为offsetHeight属性需要返回最新的布局信息,因此浏览器不得不执行渲染队列中的“待处理变化”并触发重排以返回正确的值(即使队列中改变的样式属性和想要获取的属性值并没有什么关系),所以上面的代码,前两次的操作会缓存在渲染队列中待处理,但是一旦offsetHeight属性被请求了,队列就会立即执行,所以总共有两次重排与重绘。所以 尽量不要在布局信息改变时做查询 。

最小化重排和重绘

阅读全文 »
<i class="fa fa-angle-left"></i>1…3456<i class="fa fa-angle-right"></i>

56 日志
14 标签
GitHub
© 2023 Sellenite
由 Hexo 强力驱动
主题 - NexT.Mist