• DOCTYPE三种标准模式的写法
1.<!DOCTYPE html>
2.<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01//EN” “http://www.w3.org/TR/html4/strict.dtd”>
3.<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”>
当还是XHTML的时候一般用第三种,但是现在我们一般直接用第一种写法就可以了,第一种是HTML5规范的标准写法。
• label标签
label标签的作用主要是用来绑定的,它里面有一个for属性。
通过for属性,里面写上要绑定的标签的id,就可以把这个label标签绑定到相对应的标签上了。
1.
2. <label for=“demo”>username:</label>
3. <input type=“text” id=“demo” />
HTML; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
现在我们点击username这个label标签,也可以触发input的聚焦事件,就好像这两个是一个东西一样,可以提高用户体验。
另外,我们如果在js代码中要表示label标签的for属性的话,要写htmlFor,而不能直接写for。
• 属性映射
这里的映射是指标签的行间属性(特性)和DOM对象的属性之间的映射关系。
1.
2. <div class=“demo” id=“only” data-log=“123” abc=“a”></div>
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
class和id属性是系统自带的,后两个属性是我们自己写的属性。
而我们获取出来的DOM对象div,可以通过div.className和div.id的方式来改写行间的属性值,但是却没法通过div.data-log或者div.abc来改写行间属性。
这是因为系统默认的属性之间是有一种映射关系的,我们可以直接修改DOM的属性来修改行间属性,但是对于我们自己设置的属性,就只能通过setAttribute和getAttribute来设置和获取了。
js引擎特殊赋予的映射关系有:class、id、style、value等属性。
• img图片预加载
其实图片预加载是一种思想,而不是一种代码方式。
1.
2. var count = 0,
3. img;
4. var timer = setInterval(function () {
5. count++;
6. if(count == 5) {
7. img = document.createElement(‘img’);
8. img.src=‘xxx’;
9. document.body.appendChild(img);
10. clearInterval(timer);
11. }
12. }, 1000);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
如果我们加入的图片很大的话,展示出来的效果将是一点一点的往下加载,导致很久图片整体才会显现出来。因此,我们为了让图片显示出来的时候就是整体,只需要让图片的src提前下载,等到需要展示的时候直接加入进来就可以。
1.
2. var count = 0,
3. img = document.createElement(‘img’);
4. img.src=‘xxx’;
5. var timer = setInterval(function () {
6. count++;
7. if(count == 5) {
8. document.body.appendChild(img);
9. clearInterval(timer);
10. }
11. }, 1000);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
但是凡事优化就会有利有弊,这种方式的弊端就在于前期会很占用网速。
• 模拟提取类名
1.
2. Document.prototype.getClassName = function (className) {
3. // 获取出来所有的元素
4. var allEle = document.getElementsByTagName(‘*’),
5. retArr = [];
6. // 遍历每一个元素
7. for (var i = 0, len = allEle.length; i < len; i++) {
8. // 把这个元素的所有类名都提取出来
9. var classNameArr = [];
10. if (allEle[i].className) {
11. classNameArr = allEle[i].className.split(‘ ‘);
12. // 去除掉类名数组中的空字符串
13. classNameArr = classNameArr.filter(function (value, key) {
14. return classNameArr[key];
15. });
16. }
17.
18. for (var j = 0, classlen = classNameArr.length; j < classlen; j++) {
19.
20. if(classNameArr[j] == className) {
21. retArr.push(allEle[i]);
22. break;
23. }
24. }
25.
26. }
27. return retArr;
28. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
getElementsByTagName(‘*’)的意思是把文档中所有的标签都选择出来的意思。
这样我们就可以模拟jQuery的类名提取方式了。
•文档碎片documentFragment
这个文档碎片就是我们前面提到过nodeType区分节点时候的属性值11的那个文档碎片节点。
文档碎片可以当做容器来进行效率的优化。
当我们动态生成很多元素然后加入到文档的时候,因为每一次添加页面都会重新重绘重排一次,效率非常低。
这个时候我们就可以把这些元素全部放到文档碎片里面,然后最后直接把整个文档碎片放进文档就可以了,这样页面只需要重绘重排一次即可。
1.
2. var fragment = document.createDocumentFragment();
3. var div = document.createElement(‘div’);
4. var span = document.createElement(‘span’);
5. var p = document.createElement(‘p’);
6. fragment.appendChild(div);
7. fragment.appendChild(span);
8. fragment.appendChild(p);
9. document.body.appendChild(fragment);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
但是论时间添加事件的话,文档碎片的添加速度可能会比直接添加的速度要慢,这只是理论上可以节省添加时间,但是在其他方面确实存在优化效果。
Web前端的知识之旅哟—— BOM与正则表达式
Posted on 2018-01-02
BOM
BOM的全称叫做Browser OjbectModel 浏览器对象模型,它定义了操作浏览器的接口。
BOM对象包括:Window、History、Navigator、Screen和Location。但是由于浏览器厂商的不同,BOM对象的兼容性极低,一般情况下,我们只用其中的一部分功能。
具体请参考w3school中的介绍:
http://www.w3school.com.cn/jsref/index.asp
Navigator
navigator.userAgent是我们经常使用的属性,它可以返回客户端的代理值。用这个我们就可以判断当前用户使用的是pc端还是移动端,是什么浏览器。
Screen
screen.availHeight/availWidth 可以查看除了window任务栏之外的屏幕的高度和宽度。
screen.height/width 返回显示器的屏幕的高度和宽度。不过这个兼容的较少。
screen.deviceXDPI/deviceYDPI返回显示屏幕每英寸的水平/垂直点数,即分辨率。DPI越高,分辨率越高。
History
history.length 可以返回当前窗口查看过的网页记录的数量,即浏览历史的长度。
history.back() 可以让浏览器进入到下一个历史页面。
history.forward() 可以让浏览器返回到上一个历史页面。
history.go() 这个方法有一个参数,当参数是正数的时候,前进到下一个历史页面,当是负数的时候,回退到上一个历史页面。即前两个的综合方法。
Location
location.host 可以返回当前的主机名和当前的URL端口号。比如百度就返回www.baidu.com
location.search 设置或返回从问好开始的URL(查询部分)
location.hash 可以跳转到相应的id的元素的位置,和a标签的锚点功能差不多。可以配合css3的:target一起使用改变元素的样式。
location.reload() 方法可以重新加载当前页面
BOM就介绍这么多,大家可以看看文档学习一下其他的东西。
正则表达式
在开始讲解正则表达式之前,我们先了解一下转义字符“\”。
我们如果想要打印出来双引号,因为双引号本身是一种语法规则,所以正常情况下是打印不出来的,这个时候就需要用到转义字符“/”来把双引号转化成普通的字符而不是语法规则。
1.
2. var str = ‘\”\”‘;
3. console.log(str); // “”
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
转义字符配合特殊的字母有特殊的意义:
\n 代表字符串和换行
\r 代表字符串的回车
\t 代表制表符tab
等等
多行字符串
1.真正的多行字符串,在每一行最后加一个\n就可以了。
2.为了让结构看起来清晰,我们在编辑器中人为的换行,但是本身并不是多行字符串,这个时候只需要在每一行末尾加一个“\”就可以了。
1.
2. var str = ‘\
3. <div class=”demo”>\
4. <span></span>\
5. <p></p>\
6. </div>\
7. ‘
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
了解了转义字符之后,下面我们正式进入正则表达式部分。
正则表达式的作用
正则表达式是进行字符串匹配检测的,当我们为了匹配特殊字符或有特殊匹配原则的字符的时候,正则表达式是最佳的选择。
正则表达式(RegExp)有两种创建方式:
1.直接量
1.
2. var reg = /abc/;
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
2.构造函数
1.
2. var reg = new RegExp(‘abc’);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
要匹配的字符串放在第一个参数,或者将一个现成的正则表达式放进第一个参数。
1.
2. var reg = /abc/;
3. var reg2 = new RegExp(reg);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
修饰符
一个正则表达式有三个描述的属性g、i、m,表示三种特定的修饰功能。
1.
2. var reg = /abc/gim;
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
在直接量中直接在后面加上gim就可以了,但是在构造函数中,我们要把修饰符放在第二个参数上。
1.
2. var reg = new RegExp(‘abc’, ‘gim’);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这两种写出来的效果是一模一样的,但是由于构造函数书写起来太麻烦而且还需要转义字符,个人推荐用直接量来构造正则表达式。
正则表达式上面有一个方法test(),可以测试一个字符串符不符合这个匹配规则。
1.
2. var reg = /abc/g;
3. console.log(reg.test(‘abcabc’)); // true
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这样就实现了一个最基本的匹配了。
1.i (ignore) 可以忽略大小写。
2.g (global) 全局匹配。
1.
2. var reg = /abc/;
3. var str = ‘abcabcabc’;
4. console.log(str.match(reg)); // [“abc”, index: 0, input: “abcabcabc”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
不写g的时候会返回一个类数组,写上g会返回一个数组,里面有所有的3个abc。
1.
2. var reg = /abc/g;
3. var str = ‘abcabcabc’;
4. console.log(str.match(reg)); // [“abc”, “abc”, “abc”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
3.m (multiline)多行匹配
1.
2. var reg = /^e/igm;
3. var str = “abc\nebcd”;
4. console.log(str.match(reg));
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这个reg的作用是匹配以e开头的字符串,如果不加m,这个str是无法匹配的,因为\n也算一个字符,它后面的e不算是开头的字符,但是加上m之后,\n就会被正则表达式看成是正常的换行符,因此后面的e会被看做是下一行的开头的字符,因此就可以匹配出来了。
表达式
1.[abc] 查找方括号之间的任何字符。
匹配的是一位字符,不过是括号里面的任何一位都可以。
1.
2. var reg = /[abcd]/g;
3. var str = ‘code’;
4. console.log(str.match(reg)); // [“c”, “d”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
现在反悔的一个数组,把str中的所有a或者b或者c都返回出来了。
我们还可以写一个范围,比如[a-zA-Z0-9],这样就代表所有的英文字母和数字。
1.
2. var reg = /[a-zA-Z0-9]/g;
3. var str = ‘code1024’;
4. console.log(str.match(reg)); // [“c”, “o”, “d”, “e”, “1”, “0”, “2”, “4”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这样匹配出来的就是一个code1024
2.[^abc] 查找任何不属于方括号里面的字符
注意这里的“^”是写在方括号里面的,和前面的代表开头的“^”不是一个意思。当这个符号出现在方括号里面的时候,就代表非的意思,除了方括号里面写的,其他的字符都符合。
1.
2. var reg = /[^a-zA-Z]/g;
3. var str = ‘code1024’;
4. console.log(str.match(reg)); // [“1”, “0”, “2”, “4”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这个表达式的意思就是除了字母之外的其他字符都符合要求。
3.()
括号的作用是提高优先级和产生子表达式的作用。
4.|
或表达式,和[]差不多,可以配合()来使用。
1.
2. var reg = /a|b/g;
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这个正则表达式代表ab或者cb都可以。
1.
2. var reg = /(a|c)b)/g;
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
1.
2. var reg =/(blue|red)abc/;
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
如果不加括号的话,就是匹配blue或者redabc,现在加上括号了就是匹配blueabc或者redabc。
元字符
和表达式差不多,元字符是代表区间的。
1.“.”
这个符号可以匹配除了换行符(\n)和行结束符(\r),除了这两个字符之外其他的字符都可以匹配出来。
2.\w
这个元字符代表单词字符的意思,即A-Za-z0-9_
注意这个是可以匹配下划线的。
3.\W
除了\w的单词字符之外的其他字符。
4.\d
代表数字字符,即0-9
5\D
代表除了数字字符之外的其他字符。
6.\s
查找空白字符。
空白字符都包括:空格符、制表符(\t)、回车符(\r),换行符(\n),垂直换行符(\v)、换页符(\f)。
因此\s = [\n\t\r\v\f];
7.\S
查找非空白字符,即除了\s包括的空白字符之外的其他字符。
我们如果要匹配任意字符的话,就可以搭配起来使用了:
[\s\S]、[\d\D]、[\w\W]等都可以
8.\b
匹配单词边界。
1.
2. var str = ‘accb bdaccd efg’;
3. var reg = /\bacc/;
4. console.log(str.match(reg)); // [“acc”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这样匹配的就是第一个acc,后面的acc前面不是单词边界,因此不符合匹配规则。
9.\B
非单词边界
1.
2. var str = ‘accb bdaccd efg’;
3. var reg = /\Bacc/;
4. console.log(str.match(reg)); // [“acc”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
匹配的就是中间的acc。
注意,一个汉字就是一个单词,每一个汉字的两边都有默认的单词边界。
1.
2. var str = ‘维生素c’;
3. var reg = /\bc/;
4. console.log(str.match(reg)); // [“c”, index: 3, input: “维生素c”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
量词
以下的n代表一个变量,可以是表达式或者元字符。
1.n+
匹配至少一个变量n
1.
2. var str = ‘bcdaaaaagbaaasaaka’;
3. var reg = /a+/g;
4. console.log(str.match(reg)); // [“aaaaa”, “aaa”, “aa”, “a”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
可以把全部的a当做一位来匹配出来。
2.n*
匹配至少零个变量n
1.
2. var reg = /a*c/g;
3. var str = ‘abc’;
4. console.log(str.match(reg)); // [“c”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这种情况下只有前面是a*0才有可能匹配成功。
1.
2. var reg = /\w*/g;
3. var str = ‘aabbbccc’;
4. console.log(str.match(reg)); // [“aabbbccc”, “”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
最后有一个空串,因为前面的单词被\w匹配出来,最后还有一个空字符可以被\w0匹配出来。
两个字符之间都有一个空字符, ab-> [‘a’, ”, ‘b’];
3.n?
匹配0-1个变量n
1.
2. var reg=/(aa|b)?/g;
3. var str = ‘aaaaacb’;
4. console.log(str.match(reg)); // [“aa”, “aa”, “”, “”, “b”, “”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
中间的空串是因为匹配了空串,?取0的情况。
4.n{x}
可以规定匹配x个变量n
1.
2. var reg = /a{4}/;
3. var str = ‘aaaaa’;
4. console.log(str.match(reg)); // [“aaaa”, index: 0, input: “aaaaa”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
并且大括号可以写一个区间,中间用逗号链接。
1.
2. var reg = /a{1,4}/g;
3. var str = ‘aaaaa’;
4. console.log(str.match(reg)); // [“aaaa”, “a”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
或者第二个范围不写,这样就代表到无穷的范围。
1.
2. var reg = /a{1,}/g;
3. var str = ‘aaaaa’;
4. console.log(str.match(reg)); // [“aaaaa”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这里默认的是贪婪匹配,即尽可能匹配多个字符。
1.
2. var reg = /a{1,4}/g;
3. var str = ‘aaaaaa’;
4. console.log(str.match(reg)); // [“aaaa”, “aa”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
当我们在后面加一个”?”之后,就会变成最小匹配,尽可能匹配少的字符。
1.
2. var reg = /a{1,4}?/g;
3. var str = ‘aaaaaa’;
4. console.log(str.match(reg)); // [“a”, “a”, “a”, “a”, “a”, “a”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
当然,不只是{}后面可以加”?”,其他的比如*?、+?等等后面都可以加问号。
5.n$
匹配以n结尾的字符
1.
2. var reg = /ab$/;
3. var str = ‘abab’;
4. console.log(str.match(reg)); // [“ab”, index: 2, input: “abab”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
只能找到最后一组ab
6.^n
匹配以n开头的字符,注意不是[]里面的^。
1.
2. var reg = /^acb/g;
3. var str = ‘acbacb’;
4. console.log(str.match(reg)); // [“acb”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
只能匹配出来第一个acb。
1.
2. var reg = /^ab$/g;;
3. var str = ‘abcabc’;
4. console.log(str.match(reg)); // null
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这种写法就要求从a开头,挨着的b是结尾的。”abab“现在就匹配不出来了。
1.
2. var reg = /^abc$/g;;
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这种写法可以匹配固定字符串abc。
小题目:
写一个正则表达式,检验字符串首尾是否有数字。
1.
2. var reg = /^\d|\d$/gi;
3. console.log(reg.test(‘12asdfv’)); // true
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
7.n(?=xxx)
正向预查(正向断言)
1.
2. var reg = /x(?=abc)/g;
3. var str = ‘xabcxbcdaabc’;
4. console.log(str.match(reg)); // x
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
后面是abc的那个n,匹配的是n,后面有abc是一种条件。
如果n是一个空,那么就查找后面是abc的a前面那个空串了。
1.
2. var reg = /(?=abc)/g;
3. var str = ‘xabcxbcdaabc’;
4. console.log(str.match(reg)); // [“”, “”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
8.n(?!xxx)
非正向预查
匹配后面不是abc的n。
• 我们如果要匹配*、?等这些字符的话,就需要在前面加上一个转义字符“/”了。
RegExp对象
属性
1.global multiline ignoreCase
这三个属性可以检测是否有gim修饰。
2.source
代表正则表达式本身,即两个斜线中间的部分。
1.
2. var reg = /abc\w\d/gim;
3. console.log(reg.source); // abc\w\d
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
方法
1.exec
这个方法可以检测字符串中指定的值,返回知道的值并确定其位置。
1.
2. var reg = /abc/;
3. var str = “bcabcdabcda”;
4. console.log(reg.exec(str)); // [“abc”, index: 2, input: “bcabcdabcda”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
返回的是一个类数组,其中的index是匹配出的字符开始的位置。
不论执行多少次结果都一样。
jQuery源码里面就用了很多exec方法。
我们前面提到()括号除了优先级之外,还可以产生子表达式。
1.
2. var reg = /(a)(b)(c)/;
3. var str = “bcabcdabcda”;
4. console.log(reg.exec(str)); // [“abc”, “a”, “b”, “c”, index: 2, input: “bcabcdabcda”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
返回的类数组会把三个子表达式匹配的内容也写出来。
第一个子表达式的a是第一个abc里面的a,子表达式b是abc里面的b,子表达式c是abc里面的c。
当我们不想让括号匹配出来字符作为子表达式,只充当优先级的作用的时候,那么在()的最前面加上“?:”就可以取消子表达式的作用了。
1.
2. var reg = /(?:a)(?:b)(c)/;
3. var str = “bcabcdabcda”;
4. console.log(reg.exec(str)); // [“abc”, “c”, index: 2, input: “bcabcdabcda”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这里的”?:”叫做不记录子表达式。
而有了子表达式,我们也有相应的可以选择出子表达式的表达式。
反向引用:
匹配形如1221形式的字符串,比如abba,deed等。我们可以通过子表达式和反向引用就可以实现。
1.
2. var reg = /(\w)(\w)\2\1/ ;
3. var str = “abba”;
4. console.log(reg.exec(str)); // [“abba”, “a”, “b”, index: 0, input: “abba”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
\1代表和第一个子表达式匹配到的相同的内容,第一个\w匹配到什么这里的\1就是什么,\2同理。
以上是我们的正则表达式没有写g全局时候的情况,当我们写上g之后情况又会变得不一样了。
1.
2. var reg = /abc/g;
3. var str = “bcabcdabcda”;
4. console.log(reg.exec(str)); // [“abc”, index: 2, input: “bcabcdabcda”]
5. console.log(reg.exec(str)); // [“abc”, index: 6, input: “bcabcdabcda”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
执行第一次的结果和没有全局时候是一样的,但是从第二次开始就不同了。
第二次index变成了6,匹配的是第二组abc。
所以加上g之后,后面的匹配开始的位置是从上一次匹配结束的位置开始,而不是从头开始。
如果走到最后也没有符合的字符串的话,就会返回null。但是下一再执行会回到开头开始匹配。
这一切不同之处都是因为正则表达式还有一个属性叫做lastIndex,这个属性基本只有全局的时候才有作用。他会记录表达式上一次匹配完了之后的位置,然后让下一次匹配从lastIndex的位置开始。
如果我们中途改变了lastIndex 的数值,那么我们就可以改变下一次匹配开始的位置了。
1.
2. var reg = /abc/g;
3. var str = “bcabcdabcda”;
4. reg.lastIndex = 4;
5. console.log(reg.exec(str)); // [“abc”, index: 6, input: “bcabcdabcda”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
现在第一次执行结果匹配的就是第二个abc。
String上的一些方法
1.search
检索与正则表达式相匹配的位置,匹配成功即结束,与g无关,匹配失败则会返回-1。
2.match
找到一个或多个正则表达式的匹配,当非全局匹配时和exec显示的一样,是一个类数组,但是没有lastIndex,全局匹配时会返回一个数组。
3.split
split是以什么为分隔符把字符串分割成一个数组,这里的分隔符可以写一个正则表达式。
1.
2. var reg = /[-\d]/g;
3. var str = ‘a-c-d-a0f-e’;
4. console.log(str.split(reg)); // [“a”, “c”, “d”, “a”, “f”, “e”]
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
4.replace
替换与正则表达式匹配的字串。
这个方法非常非常重要,配合正则表达式会变得很灵活。
str.replace()有两个参数,第一个是目标字符串,可以是一个正则表达式,也可以是一个字符串,第二个参数是替换成的字符串。
1.
2. var str = ‘abcdbcdbcd’;
3. var reg = /ab/g;
4. console.log(str.replace(‘bc’, ‘aa’)); // aaadbcdbcd
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
只改第一个ab,后面的ab不改。
变成aaadbcdbcd。
如果想要全部替换就要写正则表达式。
1.
2. var str = ‘abcdbcdbcd’;
3. var reg = /(\w)(\w)/g;
4. console.log(str.replace(reg, ‘aa’)); // aaaaaaaaaa
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
现在有一个字符串“ab”,如果我们想要通过正则表达式来调换两个字母的位置,也需要用这个方法。
在第二个参数的位置,字符串里面可以写$、$1、$2…等等,其中$代表匹配出来的字符串,$1代表子表达式1,$2代表子表达式2.。
这样我们通过这种参数就可以实现简单的交换字母了。
1.
2. var str = ‘ab’;
3. var reg = /(\w)(\w)/g;
4. console.log(str.replace(reg, ‘$2$1’)); // ba
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
当然这只是第一种用法,还有第二种用法,在第二个参数中可以传一个函数,系统会自动调用这个函数,然后替换成这个函数返回的字符串。
var str = ‘ab’;
var reg = /(\w)(\w)/g;
console.log(str.replace(reg, function () {
return ‘abc’
})); // abc
整体被替换成了abc。
函数可以写形参,从前到后分别代表匹配出来的字符串、第一个子表达式、第二个子表达式等等。
1.
2. var str = ‘ab’;
3. var reg = /(\w)(\w)/g;
4. console.log(str.replace(reg, function ($, $1, $2) {
5. return $2 + $1;
6. })); // ba
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这个样子同样可以实现交换字母的功能。
正则表达式的大部分知识点就这么多哟~
下面我们来做几个题目练习一下正则表达式的使用吧!
1.将形如aaaabbbb字符串调换成bbbbaaaa形式。
1.
2. var str = ‘aaaabbbb’;
3. var reg = /(\w{4})(\w{4})/g;
4. str.replace(reg, ‘$2$1’);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
2.将‘my-fist-name’这样的用‘-’连接字符串变成小驼峰式的写法。
1.
2. var str = ‘my-first-name’;
3. var reg = /-(\w)/g;
4. str.replace(reg, function ($, $1) {
5. return $1.toUpperCase();
6. })
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
3.将一串连续的数字,变成如下形式:
100000000 —> 100,000,000
1.
2. var str = ‘100000000’;
3. var reg = /(?=((\B)\d{3})+$)/g;
4. console.log(str.replace(reg, ‘,’));
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
Web前端的知识之旅哟—— 异步加载JS
Posted on 2018-01-02
先介绍一下JSON:
我们传输数据就两种格式:xml和json。
xml是以html为模板,自定义标签名作为数据名来传递数据,书写起来很麻烦,现在多用json,很少使用xml。
json是传输数据的另一种格式,它是以对象为模板(本质上就是对象,但是用途有所区别,对象是本地使用,json是用来传输数据)。
不过我们传输数据的时候并不能将一个json对象直接传过去,我们只能传过去json形式的字符串,这个时候我们就需要用到JSON上的两个方法了。
JSON.parse() —> string->json
JSON.stringify() —> json->string
通过JSON上的这两个方法,我们就可以进行数据传输了。
1. var obj = {
2. ‘name’: ‘scarlett’,
3. ‘age’: 18
4. };
5. var string = JSON.stringify(obj);
6. console.log(string); // ‘{“name”:”scarlett”,”age”:18}’
7. var data = JSON.parse(string);
8. console.log(data); // Object {name: “scarlett”, age: 18}
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
• 我们的页面有三个树:DOMTree、CSSTree、renderTree。(实际上多于三个),renderTree上有两个规则:repaint和reflow,重绘和重排。
repaint是元素自身的位置和宽高不变,只改变颜色的之类的属性而不会导致后面的元素位置的变化的时候,renderTree发生的动作。
reflow是元素自身的位置或者宽高改变了从而导致的整个页面的大范围移动的时候,renderTree发生的动作。
所以我们在DOM操作的时候,要尽量避免重排。
JS异步加载部分
我们前面知道script标签的特点是会阻塞后面的DOM解析,只有当script标签下载完成并且全部执行完之后,浏览器才会继续解析DOM。
这样就导致了js加载有这样的缺点:加载工具方法本身没有必要阻塞文档,js加载会影响整个页面效率,一旦网速不好,那么整个网站将等待js加载而不进行后续渲染。
DOMTree就是解析DOM结构的,当我们在解析DOM的时候突然碰到一个script标签,那么这个script标签就会阻断DOMTree和CSSTree,然而我们有一些js的工具库并不操作DOM,这个时候我们就需要让这些工具库进行异步加载或者按需加载了。
以前有一种写法是将script标签写在body的最后面,等在DOM全部解析完成之后才加载js。
现在有了html5标准之后,就有了另一套异步加载js的方法了。
js异步加载的三种方案:
1.defer异步加载
我们在script标签的行间写一个defer=“defer”或者直接写defer,就可以让这个script变成异步加载了。但是这种异步只是针对下载方面,只有等DOMTree全部解析完成(不包括下载完里面的资源)后才会异步执行。而且这个方法只有IE和一些高版本的firefox和chrome可以用。
1.
2. <script type=“text/javascript” defer=“defer”>
3. console.log(‘hello’);
4. </script>
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
不过这一种方式可以在script标签里面写代码。
注意:IE6和IE7的异步加载最多只能有2个,当多余两个的时候必须等前两个加载完才会加载第三个。
所有defer的js代码都保证按照顺序执行。
2.async异步加载
async是asynchronous的缩写,它是html5的标准,下载完成之后就会立即异步执行,这种方法除了IE6-IE8其他的浏览器都好用。
1.
2. <script type=“text/javascript” async=“async”></script>
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
不过这种方式不能把代码写在script标签里面,只能引用。(虽然标准是这么写的,但是现在随着内核升级,async里面也可以写代码了,在没有src的情况下)。
而且async的js代码不能保证是顺序执行的。
• 这两个属性不能一起使用!
图中蓝色部分属于js加载过程,红色部分表示js执行过程,绿色代表DOM解析过程。
兼容性写法:
1.直接写两个script标签,一个是defer,一个是async。
但是这种方法有缺陷:IE高版本会加载两遍引起冲突,有些浏览器两个都没有会一个都加载不出来。
所以我们就需要用第二种方法了。
2.通过动态添加script标签。
“Script-inserted script elements now have async default to true, which can be set to false to make the scripts execute in insertion order.”
w3c的标准规定:动态添加的script标签是异步的。
通过这个特性,我们这里就可以封装一个兼容性的异步加载js的函数了。
1.
2. function asyncLoaded(url, callback){
3. var script = document.createElement(‘script’);
4. script.type = ‘text/javascript’;
5. if(script.readyState){//IE和高版本的chrome、firefox
6. script.onreadystatechange = function(){
7. if(script.readyState = ‘loaded’ || script.readyState == ‘complete’){
8. script.onreadystatechange = null;
9. callback && callback();
10. }
11. }
12. }else{
13. script.onload = function(e){//Safari Chrome Opera Firefox
14. callback && callback();
15. }
16. }
17. script.src = url;
18. document.body.appendChild(script);
19. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
需要注意的是:readtState的if-else一定要写在script.src=url和appendChild之前,因为电脑速度可能会很快,刚走到src=url部分就已经加载完毕了,readyState已经变成了loaded,后面就不会触发onreadystatechange事件了。
这里src部分的下载是异步的,不会阻塞后面的代码的执行,也就是说可以一边把script插入到DOM中一边下载资源。
还有一点,如果我们的回调函数是写在需要加载进来的文件里面的方法的话,我们就需要把这个方法放到匿名函数里面,这样在语法解析的时候才不会因为函数未声明而报错。
• 异步加载js不允许使用document.write这个方法。它会清除文档流,一旦执行就会把全部的标签都清除掉,包括自身的script标签。
JS加载时间线(performace timing line)
1、创建Document对象,开始解析web页面。解析HTML元素和他们的文本内容后添加Element对象和Text节点到文档中。这个阶段document.readyState = ‘loading’。
2、遇到link外部css,创建线程加载,并继续解析文档。
3、遇到script外部js,并且没有设置async、defer,浏览器加载,并阻塞,等待js加载完成并执行该脚本,然后继续解析文档。
4、遇到script外部js,并且设置有async、defer,浏览器创建线程加载,并继续解析文档。 对于async属性的脚本,脚本加载完成后立即执行。(异步禁止使用document.write())
5、遇到img等,先正常解析dom结构,然后浏览器异步加载src,并继续解析文档。
6、当文档解析完成,document.readyState = ‘interactive’。
7、文档解析完成后,所有设置有defer的脚本会按照顺序执行。(注意与async的不同,
但同样禁止使用document.write());
8、document对象触发DOMContentLoaded事件,这也标志着程序执行从同步脚本执行阶段,
转化为事件驱动阶段。
9、当所有async的脚本加载完成并执行后、img等加载完成后,document.readyState = ‘complete’,window
对象触发load事件。
10、从此,以异步响应方式处理用户输入、网络事件等。
JS的时间线是非常重要的知识点,希望大家能背下来这一块哟~
Web前端的知识之旅哟—— 鼠标事件与键盘事件
Posted on 2018-01-02
鼠标事件
鼠标事件有很多:click、mousedown、mousemove、mouseup、contextmenu、mouseover、mouseout。这些方法的语义化都很好,大家看名字基本都懂是什么意思。
其中,contextmenu是右键出现菜单事件,并不是右键事件。
click是一个完整的点击事件,包括mousedown和mouseup。
mouseover和mouseout是一组事件,对应于鼠标的移入移出事件。
• 所有的鼠标事件都有clientX和clientY,代表的是鼠标点击的位置,我们可以通过e.clientX和e.clentY来查看。
• 左键点击的顺序是:mousedown、mouseup、click
1.
2. var firstTime,
3. lastTime,
4. flag = false;
5. document.onclick = function (e) {
6. console.log(‘click’);
7. }
8. document.onmousedown = function (e) {
9. console.log(‘mousedown’);
10. }
11. document.onmouseup = function (e) {
12. console.log(‘mouseup’);
13. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
输出的结果是mousedown、mouseup、click。
• 当我们同时绑定了click事件和mousedown事件后,如何解决mousedown和click的冲突问题?
其实方法很简单,我们可以通过按下鼠标的事件来判断是执行click还是mousedown事件。
1.
2. var firstTime,
3. lastTime,
4. flag = false;
5. document.onclick = function (e) {
6. if (flag) {
7. console.log(‘click’);
8. }else {
9. console.log(‘mousedown’);
10. }
11. }
12. document.onmousedown = function (e) {
13. firstTime = new Date().getTime();
14. }
15. document.onmouseup = function (e) {
16. lastTime = new Date().getTime();
17. if (lastTime - firstTime < 200) {
18. flag = true;
19. }else {
20. flag = false;
21. }
22. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
• 如何判断鼠标点击的是左键还是右键?
click事件永远都是左键,有左键和右键的区别的是mousedown事件。
再mousedown的事件对象中有一个属性叫做button,这个属性的值是0的时候代表我们按下的是左键,1的时候代表按下的中键,2的时候代表按下的是右键。
1.
2. document.onmousedown = function (e) {
3. if (e.button == 0) {
4. console.log(‘left’);
5. }else if (e.button == 1) {
6. console.log(‘mid’);
7. }else {
8. console.log(‘right’);
9. }
10. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这里我们就可以重新回顾一下拖拽事件了:
1.
2. var div = document.getElementsByTagName(‘div’)[0]
3. function drag(elem) {
4. var disX,
5. dixY;
6. addEvent(elem, ‘mousedown’, function (e) {
7. var event = e || window.event;
8. disX = event.clientX - parseInt(getStyle(elem, ‘left’));
9. disY = event.clientY - parseInt(getStyle(elem, ‘top’));
10.
11. addEvent(document, ‘mousemove’, mouseMove);
12. addEvent(document, ‘mouseup’, mouseUp);
13. stopBubble(e);
14. cancelHandler(e);
15. });
16.
17. function mouseMove(e) {
18. var event = e || window.event;
19. elem.style.left = event.clientX - disX + ‘px’;
20. elem.style.top = event.clientY - disY + ‘px’;
21. }
22. function mouseUp (e) {
23. removeEvent(document, ‘mousemove’, mouseMove);
24. removeEvent(document, ‘mouseup’, arguments.callee);
25. }
26. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这里我们为了不因为鼠标移动过快导致移出div,我们将mouseover事件绑定到了document上。
• 针对鼠标帧频过快的问题,IE中有一个也可以说是另一种事件捕获的方法——domEle.setCapture()。
调用这个方法之后,页面后续的所有操作就会全部都归于这个domEle上,比如div有一个拖拽事件,不过mousemove绑定在div上,只要调用了这个方法,当鼠标拖拽过程中鼠标移出了div,这个时候有效的鼠标移动就变成了document上的mousemove事件了,但是它还是归于了div上,因此div还是可以跟着鼠标移动。
同时还有一个domEle.releaseCapture()方法可以来释放这个捕获行为。
键盘事件
键盘事件主要有三个:keydown、keypress、keyup。
触发顺序分别是keydown、keypress、keyup。
1.
2. document.onkeydown = function (e) {
3. console.log(‘keydown’);
4. }
5. document.onkeypress = function (e) {
6. console.log(‘keypress’);
7. }
8. document.onkeyup = function (e) {
9. console.log(‘keyup’);
10. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
输出的结果是keydown、keypress、keyup。
keydown和keypress可以响应连续操作,我们一直按着键盘就会一直触发事件。
keypress的范围和keydown有所区别:
keydown可以响应任意键盘按键,keypress只能响应字符类按键,也就是有ASCII码的字符的按键,像字母数字回车空格之类的。
事件对象的属性方面:
只有keypress有charCode属性,这个属性代表的我们输入的这个字符的ASCII码,配合SHIFT之类的按键可以直接获取大写字母等。
keyCode和which每个方法都有,表示的是这个键的唯一标示,可以告诉浏览器我们按下的是键盘上的哪一个键,比如空格是32,32就代表空格。不过我们一般都用which,keyCode用的较少。
• String上有一个方法叫做fromCharCode,可以接受一个Unicode值(包含ASCII值),然后返回对应的字符串,我们可以配合这个方法和charCode来直接获取输入的字符。
1.
2. document.onkeypress = function (e) {
3. console.log(String.fromCharCode(e.charCode));
4. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
窗体操作类(window上的事件)
load事件
这个事件window.onload和在body标签上写onload是一样的效果,作用是等页面中所有的东西都下载完成再执行里面的代码。
scroll事件
这个方法是监听滚轮滚动的事件,我们可以用这个事件来写一个兼容版的fix定位。
1.
2. function beFixed(ele){
3. var initPosX = ele.getPosition().w,
4. initPosY = ele.getPosition().h;
5. addEvent(window, ‘scroll’, function(e){
6. ele.style.top = initPosY + getScrollOffset().h + ‘px’;
7. ele.style.left = initPosX + getScrollOffset().w + ‘px’;
8. })
9. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
Web前端的知识之旅哟—— 事件处理模型与事件委托
Posted on 2018-01-02
事件处理模型——事件冒泡、事件捕获
上一篇介绍了事件的绑定,我们这里先写一个三层div嵌套的结构并且给每一个div都加一个点击事件。
1.
2. .wrapper {
3. width: 200px;
4. height: 200px;
5. background-color: red;
6. }
7. .box {
8. width: 100px;
9. height: 100px;
10. background-color: green;
11. }
12.
13. .content {
14. width: 50px;
15. height: 50px;
16. background-color: black;
17. }
18.
CSS; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
1.
2. var wrapper = document.getElementsByClassName(‘wrapper’)[0],
3. box = document.getElementsByClassName(‘box’)[0],
4. content = document.getElementsByClassName(‘content’)[0];
5. wrapper.onclick = function () {
6. console.log(‘wrapper’);
7. }
8. box.onclick = function () {
9. console.log(‘box’);
10. }
11. content.onclick = function () {
12. console.log(‘content’);
13. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
现在我们点击最外层的wrapper,控制台打印的wrapper。
点击box,却打印出来box和wrapper
点击content打印出来content、box、wrapper
这个现象就是我们所说的事件冒泡。
什么叫冒泡?
在结构上(非视觉上)嵌套关系的元素,会存在事件冒泡的功能,即同一事件,子元素冒泡向父元素,结构上的自底向上。(这里的底是结构上的底,视觉上是自顶向下)
大部分事件都有事件冒泡现象,并且所有的浏览器都有事件冒泡。
结构上的冒泡,和视觉的位置没有关系,我们看一下三个方块视觉上分开的例子:
1.
2. .wrapper {
3. width: 200px;
4. height: 200px;
5. background-color: red;
6. position: absolute;
7. }
8. .box {
9. width: 100px;
10. height: 100px;
11. background-color: green;
12. position: absolute;
13. left: 200px;
14. top: 200px;
15. }
16.
17. .content {
18. width: 50px;
19. height: 50px;
20. background-color: black;
21. position: absolute;
22. left: 100px;
23. top: 100px;
24. }
CSS; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
我们点击content部分之后依然会把box和wrapper都打印出来。
并不是所有的事件都有冒泡,focus、blur、change、submit、reset、select等方法就没有事件冒泡现象。
事件捕获:
结构上(非视觉上)嵌套关系的元素,会存在事件捕获功能,即同一事件,自父元素捕获至子元素(事件源元素),结构上的自顶向下。
addEventListener最后一个参数就是是否开始事件捕获,当我们填true的时候,就代表开启了事件捕获。只要开启了事件捕获,就不会冒泡了,如果不捕获的话,就遵循事件冒泡。
因为addEventListener只有chrome有,因此事件捕获也只有chrome浏览器有。
依然是上面的那个例子:
1.
2. var wrapper = document.getElementsByClassName(‘wrapper’)[0],
3. box = document.getElementsByClassName(‘box’)[0],
4. content = document.getElementsByClassName(‘content’)[0];
5. wrapper.addEventListener(‘click’, function (e) {
6. console.log(‘wrapper’);
7. }, true);
8. box.addEventListener(‘click’, function (e) {
9. console.log(‘box’);
10. }, true);
11. content.addEventListener(‘click’, function (e) {
12. console.log(‘content’);
13. }, true);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
现在点击content之后,顺序是wrapper、box、content。
当事件冒泡和事件捕获同时存在的时候,事件冒泡和事件捕获的触发顺序则为:先捕获,再冒泡。
1.
2. var wrapper = document.getElementsByClassName(‘wrapper’)[0],
3. box = document.getElementsByClassName(‘box’)[0],
4. content = document.getElementsByClassName(‘content’)[0];
5. wrapper.onclick = function () {
6. console.log(‘wrappeBubbler’);
7. }
8. box.onclick = function () {
9. console.log(‘boxBubble’);
10. }
11. content.onclick = function () {
12. console.log(‘contentBubble’);
13. }
14. wrapper.addEventListener(‘click’, function (e) {
15. console.log(‘wrapper’);
16. }, true);
17. box.addEventListener(‘click’, function (e) {
18. console.log(‘box’);
19. }, true);
20. content.addEventListener(‘click’, function (e) {
21. console.log(‘content’);
22. }, true);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
结果是先捕获再冒泡。
但是当我们把捕获写到冒泡前面的时候,顺序好像发生了变化。
wrapper–>box–>contentBubble–>content–>boxBubble–>wrapperBubble
这里是因为点击content,并不属于冒泡,而是属于事件执行,我们先绑定的boxBubble,所以就先捕获,再事件执行,再冒泡,这与我们的结论没有冲突。
取消冒泡和阻止默认事件
有时候冒泡或者默认事件会对我们的功能造成影响,因此我们需要适时地取消冒泡和默认事件。
我们绑定事件的处理函数的时候,可以传递一个形参,代表我们的事件对象,一般是e或者event,系统会自动帮我们捕获事件源对象并且把事件源对象传入。
取消冒泡的方法
1.w3c标准方法:event.stopPropagation()
1.
2. var wrapper = document.getElementsByClassName(‘wrapper’)[0],
3. box = document.getElementsByClassName(‘box’)[0],
4. content = document.getElementsByClassName(‘content’)[0];
5. content.onclick = function (e) {
6. console.log(‘content’);
7. e.stopPropagation();
8. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
现在我们点击content之后就没有冒泡了,但是点击box还是有冒泡的,因为我们没有取消box的冒泡。
IE9以及以下的版本不支持这个方法。
2.event.cancelBubble = true
这个属性是IE的,不过一些高版本的浏览器也有这个属性,只要让这个属性的值等于true,同样也可以取消事件冒泡。
封装一个兼容性的取消事件冒泡的方法:
1.
2.
3. function stopBubble(event) {
4. if(event.stopPropagation) {
5. event.stopPropagation();
6. }else {
7. event.cancelBubble = true;
8. }
9. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
默认事件
当我们在浏览器中点击右键,会弹出一个菜单,这就是一个默认事件contextmenu。还有a标签,即使我们不写跳转的页面,也会自动刷新页面,这也是一个默认事件。
移动端的默认事件更多。
默认事件有好的也有不好的,这就需要我们把不需要的默认事件阻止掉。
阻止默认事件
1.return false
我们只要在处理函数最后写上 return false就可以阻止默认事件了。
1.
2. document.oncontextmenu = function () {
3. console.log(‘menu’);
4. return false;
5. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
现在我们在页面上右键就不会出现菜单了。
不过要注意的是,这种写法只能用在句柄方式绑定的事件上。
2.e.preventDefault()
1.
2. documet.addEventListener(‘contextmenu’, function (e) {
3. console.log(‘menu’);
4. e.preventDefault();
5. },false);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这是w3c标准的阻止默认事件的方法,句柄也同样可以使用。
不过IE9以下不兼容。
3.e.returnValue = false
这个是IE的方法,事件源对象上的属性returnValue代表是否有默认事件,直接返回false就可以阻止默认事件了。现在高版本的浏览器也有这个属性。
1.
2. document.attachEvent(‘oncontextmenu’, function (e) {
3. e.returnValue = false;
4. });
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
现在我们也可以封装一个兼容性的阻止默认事件的方法了:
1.
2. function cancelHandler(event) {
3. if(event.preventDefault) {
4. event.preventDefault();
5. }else{
6. event.returnValue = false;
7. }
8. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
小例子:阻止a标签不跳转
1.
2. var a = document.links[0];
3. a.addEventListener(‘click’, funciton (e) {
4. e.cancelHandler(e);
5. },false);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这样a标签就不会跳转了。
同时我们还可以用a标签的第四个用处,协议限定符来阻止默认事件。
1.
2. <a href=“javascript: void(0); “>www.baidu.com</a>
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
不仅仅是0,只要填写一个代表false的值,就可以取消掉默认事件。
事件对象
在IE中,系统不会把事件对象传到方法中,因此我们的参数e或者event在IE中是不好用的,IE会把事件对象传递到window.event上,所以当我们使用事件对象的时候,就要写兼容性的写法:
1.
2. var event = e || window.event;
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这样就可以正常地获取到事件对象了。
事件委托
事件源对象
我们现在有一个ul,下面有十万个li,当我们给父级的ul添加第一个点击事件之后,由于事件冒泡的存在,不论我们点击哪一个li都会调用父级的点击事件处理函数,这个时候触发父级ul的点击函数的那个li就被称之为事件源对象。
event.target 是火狐的获取事件源对象
event.srcElement 是IE的获取事件源对象
chrome两种都有
因此我们在获取事件源对象的时候也需要写兼容性写法, 配合刚才的事件对象的兼容性写法就是这个样子的:
1.
2. oUl.addEventListener(‘click’, function (e) {
3. var event = e || window.event;
4. var tar = event.target || event.srcElement;
5. console.log(tar);
6. }, false);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
我们利用事件源对象和事件冒泡来处理的方式就叫做事件委托。
1.
2. oUl.addEventListener(‘click’, function (e) {
3. var event = e || window.event;
4. var tar = event.target || event.srcElement;
5. console.log(tar.innerHTML);
6. }, false);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
事件委托的优点:
1.性能 不需要循环所有的子元素一个个绑定事件
2.灵活 当有新的子元素被加入的时候不需要重新绑定事件
JWeb前端的知识之旅哟—— 事件的绑定与解除
Posted on 2018-01-02
事件是交互体验的核心功能,非常非常重要。
事件是每一个浏览器本来就有的,我们只是给相应的事件添加了一个回调函数。
下面先看一个拖拽事件的例子来感受一下事件:
1. div.addEventListener(‘mousedown’, function (e){
2. var disX = e.clientX - parseInt(getStyle(this, ‘left’)),
3. disY = e.clientY - parseInt(getStyle(this, ‘top’));
4. document.addEventListener(‘mousemove’, mouseMove, false);
5. div.addEventListener(‘mouseup’, function (e) {
6. document.removeEventListener(‘mousemove’, mouseMove, false);
7. }, false);
8. }, false);
9.
10. function mouseMove(e) {
11. div.style.left = e.clentX - disX + ‘px’;
12. div.style.top = e.clientY - disY + ‘px’;
13.
14. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
我们首先看一下如何绑定事件,这里我们以点击事件为例。
绑定事件
1.句柄方式
1. var div = document.getElementsByTagName(‘div’)[0];
2. div.onclick = function (e) {
3. console.log(‘a’);
4. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
后面的这个函数就叫做事件处理函数,也称回调函数,当我们点击事件触发的时候,就会执行后面的处理函数。
事件可以持续监听,并不是执行完一次就失效,因此这个事件监听部分并不属于js引擎,因为js引擎是单线程的,事件监听属于内核的其他模块部分,一旦事件触发,事件监听就会把处理函数放入执行队列,等待js引擎来执行。
虽然句柄方式的兼容性很好,但是一个元素的一种事件只能绑定一个函数。
基本等同于写在行间的事件。
1. <div onclick=“console.log(‘a’)“></div>
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这两种写法是一致的。
一些比较简单的函数可以用这种方式来写。
2.ele.addEventListener(type, handle, false)方法。
1. div.addEventListener(‘click’, function(e) {
2. console.log(‘a’);
3. }, false);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这里面有三个参数,第一个参数是事件类型,第二个参数是处理函数,第三个参数是是否捕获。
处理函数可以直接在addEventListener方法里面写一个匿名函数,也可以在外面写一个命名函数,然后在放法里面写函数的引用。
这种方法更加通用常见,而且一种事件可以绑定多个函数,但是同一个处理函数只能绑定一次。
1.
2. function test1 () {
3. console.log(‘a’);
4. }
5. function test2() {
6. console.log(‘a’);
7. }
8. div.addEventListener(‘click’, test1, false);
9. div.addEventListener(‘click’, test2, false);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
点击一次同时打印a和b。
• 这是唯一一个有事件捕获的方法。
注意:即使函数体相同,两个函数也不是一个函数。
不过很遗憾的是,这种方法在IE9以下不兼容。
3.ele.attachEvent(‘on’ + type, handle)
这个方法是IE独有的方法,一个事件同样可以绑定多个处理函数。
1.
2. div.attachEvent(‘onclick’, function (){
3. console.log(‘a’);
4. });
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
基本和addEventListener差不多,但是有一点区别是,当同一个函数绑定多次的时候,addEventListener是只执行一次,但是attachEvent会绑定几次执行几次。
1.
2. function test () {
3. console.log(‘a’);
4. }
5. div.attachEvent(‘onclick’, test);
6. div.attachEvent(‘onclick’, test);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
现在点击一次会打印两个a。
现在这里有一个笔试常考的绑定事件的题:使用原生js,addEventListener,为每一个li绑定一个点击事件,输出他们的顺序。
这里就要注意这个题考察的不仅仅是绑定事件,更多的是闭包的运用。
1.
2. var $Li = document.getElementsByTagName(‘li’);
3. for (var i = 0, len = $Li.length; i < len; i++) {
4. (function (n) {
5. $Li[n].addEventListener(‘click’, function () {
6. console.log(n);
7. },false);
8. } (i))
9. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
事件处理程序的运行环境
1.句柄绑定方式中,函数里面的this指向元素本身。
2.addEventListener方式中,函数里面的this也是指向元素本身。
3.attachEvent中,函数里面的this指向的是window而不是元素本身,这算是IE的一个BUG。针对这种情况,我们就需要把函数提取出来,然后在attachEvent的时候用call来改变函数内部this的指向。
1. div.attachEvent(‘onclick’, function () {
2. test.call(div);
3. }, false);
4.
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
有了以上的知识,我们就可以封装一个兼容性的事件绑定函数了。
1.
2. function attachEvent(ele, type, handle) {
3. if (ele.addEventListener) {
4. ele.addEventListener(type, handle, null);
5. }else if (ele.attachEvent) {
6. ele.attachEvent(‘on’ + type, function () {
7. handle.call(ele);
8. });
9. }else {
10. ele[‘on’ + type] = handle;
11. }
12. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这样事件绑定基本就完成了,但是还可以优化一下,在第二个IE的方法中我们用了一个匿名函数,这样这个函数就无法解除绑定了,因此可以优化成命名函数。
1.
2. function attachEvent(ele, type, handle) {
3. if (ele.addEventListener) {
4. ele.addEventListener(type, handle, null);
5. }else if (ele.attachEvent) {
6. ele[‘temp’ + type + handle] = handle;
7. ele[type + handle] = function () {
8. ele[‘temp’ + type + handle].call(ele);
9. };
10. ele.attachEvent(‘on’ + type, ele[type + handle]);
11. }else {
12. ele[‘on’ + type] = handle;
13. }
14. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这种写法应该是业内公认的事件绑定方式。这里专门处理了IE方法中的匿名函数问题,我们用元素自身的一个属性来保存了这个处理函数。
解除事件处理程序
1.句柄方式
ele.onclick=null
这样很简单的就可以解除绑定的事件处理函数了。
2.ele.removeEventListener(type, handle, false)
针对的addEventListener的解除绑定。
但是这里要注意,只有命名函数才可以解除绑定,当绑定的函数是匿名函数的时候,是没有办法解除绑定的。
1.
2. div.addEventListener(‘click’, function (){console.log(‘a’);}, false);
3. div.removeEventListener(‘click’, function (){console.log(‘a’);}, false);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这是没有办法解除绑定的,因为这是两个匿名函数。
1.
2. function test() {
3. console.log(‘a’);
4. }
5. div.addEventListener(‘click’, test, false);
6. div.removeEventListener(‘click’, test, false);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
必须改成这种方式才可以解除绑定。
3.ele.detachEvent(‘on’ + type, handle)
针对IE的attachEvent的解除绑定。
也是同一个函数才可以解除绑定,匿名函数无法解除绑定。
封装兼容性的解除绑定函数:
1.
2. function remvoeEvent(ele, type, handle) {
3. if(ele.removeEventListener) {
4. ele.removeEventListener(type, handle, false);
5. }else if (ele.detachEvent) {
6. ele.detachEvent(‘on’ + type, handle);
7. }else {
8. ele[‘on’ + type] = null;
9. }
10. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
今天的事件只是就到这里哟~
Web前端的知识之旅哟——脚本化CSS
Posted on 2018-01-02
这篇文章主要介绍几种查看css样式的方法。
读写css属性
每一个dom元素都有一个属性style,dom.style里面存放的这个元素的行间样式,我们可以通过这个属性来读写元素的行间样式。
注意:
1.我们碰到float这样的关键字属性的时候,前面要加一个css前缀。
float—>cssFloat
2.复合属性必须拆解
border: 1px solid red;
这种属性是不允许出现的,只能写border-width、border-color、border-style。
3.当css中的样式是用“-”连接起来的时候,我们要变成小驼峰式写法。
background-color —> backgroundColor;
这个属性只能读写行间样式,我们写在css的样式是不能获取的,因此我们通过这个属性加样式的时候要注意权重值问题。
同时,我们在js中写样式只能往行间写,永远也写不到css文件里面。
查询计算样式
window上面有一个方法叫做getComputedStyle可以来获取元素的计算样式,也就是css样式。
1. window.getComputedStyle(ele. null);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这里的样式是取优先级最高的,不只是行间样式,所有的只要是表现出来的样式都可以获取出来。
同时,返回的计算样式的值都是绝对值,没有相对单位。
我们写的background-color: red会返回rgb(255,0,0),em单位或者百分比的单位都会化成具体的像素值返回。
这个方法有两个参数,第一个参数是我们要获取的元素。
第二个参数是一个字符串,代表我们是否要获取这个元素上面的某一个伪元素,如果不的话,就填写null,否则就填写要获取的这个元素的哪一个伪元素。
1. div:after{
2. width: 100px;
3. height; 100px;
4. background-color: red;
5. }
6. window.getComputedStyle(div, ‘after’).width; // 100px
CSS; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
但是很遗憾的是IE8以及以下的版本不兼容这个方法。
IE8有一个特殊的样式currentStyle。
dom.currentStyle也会返回一个样式表,和上面的基本一样,唯一的区别在于返回的计算样式的值不是经过转换的绝对值,而是我们写什么值就会返回什么值。
现在有了这些方法和属性,我们就可以封装一个兼容性的获取样式的函数了。
1. function getStyle(obj, prop, fake) {
2. var fake = fake || null;
3. if(obj.currentStyle) {
4. return obj.currentStyle[prop];
5. }else {
6. return window.getComputedStyle(obj, fake)[prop];
7. }
8. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
• 脚本化样式表
在document上有一个属性叫做styleSheets,这个属性储存了一个html文档所有的css样式表的集合,我们可以来操作style标签,不过在实际中基本是用不到的,所以这里就不过多介绍了。
最后我们可以写一个让小方块运动的函数:
1.
2. div#demo {
3. width: 100px;
4. height: 100;
5. position: absolute;
6. left: 0;
7. top: 0;
8. }
9.
10.
11. var div = document.getElementById(‘demo’);
12. var timer = window.setInterval(function () {
13. div.style.left = parseInt(getStyle(div, ‘left’)) + 1 + ‘px’;
14. ifparseInt((getStyle(div, ‘left’)) > 1000) {
15. clearInterval(timer);
16. }
17. }, 10);
CSS/JS; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
Web前端的知识之旅哟——滚动条与元素尺寸
Posted on 2018-01-02
查看滚动条的滚动距离
js中有两套方法可以查看当前滚动条的滚动距离。
第一套是这样的:
window.pageXOffset/window.pageYOffset
这个方法可以查看滚动条的横轴和纵轴的滚动距离,但是很遗憾的是IE8以及以下的版本不兼容。
因此针对于IE,我们就需要有第二套方法:
document.body.scrollLeft/doucment.body.scrollTop
document.documentElement.scrollLeft/document.documentElement.scrollTop
虽然IE可以使用这两个方法,但是这两个方法不仅仅是IE才可以使用。
这里要说明的是,这两种方法要一起使用,因为浏览器的兼容性问题,有的浏览器是document.body有值,有的是document.documentElement有值,但是所有的浏览器都只有一个有值,不会两个都有或者一个都没有,而且这里的没有值不代表是null,而是0,因此我们使用的时候一般都是两个一起使用。
针对兼容性的问题,我们现在就可以封装一个函数,求滚动条滚动距离的方法。
1. function getScrollOffset(){
2. if(window.pageXOffset) {
3. return {
4. x: window.pageXOffset,
5. y: window.pageYOffset
6. }
7. }
8. return {
9. x: document.body.scollTop + document.documentElement.scrollTop,
10. y: document.body.scrollLeft + document.documentELement.scrollLeft;
11. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
当浏览器不是IE的时候,可以直接使用window.pageXOffset和window.pageYOffset的方法,当没有这两个的时候,我们才需要来计算。
查看视口的尺寸
视口就是我们的可视区,因为我们改变浏览器的大小的时候会改变可视区的大小,那么我们如何来查看当前浏览器的可视区大小?
这里我们依然有两套方法:
第一套是window.innerHeight/window.innerWidth;
这个方法可以直接获取到当前可视区的宽高,但是依然很遗憾的是IE8以及以下的版本不兼容。
这个时候就需要第二套方法了:
在标准模式下,doucment.documentElement.clientWidth/document.documentElement.clientHeight
在任何浏览器都兼容。
在怪异模式(混杂模式)下,
document.body.clientWidth/document.body.clientHeight
才能查看视口大小,而不能使用上面标准模式下的方法。
• 什么是怪异模式和标准模式呢?
浏览器的渲染模式有两种:标准模式和怪异模式。我们常用的是标准模式,怪异模式是防止浏览器升级版本过高对后面版本你的代码不兼容,让浏览器可以向后兼容其他低版本的代码的法规则,比如IE6的怪异模式下可以兼容IE5个IE4的语法。
• 我们如何变成怪异模式呢?
其实很简单,只需要把我们html代码的第一行的<!DOCTYPE HTML>去掉就可以变成怪异模式了,而加上这一行代码就可以变成标准模式。
• 如何判断怪异模式还是标准模式?
document上中有一个方法叫做compatMode,当浏览器处于怪异模式的时候会返回字符串“BackCompat”,在标准模式下可以返回“CSS1Compat”,通过这个方法我们就可以判断了。
知道了什么是标准模式和怪异模式以及如何判断的情况下,我们就可以封装一个兼容性的函数,返回浏览器的视口大小。
1.
2. function getViewportOffset () {
3. if(window.innerWidth) {
4. return {
5. w: window.innerWidth,
6. h: window.innerHeight
7. }
8. }
9. if(document.compatMode == ‘CSS1Compat’) {
10. return {
11. w: document.documentElement.clientWidth,
12. h: document.documentElement.clientHeight
13. }else {
14. return {
15. w: document.body.clientWidth,
16. h: document.body.clientHeight
17. }
18. }
19. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
查看元素的几何尺寸
domEle.getBoundingClientRect()
在获取的dom元素上,有一个getBoundingClientRect方法,这个方法可以获取到元素的宽高和左上点的位置以及有下点的位置(width、height、top、left、right、bottom),注意这里的宽高是指除去了margin的盒子模型的宽高。
但是老版本的IE浏览器没有实现width和height,那么我们在老版本的IE计算宽高的时候就需要用bottom-top和right-left来计算宽高值了。
还有一点需要注意的是,这里的宽高也不是实时更新的,数据只是一个副本。
我们依然可以封装一个函数,可以返回元素的宽高。
1.
2. Element.getElementOffset() {
3. var objData = this.getBoundingClientRect();
4. if(objData.width) {
5. return {
6. w: objData.width,
7. h: objData.height
8. }
9. }else {
10. return {
11. w: objData.right - objData.left,
12. h: objData.bottom - objData.top
13. }
14. }
15. }
16.
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
滚动条滚动
让滚动条滚动的方法有三个:scroll、scrollTo、scrollBy。
这三个方法都有两个参数,分别是x值和y值。前两个方法的作用一模一样,都是让滚动条滚动到(x,y)的位置,但是最后一个scrollBy有一些区别,它是让滚动条相对于上一个位置滚动多少距离。
我们可以简单的用这个方法来实现一个自动阅读的功能:
setInterval(function () {
scrollBy(0, 10);
},50);
另一个查看元素尺寸的方法
dom.offsetWidth/dom.offsetHeight
虽然前面有一个ele.getBoundingClientRect()方法,但是由于这个方法名字是在太长了,因此用的并不是非常多,相比较而言,这两个dom元素的属性用的更多一些。
这里获取的宽高值和上面的一样,都是除去margin后的盒子模型的宽高。
查看元素的位置
dom.offsetLeft/dom.offsetTop
这两个值分别是元素距离左侧和上侧的距离,这里的值是相对于有定位的父级而言的,如果没有有定位的父级的话,才是相对于文档的坐标。
那么现在就来了一个问题:如何找到有定位的父级呢?
domEle上面还有一个属性是offsetParent,这个属性可以查看到元素的有定位的父级,如果没有的话就会返回body,而body的offsetParent则是null。
我们利用上面的信息,可以来封装一个函数,求元素的相对于文档的坐标。
1.
2. Element.prototype.getPosition = function (){
3. if (!this.offsetParent){
4. return {
5. “w”: this.offsetLeft,
6. “h”: this.offsetTop
7. }
8. }
9. var width = this.offsetLeft,
10. height = this.offsetTop,
11. ele = this.offsetParent;
12. while (ele.offsetParent){
13. width += this.offsetParent.offsetLeft;
14. height += this.offsetParent.offsetTop;
15. ele = ele.offsetParent;
16. }
17. return {
18. “w”: width,
19. “h”: height
20. }
21. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
那么今天要介绍的关于获取dom元素的尺寸以及滚动条相关的知识就这些哟~
Web前端的知识之旅哟——日期对象与定时器
Posted on 2018-01-02
日期对象
日期对象的方法在w3c上其实有介绍:
http://www.w3school.com.cn/jsref/jsref_obj_date.asp
这里只是大体的提一下:
日期对象也是javascript的内置对象之一,我们可以直接使用。
var date = new Date();
在文档中我们也可以看到它的方法很多,但是常用的其实并没有那么多。
|Date()|返回当日的日期和时间。|
|getDate()|从 Date 对象返回一个月中的某一天 (1 ~ 31)。|
|getDay()|从 Date 对象返回一周中的某一天 (0 ~ 6)。|
|getMonth()|从 Date 对象返回月份 (0 ~ 11)。|
|getFullYear()|从 Date 对象以四位数字返回年份。|
|getYear()|请使用 getFullYear() 方法代替。|
|getHours()|返回 Date 对象的小时 (0 ~ 23)。|
|getMinutes()|返回 Date 对象的分钟 (0 ~ 59)。|
|getSeconds()|返回 Date 对象的秒数 (0 ~ 59)。|
|getMilliseconds()|返回 Date 对象的毫秒(0 ~ 999)。|
|getTime()|返回距1970年1月1日凌晨的毫秒数|
|setDate()|设置 Date 对象中月的某一天 (1 ~ 31)。|
|setMonth()|设置 Date 对象中月份 (0 ~ 11)。|
|setFullYear()|设置 Date 对象中的年份(四位数字)。|
|setYear()|请使用 setFullYear() 方法代替。|
|setHours()|设置 Date 对象中的小时 (0 ~ 23)。|
|setMinutes()|设置 Date 对象中的分钟 (0 ~ 59)。|
|setSeconds()|设置 Date 对象中的秒钟 (0 ~ 59)。|
|setMilliseconds()|设置 Date 对象中的毫秒 (0 ~ 999)。|
|setTime()|以毫秒设置 Date 对象。|
这两组方法用的较多,其中值得注意的是,getMonths方法返回的是0-11,我们要获取当前月份的时候记得加一。
另外一点getTime方法返回从1970年1月1日到现在的毫秒数,这个时间我们称之为时间戳。
个人感觉日期对象并没有什么值得特别说明的地方…
JS定时器
定时器是这篇文章的重点部分。
在javascript中,与定时器有关的方法是:
setInterval、clearInterval
以及
setTimeout、clearTimeout
这些方法都是定义在window对象上面的,因此我们写window.setInterval和解setInterval的意思是一样的,前面的window可以省略掉。
setInterval这个定时器的功能是每过一段时间,就把我们想要执行的函数放到js的执行队列中等待执行。因为执行队列不一定是空的,需要等执行队列中的所有任务都执行完之后才会执行我们的函数,因此这个函数执行的时间也会有细微的差别。
这个方法的语法是:
window.setInterval(function () {}, 1000);
第一个参数是我们要执行的函数,第二个参数是每过多长时间把函数放入执行队列。
这里要说明的是,第一个参数的那个函数,不能带有参数。其次,里面的this默认指向window,因为前面提到过,谁调用方法,方法里面的this就指向谁,setInterval其实前面省略了window,因此里面的this默认一定指向window,不论这个setInterval是否是一个对象的方法。
setInterval其实很消耗内存,这个定时器一旦执行,就不会终止,因此需要我们的内核一直监听这个函数。
这个时候我们就需要一个方法来清除定时器了:clearInterval。
定时器其实会返回一个标记,我们可以通过定时器的这个标记来清除掉相对应的定时器。
1. var i = 0;
2. var timer = setInterval(function () {
3. i++;
4. if(i === 10) {
5. clearInterval(timer);
6. }
7. }, 100);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
实际上,如果我们打印这个timer来看的话,它就是一个数字1,我们再开一个定时器,返回的就是2,也就是说,这个标记就是定时器的序号数,我们直接clearInterval(1);其实也可以清除掉第一个定时器,不过这里还是用标记来清除较好。
因此这里有一点需要特别强调:凡是写定时器,一定要清除定时器!
还有一点需要注意的是,定时器是先等待再执行。
有时候我们只需要这个函数延迟一段时间执行,并不需要专门开启一个定时器,这个时候就需要用到setTimeout了。
setTimeout是延迟执行的意思,语法和用法和setInterval一样,只是这个方法只是把函数延迟一段时间之后执行一次而已。
同时它也有clearTimeout,当我们不想让这个函数执行了,提前把它clear掉就可以了。
其实setInterval和setTimeout的第一个参数不一定非要是一个函数,它也可以是一串字符型的js代码。
1. setInterval(“console.log(1);”, 100);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这段代码同样可以执行,定时器每100ms给我们打印一个1。
通过这个特性,我们不一定要把函数写在定时器里面,大部分时间我们都是在外部把函数定义好了,然后直接把函数名传进去就可以了。
1. function test () {}
2. setInterval(test, 1000);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
那么这一次的知识点就这么多哟~
Web前端的知识之旅哟——DOM的基本操作练习代码
Posted on 2018-01-02
1.用DOM动态生成这样一个结构:
<div class=”example”>
<p class=”slogan”>HelloWorld</p>
</div>
我认为中规中矩的写法是这个样子的:
1. var div = document.createElement(‘div’),
2. p = document.createElement(‘p’),
3. text = document.create(‘HelloWorld’);
4. div.setAttribute(‘class’, ‘example’);
5. p.setAttribute(‘class’, ‘slogan’);
6. p.appendChild(text);
7. div.appendChild(p);
8. document.body.append(div);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
• 不过这里有点小提示,节点的className属性可以读写操作class。
因此这里我们为标签赋类名的值的时候可以不用setAttribute,可以直接用className。
1. div.className = ‘example’;
2. p.className = ‘slogan’;
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
不过,还有一个最取巧的办法,那就是直接用innerHTML。
1. div.innerHTML = <p class=“slogan”>HelloWorld</p>
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
2.封装函数InsertAfter();功能类似insertBefore,直接在原型链上编程,可以忽略老版本的浏览器。
代码:
1. Element.insertAfter = function (targetNode, afterNode) {
2. var nextSib = after.nextElementSibling;
3. if(this.children.length <= 1 || !nextSib) {
4. this.appendChild(targetNode);
5. }else {
6. this.insertBefore(targetNode, nextSibling);
7. }
8. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这里是利用了原生的insertBefore方法,我们检测当只有一个子元素或者我们要找的元素就是最后一个,那么直接调用appendChild方法就可以,否则就找到要找的元素的下一个,然后把它的下一个元素当做insert的元素调用insertBefore方法就可以了。
3.封装remove()函数,使得child.remove()可以销毁自身。
看题目的要求就是要在原型链上编程。
代码:
1. Element.prototype.remove = function () {
2. var parent = this.parentNode;
3. parent.removeChild(this);
4. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)
这个问题的代码其实很简单,只要找到这个元素的父级,然后让父级删除掉这个元素就可以了。
4.将目标节点内部的节点顺序逆序。
eg: <div><p></p><a></a></div> —>
<div><a></a><p></p></div>
对于这个题目,我的做法是利用我们插入的元素如果是本身就存在的,那么就会剪切到新的位置上这个特性来做的。
代码:
1. Element.prototype.reverseElement = function(){
2. var len = this.childNodes.length,
3. child,
4. first = this.firstChild;
5. for (var i = 0; i < len - 1; i++){
6. child = this.lastChild;
7. this.insertBefore(child, first);
8. }
9. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)