XyyFighting


  • 首页

  • 标签

  • 分类

  • 归档

JS能力测评经典题1-10

发表于 2018-02-14 | 分类于 编程题 , JS能力测评

1. 查找数组元素的位置

找出元素 item 在给定数组 arr 中的位置

输出描述:

1
如果数组中存在 item,则返回元素在数组中的位置,否则返回 -1

示例1

输入

1
[ 1, 2, 3, 4 ], 3

输出

1
2

break是跳出一层循环,continue是结束一趟循环 ,return结束所有层循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

function indexOf(arr, item) {
//1.判断浏览器是否支持indexOf方法
if (Array.prototype.indexOf){
return arr.indexOf(item);
} else {
for (var i = 0; i < arr.length; i++){
//2.用===进行判断
if (arr[i] === item){
return i;
}
}
}
return -1;
}

2. 数组求和

计算给定数组 arr 中所有元素的总和

输入描述:

1
数组中的元素均为 Number 类型

示例1

输入

1
[ 1, 2, 3, 4 ]

输出

1
10

普通常规

1
2
3
4
5
6
7
function sum(arr) {
var sum=0;
for(var i=0;i<arr.length;i++){
sum+=arr[i];
}
return sum;
}

迭代方法

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
33
34
35
36
37
38
39
40
41
42
43
44
function sum(arr){
var sum=0;
arr.every(function(item,index,array){
sum+=item;
//every方法数组中每一项执行函数结果都为true才会返回true。
return true;
})
return sum;
}

function sum(arr){
var sum=0;
//some方法如果数组中有元素满足条件返回 true,否则返回 false。
arr.some(function(item,index,array){
sum+=item;
})
return sum;
}


function sum(arr){
var sum=0;
//filter()返回数组,包含了符合条件的所有元素。如果没有符合条件的元素则返回空数组。
arr.filter(function(item,index,array){
sum+=item;
})
return sum;
}
function sum(arr){
var sum=0;
//map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。
arr.map(function(item,index,array){
sum+=item;
})
return sum;
}
function sum(arr){
var sum=0;
//forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数。没有返回值
arr.forEach(function(item,index,array){
sum+=item;
})
return sum;
}

归并方法

1
2
3
4
5
function sum(arr){
return arr.reduce(function(prev,cur,index,array){
return prev+cur;
})
}

递归方法

1
2
3
4
5
6
7
8
9
10
function sum(arr) {
    var len = arr.length;
    if(len == 0){
        return 0;
    } else if (len == 1){
        return arr[0];
    } else {
        return arr[0] + sum(arr.slice(1));
    }
}

eval方法

1
2
3
function sum(arr) {
return eval(arr.join("+"));
};

3.移除数组中的元素

移除数组 arr 中的所有值与 item 相等的元素,直接在给定的 arr 数组上进行操作,并将结果返回

示例1

输入

1
[1, 2, 2, 3, 4, 2, 2], 2

输出

1
[1, 3, 4]

迭代方法

1
2
3
4
5
function remove(arr, item) {
return arr.filter(function(it,index,arr){
return (it!==item)
});
}

常规方法

1
2
3
4
5
6
7
8
9
function remove(arr,item){
var a=[];
for(var i=0;i<arr.length;i++){
if(arr[i]!==item){
a.push(arr[i]);
}
}
return a;
}

splice方法

1
2
3
4
5
6
7
8
9
10
11
function remove(arr, item) {
var a = arr.slice(0);
for (var i = 0; i < arr.length; i++) {
//注意:应该是新数组a中的元素和item进行比较
if (a[i] == item) {
a.splice(i, 1);
i--;
}
}
return a;
}

4. 移除数组中的元素(2)

移除数组 arr 中的所有值与 item 相等的元素,直接在给定的 arr 数组上进行操作,并将结果返回

示例1

输入

1
[1, 2, 2, 3, 4, 2, 2], 2

输出

1
[1, 3, 4]

我写的:

1
2
3
4
5
6
7
8
9
10
11
12
13
function removeWithoutCopy(arr, item) {
for(var i=0;i<arr.length;i++){
if(arr[i]==item){
for(var j=i;j<arr.length;j++){
arr[j]=arr[j+1];
}
arr.pop();
//注意需要i--,否则会漏判断数组元素
i--;
}
}
return arr;
}

讨论区答案

1
2
3
4
5
6
7
8
9
function removeWithoutCopy(arr, item) {
for(var i=0;i<arr.length;i++){
if(arr[i]==item){
arr.splice(i,1);
i--;
}
}
return arr;
}

5.添加元素(1)

在数组 arr 末尾添加元素 item。不要直接修改数组 arr,结果返回新的数组

示例1

输入

1
[1, 2, 3, 4],  10

输出

1
[1, 2, 3, 4, 10]

我写的:

1
2
3
function append(arr, item) {
return arr.concat(item);
}

普通迭代拷贝:

1
2
3
4
5
6
7
8
function append(arr, item) {
var a=[];
for(var i=0;i<arr.length;i++){
a.push(arr[i]);
}
a.push(item);
return a;
}

slice拷贝数组

1
2
3
4
5
6
function append(arr, item) {
var a=[];
a=arr.slice(0);
a.push(item);
return a;
}

6.删除数组最后一个元素

删除数组 arr 最后一个元素。不要直接修改数组 arr,结果返回新的数组

示例1

输入

1
[1, 2, 3, 4]

输出

1
[1, 2, 3]

我写的

1
2
3
4
5
function truncate(arr) {
var a=arr.slice(0);
a.pop();
return a;
}

利用slice

1
2
3
functiontruncate(arr) {
return arr.slice(0,-1);
}

普通拷贝迭代

1
2
3
4
5
6
7
function truncate(arr, item) {
    var newArr=[];
    for(var i=0;i<arr.length-1;i++){
        newArr.push(arr[i]);
    }
    return newArr;
}

其他方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//利用filter
function truncate(arr) {
    return arr.filter(function(v,i,ar) {
        return i!==ar.length-1;
    });
}
//利用push.apply+pop
function truncate(arr) {
    var newArr=[];
    [].push.apply(newArr, arr);
    newArr.pop();
    return newArr;
}
//利用concat+pop
function truncate(arr) {
    var newArr = arr.concat();
    newArr.pop();
    return newArr;
}

7.添加元素(2)

在数组 arr 开头添加元素 item。不要直接修改数组 arr,结果返回新的数组

示例1

输入

1
[1, 2, 3, 4], 10

输出

1
[10, 1, 2, 3, 4]

我写的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//unshift
function prepend(arr, item) {
var a=arr.concat();
a.unshift(item);
return a;
}
//常规方法
function prepend(arr, item) {
var a=[];
a.push(item);
for(var i=0;i<arr.length;i++){
a.push(arr[i]);
}
return a;
}

新思路:

1
2
3
function prepend(arr, item) {
return [item].concat(arr);
}

8.删除数组第一个元素

删除数组 arr 第一个元素。不要直接修改数组 arr,结果返回新的数组

示例1

输入

1
[1, 2, 3, 4]

输出

1
[2, 3, 4]

我写的

1
2
3
4
5
6
7
8
9
10
11
12
//slice方法
function curtail(arr) {
return arr.slice(1);
}
//普通循环
function curtail(arr) {
var a=[];
for(var i=1;i<arr.length;i++){
a.push(arr[i]);
}
return a;
}

9.数组合并

合并数组 arr1 和数组 arr2。不要直接修改数组 arr,结果返回新的数组

示例1

输入

1
[1, 2, 3, 4], ['a', 'b', 'c', 1]

输出

1
[1, 2, 3, 4, 'a', 'b', 'c', 1]

我写的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function concat(arr1, arr2) {
return arr1.concat(arr2);
}
//普通的拷贝迭代
function concat(arr1, arr2) {
var a=[];
for(var i=0;i<arr1.length;i++){
a.push(arr1[i]);
}
for(var i=0;i<arr2.length;i++){
a.push(arr2[i]);
}
return a;
}

讨论区

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//利用slice+push.apply
function concat(arr1, arr2) {
    var newArr=arr1.slice(0);
    [].push.apply(newArr, arr2);
    return newArr;
}
//利用slice+push
function concat(arr1, arr2) {
    var newArr=arr1.slice(0);
    for(var i=0;i<arr2.length;i++){
        newArr.push(arr2[i]);
    }
    return newArr;
}

10.添加元素(3)

在数组 arr 的 index 处添加元素 item。不要直接修改数组 arr,结果返回新的数组

示例1

输入

1
[1, 2, 3, 4], 'z', 2

输出

1
[1, 2, 'z', 3, 4]
1
2
3
4
5
function insert(arr, item, index) {
var a=arr.concat();
a.splice(index,0,item);
return a;
}

JavaScript高级程序设计读书笔记22

发表于 2018-02-03 | 分类于 读书笔记 , JavaScript高级程序设计

第22章 高级技巧

高级函数

安全的类型检测

  • typeof在Safari中对正则表达式也返回function。
  • instanceof在存在多个全局作用域时也会把同种却不同作用域中构造函数的实例识别为不同的实例
1
2
3
4
5
6
7
8
9
function isArray(value){
return Object.prototype.toString.call(value) == "[object Array]";
}
function isFunction(value){
return Object.prototype.toString.call(value) == "[object Function]";
}
function isRegExp(value){
return Object.prototype.toString.call(value) == "[object RegExp]";
}

作用域安全的构造函数

  • 当使用new操作符,构造函数内用到的this对象会指向新创建的对象实例
  • 作用域安全的构造函数在进行任何更改前,首先确认this对象是正确类型的实例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Person(name, age, job){
if (this instanceof Person){
this.name = name;
this.age = age;
this.job = job;
} else {
return new Person(name, age, job);
}
}
var person1 = Person("Nicholas", 29, "Software Engineer");
alert(window.name); //""
alert(person1.name); //"Nicholas"
var person2 = new Person("Shelby", 34, "Ergonomist");
alert(person2.name); //"Shelby"
  • 不过在使用了这样作用域安全的构造函数后,如果使用基于构造函数窃取的继承且不使用原型链,就会出现问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function Polygon(sides){
if (this instanceof Polygon) {
this.sides = sides;
this.getArea = function(){
return 0;
};
} else {
return new Polygon(sides);
}
}
function Rectangle(width, height){
//this对象并非Polygn的实例,所以会创建并返回一个新的Polygn对象
Polygon.call(this, 2);
this.width = width;
this.height = height;
this.getArea = function(){
return this.width * this.height;
};
}
var rect = new Rectangle(5, 10);
alert(rect.sides); //undefined
  • 使用原型链或者寄生组合可以解决
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function Polygon(sides){
if (this instanceof Polygon) {
this.sides = sides;
this.getArea = function(){
return 0;
};
} else {
return new Polygon(sides);
}
}
function Rectangle(width, height){
Polygon.call(this, 2);
this.width = width;
this.height = height;
this.getArea = function(){
return this.width * this.height;
};
}
Rectangle.prototype = new Polygon();
var rect = new Rectangle(5, 10);
alert(rect.sides); //2

惰性载入函数

  • 大量的判断浏览器能力的函数需要被使用(通常是大量的if),然而这些判断一般其实不必每次都执行,在执行一次后,浏览器的能力就确定了,以后就应该不用在判断了。
  • 第一种方式就是在函数被调用时再处理函数。
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
function createXHR(){
if (typeof XMLHttpRequest != "undefined"){
createXHR = function(){
return new XMLHttpRequest();
};
} else if (typeof ActiveXObject != "undefined"){
createXHR = function(){
if (typeof arguments.callee.activeXString != "string"){
var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0",
"MSXML2.XMLHttp"],
i, len;
for (i=0,len=versions.length; i < len; i++){
try {
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
} catch (ex){
//skip
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
};
} else {
createXHR = function(){
throw new Error("No XHR object available.");
};
}
return createXHR();
}
  • 第二种实现惰性载入的方式是在声明函数时就指定适当的函数。
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
var createXHR = (function(){
if (typeof XMLHttpRequest != "undefined"){
return function(){
return new XMLHttpRequest();
};
} else if (typeof ActiveXObject != "undefined"){
return function(){
if (typeof arguments.callee.activeXString != "string"){
var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0",
"MSXML2.XMLHttp"],
i, len;
for (i=0,len=versions.length; i < len; i++){
try {
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
break;
} catch (ex){
//skip
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
};
} else {
return function(){
throw new Error("No XHR object available.");
};
}
})();
var xhr1 = createXHR();

函数绑定

  • bind()函数可以将函数绑定到指定环境
1
2
3
4
5
function bind(fn, context) {
return function() {
return fn.apply(context, arguments);
};
}

函数柯里化

  • 函数柯里化:用来创建已经设置好一个或多个参数的函数
1
2
3
4
5
6
7
8
function curry(fn){
var args = Array.prototype.slice.call(arguments, 1);
return function(){
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return fn.apply(null, finalArgs);
};
}

防篡改对象

不可拓展对象

  • 不能给对象添加属性和方法。但是可以删除和修改已有成员。
  • preventExtensions()方法:对象不可扩展
  • Object.isExtensible():确定对象是否可以扩展

密封的对象

  • 密封对象不可扩展,已有成员的[[Configurable]]特性将被设置为false。不能删除属性和方法。
  • Object.frozen():密封对象
  • Object.isFrozen():确定对象是否被密封了

冻结的对象

  • frozen对象不可拓展又是密封的,而且对象属性的[[Writable]]特性会被设置为false。如果定义[[Set]]函数,访问器属性仍是可写的。
  • Object.frozen(person):冻结对象
  • Object.isFrozen(person):确定对象是否被冻结

高级定时器

  • 关于定时器要记住的最重要的事情是,指定的时间间隔表示何时将定时器的代码添加到队列,而不是何时执行代码。

重复的定时器

  • 使用setInterval创建定时器的目的是使代码规则的插入到队列中。这个方式的问题在于,存在这样一种可能,在上次代码还没执行完的时候代码再次被添加到队列。
  • 解决方式:使用链式setTimeout()
1
2
3
setTimeout(function(){ 
setTimeout(arguments.callee, interval);
}, interval);

Yielding Processes

  • 如果你的页面中要进行大量的循环处理,每次循环会消耗大量的时间,那就会阻塞用户的操作。
  • 解决方法:数组分块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function chunk(array, process, context){
setTimeout(function(){
var item = array.shift();
process.call(context, item);
if (array.length > 0){
setTimeout(arguments.callee, 100);
}
}, 100);
}
var data = [12,123,1234,453,436,23,23,5,4123,45,346,5634,2234,345,342];
function printValue(item){
var div = document.getElementById("myDiv");
div.innerHTML += item + "<br>";
}
chunk(data, printValue);

函数节流

  • 这个是为了避免某个操作连续不停的触发,比如涉及到DOM操作,连续大量的DOM操作非常耗资源。
  • 解决方式:函数节流
  • 函数节流的基本思想是,每一次调用其实是设置一个真正调用的setTimeout操作,每次调用都会先清除当前的setTimeout再设置一个新的。如果短时间内大量调用,就回一直设置新的setTimeout而不执行setTimeout内的操作。只有停止调用足够长的时间,直到setTimeout时间到了,内部的真正操作才会执行一次。这个对onresize事件特别有用。
1
2
3
4
5
6
7
8
9
10
11
12
13
function throttle(method, context) {
clearTimeout(method.tId);
method.tId= setTimeout(function(){
method.call(context);
}, 100);
}
function reDiv(){
var div = document.getElementById("myDiv");
div.innerHTML += "qqqqqq" + "<br>";
}
window.onresize = function(){
throttle(reDiv);
};

自定义事件

  • 事件这样的交互其实就是观察者模式,这类模式由两类对象组成:主体和观察者。主体负责发布事件,观察者通过订阅这些事件来观察该主体。
  • 观察者知道主体并能注册事件的回调函数(事件处理程序)。涉及DOM上时,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
33
34
function EventTarget(){
this.handlers = {};
}
EventTarget.prototype = {
constructor: EventTarget,
addHandler: function(type, handler){
if (typeof this.handlers[type] == "undefined"){
this.handlers[type] = [];
}
this.handlers[type].push(handler);
},
fire: function(event){
if (!event.target){
event.target = this;
}
if (this.handlers[event.type] instanceof Array){
var handlers = this.handlers[event.type];
for (var i=0, len=handlers.length; i < len; i++){
handlers[i](event);
}
}
},
removeHandler: function(type, handler){
if (this.handlers[type] instanceof Array){
var handlers = this.handlers[type];
for (var i=0, len=handlers.length; i < len; i++){
if (handlers[i] === handler){
break;
}
}
handlers.splice(i, 1);
}
}
};
  • 使用EventTarget类型的自定义事件
1
2
3
4
5
6
7
8
function handleMessage(event){
alert("Message received: " + event.message);
}
var target = new EventTarget();
target.addHandler("message", handleMessage);
target.fire({ type: "message", message: "Hello world!"});
target.removeHandler("message", handleMessage);
target.fire({ type: "message", message: "Hello world!"});

拖放

  • 创建一个绝对定位的元素,使其可以用鼠标移动
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
33
34
35
36
37
38
39
var DragDrop = function(){
var dragging = null;
function handleEvent(event){
//获取事件和目标
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
//确定事件类型
switch(event.type){
case "mousedown":
if (target.className.indexOf("draggable") > -1){
dragging = target;
}
break;
case "mousemove":
if (dragging !== null){
//指定位置
dragging.style.left = event.clientX + "px";
dragging.style.top = event.clientY + "px";
}
break;
case "mouseup":
dragging = null;
break;
}
};
//公共接口
return {
enable: function(){
EventUtil.addHandler(document, "mousedown", handleEvent);
EventUtil.addHandler(document, "mousemove", handleEvent);
EventUtil.addHandler(document, "mouseup", handleEvent);
},
disable: function(){
EventUtil.removeHandler(document, "mousedown", handleEvent);
EventUtil.removeHandler(document, "mousemove", handleEvent);
EventUtil.removeHandler(document, "mouseup", handleEvent);
}
}
}();

修缮拖动方法

  • 原先的代码会让被拖动的元素的左上角在鼠标下方。
  • 现在对鼠标相对元素的位置记录后再次计算元素的绝对位置。
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
33
34
35
36
37
38
39
40
41
42
43
var DragDrop = function(){
var dragging = null,
diffX = 0,
diffY = 0;
function handleEvent(event){
//获取事件和目标
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
//确定事件类型
switch(event.type){
case "mousedown":
if (target.className.indexOf("draggable") > -1){
dragging = target;
diffX = event.clientX - target.offsetLeft;
diffY = event.clientY - target.offsetTop;
}
break;
case "mousemove":
if (dragging !== null){
//指定位置
dragging.style.left = (event.clientX - diffX) + "px";
dragging.style.top = (event.clientY - diffY) + "px";
}
break;
case "mouseup":
dragging = null;
break;
}
};
//公共接口
return {
enable: function(){
EventUtil.addHandler(document, "mousedown", handleEvent);
EventUtil.addHandler(document, "mousemove", handleEvent);
EventUtil.addHandler(document, "mouseup", handleEvent);
},
disable: function(){
EventUtil.removeHandler(document, "mousedown", handleEvent);
EventUtil.removeHandler(document, "mousemove", handleEvent);
EventUtil.removeHandler(document, "mouseup", handleEvent);
}
}
}();

添加自定义事件

  • 新定义一个dragdrop变量,它是EventTarget类型的对象,在它上面我们可以添加事件处理函数或触发事件。在拖动开始时,过程中,结束时,都触发了自定义事件,这样有人想在这几个节点做什么就直接添加事件处理函数就可以了。
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
var DragDrop = function(){
var dragdrop = new EventTarget(),
dragging = null,
diffX = 0,
diffY = 0;
function handleEvent(event){
//获取事件和目标
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
//确定事件类型
switch(event.type){
case "mousedown":
if (target.className.indexOf("draggable") > -1){
dragging = target;
diffX = event.clientX - target.offsetLeft;
diffY = event.clientY - target.offsetTop;
dragdrop.fire({type:"dragstart", target: dragging, x: event.clientX, y: event.clientY});
}
break;
case "mousemove":
if (dragging !== null){
//指定位置
dragging.style.left = (event.clientX - diffX) + "px";
dragging.style.top = (event.clientY - diffY) + "px";
//触发自定义事件
dragdrop.fire({type:"drag", target: dragging, x: event.clientX, y: event.clientY});
}
break;
case "mouseup":
dragdrop.fire({type:"dragend", target: dragging, x: event.clientX, y: event.clientY});
dragging = null;
break;
}
};
//公共接口
dragdrop.enable = function(){
EventUtil.addHandler(document, "mousedown", handleEvent);
EventUtil.addHandler(document, "mousemove", handleEvent);
EventUtil.addHandler(document, "mouseup", handleEvent);
};
dragdrop.disable = function(){
EventUtil.removeHandler(document, "mousedown", handleEvent);
EventUtil.removeHandler(document, "mousemove", handleEvent);
EventUtil.removeHandler(document, "mouseup", handleEvent);
};
return dragdrop;
}();
DragDrop.enable();
DragDrop.addHandler("dragstart", function(event){
var status = document.getElementById("status");
status.innerHTML = "Started dragging " + event.target.id;
});
DragDrop.addHandler("drag", function(event){
var status = document.getElementById("status");
status.innerHTML += "<br>Dragged " + event.target.id + " to (" + event.x + "," + event.y + ")";
});
DragDrop.addHandler("dragend", function(event){
var status = document.getElementById("status");
status.innerHTML += "<br>Dropped " + event.target.id + " at (" + event.x + "," + event.y + ")";
});

JavaScript高级程序设计读书笔记21

发表于 2018-02-03 | 分类于 读书笔记 , JavaScript高级程序设计

第21章 Ajax与Comet

  • Ajax核心是XMLHttpRequest对象,能够以异步的方式从服务器获取更多信息,意味着用户单击后,可以不必刷新页面也能取得数据

XMLHttpReques对象

  • 浏览器兼容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function createXHR(){
if(typeof XMLHttpRequest != "undefined"){
return new XMLHttpRequest;
}else if(typeof ActiveXObject != "undefined"){
if(typeof arguments.callee.activeXString != "string"){
var versions = ["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"],i,len;
for(i=0,len=versions.length;i < len;i++){
try{
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
break;
}catch(ex){
//跳过
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
}else{
throw new Error("No XHR Object available");
}
}
//使用示例
var xhr = createXHR();

HXR的用法

  • open,接收3个参数:要发送的请求的类型(“get”,“post”)、请求的URL、表示是否异步发送请求的布尔值
  • URL相对于执行代码的当前页面
  • 调用open()方法并不会真正发送请求
  • send接受一个参数:作为请求主体发送的数据,不需要则要传入null。
  • 请求是同步的,JavaScript代码会等到服务器响应之后再继续执行。在收到响应后,响应的数据会自动填充xhr对象的属性:

    responseText: 作为响应主体被返回的文本。
    responseXML: 如果响应的内容类型是”text/xml”或”application/xml”,这个属性中将保存包含着响应数据> 的XML DOM文档。
    status: 响应的HTTP状态。
    statusText: HTTP状态的说明。

1
2
3
4
5
6
7
8
xhr.open("get","example.php",false);               //同步请求
xhr.send(null);

if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.resopnseText);
}else{
alert("Request was unsuccessful: "+xhr.status);
}
  • 如果要发送异步请求,可以检测XHR对象的readyState属性,该属性表示请求/响应过程的当前活动阶段:

    0: 未初始化。尚未调用open()方法
    1: 启动。已经调用open()方法但尚未调用send()方法
    2:发送。已经调用send()方法但尚未接收到响应。
    3:接受。已经接受到部分响应数据。
    4:完成。已经接受到全部响应数据,而且可以在客户端使用了。

  • readyState属性的值由一个值变成另一个值,都触发一次readystatechange事件。

  • 必须在调用open之前指定onreadystatechange事件处理程序才能确保跨浏览器兼容性
1
2
3
4
5
6
7
8
9
10
11
12
var xhr = createXHR();
xhr.onreadystatechange = function(){ //DOM0级方法,不是所有浏览器都支持DOM2级方法
if(xhr.readyState == 4){
if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.resopnseText);
}else{
alert("Request was unsuccessful: "+xhr.status);
}
}
};
xhr.open("get","example.php",true); //异步请求
xhr.send(null);

HTTP头部

  • 默认情况下,发送XHR请求的同时,还会发送下列头部信息

    Accept:浏览器能够处理的内容类型
    Accept-Charset:浏览器能够显示的字符集
    Accept-Encoding:浏览器能够处理的压缩编码
    Accept-Language:浏览器当前设置语言
    Connection:浏览器与服务器之间的链接类型
    Cookie:当前页面设置的热河Cookie
    Host:发出请求的页面所在的域
    Referer:发出请求的页面的URI。(正确拼法是referrer)
    User-Agent:浏览器的用户代理字符串

  • setRequestHeader方法设置自定义的请求头部信息。接收两个参数:头部字段名称,头部字段值,

  • 要成功发送请求头部信息,必须在调用open方法之后,调用send方法之前调用setRequestHeader
  • 调用XHR对象的getRequestHeader方法并传入头部字段名称,可以取得相应头部信息
  • 调用getAllResponseHeaders()方法则可以获得一个包含所有头部信息的长字符串。

GET请求

  • GET请求最常用语想服务器查询某些信息
  • 经常发生的一个错误,就是查询字符串的格式有问题,查询字符串的每个参数的名称和值都必须使用encodeURIComponent()编码,然后才能放到URL末尾
  • 下面这个函数可以辅助向现有URL的末尾添加查询字符串参数
1
2
3
4
5
function addURLParam(url, name, value){
url += (url.indexOf("?")) == -1? "?" : "&";
url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
return url;
}

POST请求 ###​

  • 通常用于向服务器发送应该保存的数据,应该把数据作为请求的主体提交,可以包含非常多的数据,且格式不限。
  • 如果需要将页面中的表单数据进行序列化,然后通过XHR放到服务器,则可以使用14章的serialize函数来创建字符串
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function submitData(){
var xhr = createXHR();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.resopnseText);
}else{
alert("Request was unsuccessful: "+xhr.status);
}
}
};

xhr.open("post","postExample.php",true);
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
var form = document.getElementById("user-info");
xhr.send(serialize(form));
}

XMLHttpRequest 2级

FormData

  • FormData类型:为序列化表单以及创建与表单格式相同的数据(用于通过XHR传输)提供了便利。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function submitData(){
var xhr = createXHR();
xhr.onreadystatechange = function(event){
if (xhr.readyState == 4){
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.responseText);
} else {
alert("Request was unsuccessful: " + xhr.status);
}
}
};
xhr.open("post", "postexample.php", true);
var form = document.getElementById("user-info");
xhr.send(new FormData(form));
}

超时设定

  • timeout属性,表示请求在等待响应多少毫秒之后就终止。如果在规定的时间内浏览器还没有接收到响应,那么就会触发timeout事件,进而会调用ontimeout事件处理程序。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var xhr = createXHR();        
xhr.onreadystatechange = function(event){
try {
if (xhr.readyState == 4){
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.responseText);
} else {
alert("Request was unsuccessful: " + xhr.status);
}
}
} catch (ex){
//assume handled by ontimeout
}
};
xhr.open("get", "timeout.php", true);
xhr.timeout = 1000;
xhr.ontimeout = function(){
alert("Request did not return in a second.");
};
xhr.send(null);

overrideMimeType()方法

  • overrideMimeType()方法:重写XHR响应的MIME类型
  • 必须在send()方法之前调用overrideMimeType(),才能保证重写相应的MIME类型
1
2
3
4
var xhr = createXHR();
xhr.open("get", "text.php", true);
xhr.overrideMimeType("text/xml");
xhr.send(null);

进度事件

  • loadstrart:在接收到响应数据的第一个字节时触发。
  • progress:在接收响应期间持续不断地触发。
  • error:在请求发生错误时触发
  • abort:在因为调用abort()方法而终止连接时触发。
  • load:在接收到完整的响应数据时触发。
  • loadend:在通信完成或者触发error、abort或load事件后触发。

load事件

  • 用于替代readystatechange事件。onload事件处理程序会接收到一个event对象,其target属性就指向XHR对象实例,因而可以访问到XHR对象的所有方法和属性。
1
2
3
4
5
6
7
8
9
10
11
12
13
window.onload = function(){
var xhr = createXHR();
xhr.onload = function(event){
if ((xhr.status >= 200 && xhr.status < 300) ||
xhr.status == 304){
alert(xhr.responseText);
} else {
alert("Request was unsuccessful: " + xhr.status);
}
};
xhr.open("get", "altevents.php", true);
xhr.send(null);
};

progress事件

  • 这个事件会在浏览器接收新数据期间周期性地触发。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
window.onload = function(){
var xhr = createXHR();
xhr.onload = function(event){
if ((xhr.status >= 200 && xhr.status < 300) ||
xhr.status == 304){
console.log(xhr.responseText);
} else {
console.log("Request was unsuccessful: " + xhr.status);
}
};
xhr.onprogress = function(event){
var divStatus = document.getElementById("status");
if (event.lengthComputable){
divStatus.innerHTML = "Received " + event.position + " of " + event.totalSize + " bytes";
}
};
xhr.open("get", "altevents.php", true);
xhr.send(null);
};

跨资源共享 CORS

  • 在发送请求时,需要给它附加一个额外的Origin头部,其中包含请求页面的源信息,以便服务器根据这个头部信息来决定是否给予响应。
  • 如果服务器认为这个请求可以接受,就在Access-Control_Allow-Origin头部中发相同的源信息。

IE对CORS的支持

  • IE8中引入了XDR(XDomainRequest)类型,与XHR类似,但能实现安全可靠的跨域通信。与XHR不同:

    1.cookie不会随请求发送,也不会随请求返回
    2.只能设置头部信息中的Content-Type
    3.不能访问响应头部信息
    4.只支持GET和POST请求

  • 使用方法,也是创建一个XDomainRequest实例,调用open,调用send,open方法只接受两个参数,请求类型,URL。所有请求都是异步。

其他浏览器对CORS的支持

  • Firefox3.5+,Safari4+,Chrome,ios版Safari,Android平台中的WebKit都通过XHR对象实现了对CORS的原生支持。
  • 限制:

    1.不能使用setRequestHeader()设置自定义头部
    2.不能发送和接受cookie
    3.调用getAllResponseHeaders()方法总会返回空字符串

Preflighted Reqeusts

  • CORS通过一种叫做PreFlighted Requests的透明服务器验证机制支持使用自定义头部、get和post之外的方法以及不同类型的主体内容。

带凭据的请求

  • 默认情况下,跨源请求不提供凭据(cookie、HTTP认证及客户端SSL证明等)。通过将withCredentials属性设置为true,可以指定某个请求应该发送凭据。
  • 服务器如果接受带凭据的请求,会响应Access-Control-Allow-Credentials: true

跨浏览器的CORS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function createCORSRequest(method, url){
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr){
xhr.open(method, url, true);
} else if (typeof XDomainRequest != "undefined"){
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
xhr = null;
}
return xhr;
}
var request = createCORSRequest("get", "http://www.somewhere-else.com/xdr.php");
if (request){
request.onload = function(){
//do something with request.responseText
};
request.send();
}

其他跨域技术

图像ping

  • 图像ping最常用于跟踪用户点击页面或动态广告曝光次数。
  • 图像ping有两个主要的缺点:

    只能发送get请求
    无法访问服务器的响应文本。

  • 因此,图像ping只能用于浏览器与服务器的单向通信。

1
2
3
4
5
var img = new Image();
img.onload = img.onerror = function(){
alert("Done!");
};
img.src = "http://www.example.com/test?name=Nicholas";

JSONP

  • JSONP由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数。回调函数的名称一般是在请求中指定的。而数据就是传入回调函数中的JSON数据。
  • 与图像Ping相比优点:

    能够直接访问相应文本
    支持在浏览器与服务器之间双向通信

  • 不足:

    是从其他域中家在代码执行,可能会夹带一些恶意代码
    要确定JSONP请求是否失败并不容易

Comet

  • Comet一种更高级的Ajax技术。Ajax是从页面向服务器请求数据的技术,Comet是服务器向页面推送数据的技术。能够让信息近乎实时的被推送到页面上。
  • 实现Comet的方式:长轮询和流

服务器发送数据

  • SSE(Server-Sent Events,服务器发送事件)是围绕只读Comet交互推出的API或者模式

Web Socket

  • Web Socket的目标是在一个单独的持久连接上提供全双工、双向通信。
  • 使用标准的HTTP服务器无法实现Web Socket,只有支持这种协议的专门服务器才能工作。

Web Sockets API

  • 表示当前状态的readyState属性:

    WebSocket.OPENING(0):正在建立连接
    WebSocket.OPEN(1):已建立连接
    WebSocket.CLOSING(1):正在关闭连接
    WebSocket.CLOSE(3):已关闭

发送和接收数据

  • send():只能发送纯文本数据,发杂结构的数据要经过序列化。

其他事件

  • open:成功建立连接触发
  • error:发生错误触发,连接不能持续
  • close:连接关闭触发

安全

  • 为确保通过XHR访问的URL安全,通行的做法就是验证发送请求者是否有权限访问相应的资源,有下列几种方式可供选择

    要求以SSL连接来访问可以通过XHR请求的资源
    要求每一次请求都要附带经过相应算法计算得到的验证码。

JavaScript高级程序设计读书笔记20

发表于 2018-02-02 | 分类于 读书笔记 , JavaScript高级程序设计

第20章 Json

  • JSON(Javascript Object Notaion,对象表示法)并不从属于Javascript,只是一种数据格式

语法

  • 简单值:使用与Javascript相同的语法,可以在JSON中表示字符串、数值、布尔值和null,但JSON不支持Javascript中的特殊值undefined
  • 对象:表示一组无序的键值对儿
  • 数组:表示一组有序的值的列表

简单值

  • JSON字符串必须使用双引号(单引号会导致语法错误)

对象

  • JSON中的对象要求给属性加引号
  • JSON对象与Javascript的对象字面量的区别:

    1.没有声明变量;
    2.没有末尾的分号(因为这不是Javascirpt语句,所以不需要分号)
    3.对象的属性必须加双引号,这在JSON中是必须的

1
2
3
4
5
6
7
8
{
"name":"Nicholas",
"age":19,
"school":{
"name":"school",
"location":"location"
}
}

数组

  • JSON数组采用的就是JavaScript的数组字面量形式。
  • 在JSON中,可以采用相同的语法表示同一个数组
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    [
    {
    "title": "Professional JS",
    "author": ["Nicholas C. Zakas"
    ],
    "edition": 2,
    "year": 2017
    },
    {
    "title": "Reading notes",
    "author": ["xyy"
    ],
    "edition": 1,
    "year": 2016
    }
    ]

解析与序列化

  • stringify()和parse()用于把JavaScript对象序列化为JSON字符串和把JSON字符串解析为原生JavaScript值。
  • 在序列化JS对象时,所有函数及原型成员都会被有意忽略,不体现在结果中
  • 值为undefined的任何属性也都会被跳过

序列化选项

  • JSON.stringify还可以接收另外两个参数:1、过滤器,可以是数组,也可以是函数。2、选项,表示是否在JSON字符串保留缩进

过滤结果

  • 若过滤器是数组,则JSON.stringify结果将只包含数组列出的元素

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var book = {
    title:"title",
    author:[
    "Nicholas C. Zakas"
    ],
    edition:3,
    year:2011
    };
    var jsonText = JSON.stringify(book,["title","edition"]);
    //{"title":"title","edition":3}
  • 若过滤器是函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    var jsonText = JSON.stringify(book,function(key,value){
    switch(key){
    case "author":
    return value.join(",");

    case "year":
    return 5000;

    case "edition":
    return undefined;
    default:
    return value;
    }
    });
    //{"title":"title","author":"Nicholas C. Zakas","year":5000}

字符串缩进

  • JSON.stringify第三个参数用于控制缩进和空白符,若是数值,表示每个级别的缩进空格数
  • 最大缩进空格数为10,大于10的数值全部转为10
  • 若缩进参数是字符串,则将被用于缩进字符

toJSON()方法

toJSON方法可以作为函数过滤器的补充,理解序列化顺序:

  1. 如果存在toJSON方法,而且能通过它取得有效值,调用该方法,否则返回对象本身
  2. 若提供了第二个参数,应用这个函数过滤器,传入函数过滤器的值是第一步返回值
  3. 对第二步返回值进行序列化
  4. 若提供了第三个参数,执行相应格式化

解析选项

  • JSON.parse也接收另一个参数,是一个函数,将在每个键值对上调用。若返回结果为undefined,表示删除该值,返回其他值,则将该值插入到结果。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var book = {
title:"title",
author:[
"Nicholas C. Zakas"
],
edition:3,
year:2011,
releaseDate:new Date(2011,11,1)
};

var jsonText = JSON.stringify(book);
var bookCopy = JSON.parse(jsonText,function(key,value){
if(key == "releaseDate"){
return new Date(value);
}else{
return value;
}
});

alert(bookCopy.releaseDate.getFullYear()); //2011

JavaScript高级程序设计读书笔记19

发表于 2018-02-02 | 分类于 读书笔记 , JavaScript高级程序设计

第19章 E4X

E4X的类型

  • XML:XML结构中的任何一个独立的部分
  • XMList:XML对象的集合
  • NameSpace:命名空间前缀与命名空间URI之间的映射
  • QName:由内部名称和命名空间URI组成的一个限定名

一般用法

  • 点号加特性访问其中不同的层次和结构employee.name
  • 如果有多个元素具有相同的标签名,就会返回XMLList employees.employee[0].name
  • 若不确定资源的内部名称,可以使用星号。 employees.*[0].name
  • child():将属性名或者索引值传递给该方法
  • children():返回所有子元素
  • elements():与child()类似,但是只返回表示元素的XML对象

访问特性

  • 用@符号表示应该返回特性
  • ..表示要匹配所有后代元素

其他节点类型

  • nodeKind():可以得到XML对象表示的类型

查询

  • 所有查询都可以通过一组执行相同操作的方法来实现

JavaScript高级程序设计读书笔记18

发表于 2018-02-02 | 分类于 读书笔记 , JavaScript高级程序设计

第18章 JavaScript与XML

浏览器对XML DOM的支持

DOM2级核心

  • document.implementation.createDocument():创建一个空白的XML文档

DOMParser类型

  • 将XML解析为DOM文档,首先必须创建一个DOMParser的实例,然后再调用parseFromString()方法,返回值是一个document实例
  • DOMParser只能解析XML,不能讲HTML解析为HTML文档
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var parser = new DOMParser();
    var xmldom = parser.parseFromString("<root><child/></root>", "text/xml");
    alert(xmldom.documentElement.tagName); //root
    alert(xmldom.documentElement.firstChild.tagName); //child

    var anotherChild=xmldom.createElement("child");
    xmldom.documentElement.appendChild(anotherChild);

    var children=xmldom.getElementsByTagName("child");
    alert(children.length); //2

XMLSerializer类型

  • 将DOM文档序列化为XML文档,使用XMLSerializer类型
  • XMLSerializer类型可以序列化XML和HTML

IE8及之前版本中的XML

  • 通过ActivrX对象实现对XML的支持
  • 创建XML文档:使用ActiveXObject构造函数并为其传入一个表示XML文档版本的字符串
  • 解析XML:loadXML()方法
  • 序列化:每个DOM节点都有一个xml属性,xmldom.xml
  • 加载XML文件:确定加载XML方式,调用load()启动下载过程

跨浏览器处理XML

  • 解析XML

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    function parseXml(xml){
    var xmldom = null;
    if (typeof DOMParser != "undefined"){
    xmldom = (new DOMParser()).parseFromString(xml, "text/xml");
    var errors = xmldom.getElementsByTagName("parsererror");
    if (errors.length){
    throw new Error("XML parsing error:" + errors[0].textContent);
    }
    } else if (typeof ActiveXObject != "undefined"){
    xmldom = createDocument();
    xmldom.loadXML(xml);
    if (xmldom.parseError != 0){
    throw new Error("XML parsing error: " + xmldom.parseError.reason);
    }
    } else {
    throw new Error("No XML parser available.");
    }
    return xmldom;
    }
  • 序列化XML

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function serializeXml(xmldom){
    if (typeof XMLSerializer != "undefined"){
    return (new XMLSerializer()).serializeToString(xmldom);
    } else if (typeof xmldom.xml != "undefined"){
    return xmldom.xml;
    } else {
    throw new Error("Could not serialize XML DOM.");
    }
    }

浏览器对XPath的支持

  • XPath是设计用来在DOM文档中查找节点的一种手段,因而对XML处理也很重要

DOM3级XPath

  • XPathEvaluator:用于在特定的上下文中对XPath表达式求值
  • 处理命名空间有两种方法:

    1.createNSResolver()创建XPathNSResolver对象
    2.定义一个函数,让它接收一个命名空间前缀

IE中的XPath

  • 在IE8及之前的浏览器,XPath是采用内置基于ActiveX的XML DOM文档对象实现的。在每一个节点上提供了两个方法:selectSingleNode()和selectNodes()。
  • 处理命名空间的方法:setProperty()

跨浏览器使用XPath

  • 要在其它使用DOM3级XPath对象的浏览器中,重新创建selectSingleNode()和selectNodes()方法。
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
function selectSingleNode(context, expression, namespaces) {
var doc = (context.nodeType != 9 ? context.ownerDocument : context);
if (typeof doc.evaluate != "undefined") {
var nsresolver = null;
if (namespaces instanceof Object) {
nsresolver = function (prefix) {
return namespaces[prefix];
};
}
var result = doc.evaluate(expression, context, nsresolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
return (result !== null ? result.singleNodeValue : null);
} else if (typeof context.selectSingleNode != "undefined") {
if (namespaces instanceof Object) {
var ns = "";
for (var prefix in namespaces) {
if (namespaces.hasOwnProperty(prefix)) {
ns += "xmlns:" + prefix + "='" + namespaces[prefix] + "' ";
}
}
doc.setProperty("SelectionNamespaces", ns);
}
return context.selectSingleNode(expression);
} else {
throw new Error("No XPath engine found.");
}

}

浏览器对XSLT的支持

  • XSLT:它利用XPath将文档从一种表现形式转换成另一种表现形式

IE中的XSLT

  • 简单的XSLT转换:使用XSLT样式表

    1
    2
    3
    xmldom.load("employees.xml");
    xstldom.load("employees.xstl");
    var result = xmldom.transformNodes(xstldom);
  • 复杂的XSLT转换:使用XSL模板和XSL处理器

  • 第一步: 先要把XSTL样式表加载到一个线程安全的XML文档中。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    function createThreadSafeDocument() {
    if (typeof arguments.callee.activeXString != "string") {
    var versions = ["MSXML2.FreeThreadedDOMDocument.6.0", "MSXML2.FreeThreadedDOMDocument.3.0", "MSXML2.FreeThreadedDOMDocument"],
    i, len;
    for (i = 0, len = versions.length; i < len; i++) {
    try {
    new ActiveXObject(versions[i]);
    arguments.callee.activeXString = versions[i];
    break;
    } catch (ex) {
    //skip
    }
    }
    }
    return new ActiveXObject(arguments.callee.activeXString);
    }
  • 第二步:将创建并加载了自由线程的DOM文档指定给一个XSL模板,这也是一个ActiveX对象,模板用来创建XSL处理器对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    function createXSTLTemplate() {
    if (typeof arguments.callee.activeXString != "string") {
    var versions = ["MSXML2.FreeThreadedDOMDocument.6.0", "MSXML2.FreeThreadedDOMDocument.3.0", "MSXML2.FreeThreadedDOMDocument"],
    i, len;
    for (i = 0, len = versions.length; i < len; i++) {
    try {
    new ActiveXObject(versions[i]);
    arguments.callee.activeXString = versions[i];
    break;
    } catch (ex) {
    //skip
    }
    }
    }
    return new ActiveXObject(arguments.callee.activeXString);
    }
  • 使用示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var xsltdom = createThreadSafeDocument();
    xstldom.async = false;
    xsltdom.load("employees.xslt");

    var template = createXSTLTemplate();
    template.stylesheet = xstldom;

    var processor = tempalte.createProcessor();
    processor.input = xmldom;
    processor.transform();
    var result = processor.output;

XSLTProcessor类型

  • XSLTProcessor:是通过JS进行XSLT转换的事实标准
    1
    2
    var processor = new XSLTProcessor();
    processor.importStylesheet(xsltdom);

跨浏览器使用XSLT

  • 接收两个参数: 要执行转换的上下文节点和XSLT文档对象。 返回序列化之后的字符串。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function transform(context, xslt) {
    if (typeof XSLTProcessor != "undefined") {
    var processor = new XSLTProcessor();
    processor.importStylesheet(xslt);
    var result = processor.transformToDocument(context);
    return (new XMLSerializer()).serializeToString(result);
    } else if (typeof context.transformNode != "undefined") {
    return caontext.transformNode(xslt);
    } else {
    throw new Error("No XSLT processor available.");
    }
    }

JavaScript高级程序设计读书笔记17

发表于 2018-02-02 | 分类于 读书笔记 , JavaScript高级程序设计

第17章 错误处理与调试

错误处理

try-catch语句

  • 把所有可能会抛出错误的代码都放在try语句快中,而把那些用于错误处理的代码放在catch块中
  • 错误信息有message属性和name属性
  • message属性是唯一一个能保证所有浏览器都支持的属性

finally

  • 只要代码中包含finally子句,则无论try或catch语句块中包含什么样的代码——甚至return语句,都不会阻止finally子句的执行。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function testFinally() {
    try {
    return 2;
    } catch (error) {
    return 1;
    } finally {
    return 0;
    }
    }
  • 调用这个函数只能返回0

错误类型

  • 每种错误都有对应的错误类型,而当错误发生时,就会抛出相应类型的错误对象。
  • Error:Error是基类型,其他错误类型都继承自该类型
  • EvalError:在使用eval()函数而发生异常时抛出
  • RangeError:数值超出相应范围时触发
  • ReferenceError:在找不到对象的情况下,会发生ReferenceError
  • SyntaxError:当我们把语法错误的JavaScript字符串传入eval()函数时,就会导致此类错误
  • TypeError:执行特定于类型的操作时,变量的类型并不符合要求所致。最常发生类型错误的情况,就是传递给函数的参数事先未经检查,结果传入类型与预期类型不相符。
  • URIError:URI格式不正确时,就会导致URIError错误

合理使用try-catch

  • 当try-catch语句中发生错误时,浏览器会认为错误已经被处理了。因而不会通过前面讨论的机制记录或报告错误。
  • 使用try-catch最适合处理那些我们无法控制的错误.
  • 在明明白白地知道自己的代码会发生错误时,不应该使用try-catch语句。

抛出错误

  • throw操作符,用于随时抛出自定义错误。
  • 在遇到throw操作符时,代码会立即停止执行。仅当有try-catch语句捕获到被抛出的值时,代码才会继续执行。
  • 通过使用某种内置错误类型,可以更真实地模拟浏览器错误。每种错误类型的构造函数接受一个参数,即实际的错误信息。
  • 利用原型链还可以通过继承Error来创建自定义错误类型。此时,需要为新创建的错误类型指定name和message属性。

    1
    2
    3
    4
    5
    6
    function CustomError(message){
    this.name = "CustomError";
    this.message = message;
    }
    CustomError.prototype = new Error();
    throw new CustomError("My message");
  • 想知道函数为什么执行失败,抛出自定义错误是一种很方便的方式。

  • 应该捕获那些你确切知道该如何处理的错误。
  • 捕获错误的目的在于避免浏览器以默认方式处理他们;而抛出错误的目的在于提供错误发生具体原因的消息。

错误(error)事件

  • 任何没有通过try-catch处理的错误都会触发window对象的error事件。
  • 在事件处理程序中返回false,可以阻止浏览器报告错误的默认行为,实际充当了整个文档中的try-catch语句,可以捕获所有无代码处理的运行时错误。

处理错误的策略

  • 作为开发者,必须知道代码何时可能出错,出什么错,还要有一套错误跟踪问题的系统。

常见的错误类型

  • 常见的错误类型:类型转换错误、数据类型错误、通信错误

类型转换错误

  • 由于相等和不相等操作符会在比较之前进行一个转换,所以js会误以为这个比较是正确的而继续执行代码,然后导致错误,所以建议使用全等或不全等
  • 在流程控制语句中使用非布尔值为条件很容易导致类型转换错误。

数据类型错误

  • 在流程控制语句中使用非布尔值为条件很容易导致数据类型错误
  • 另一种错误做法,就是只针对要使用的某一个特性执行特性检测
  • 关于数据类型错误,大体上来说,基本类型的值应该使用typeof来洁厕,而对象的值应该使用instanceof来检测。

通信错误

  • 最常见的错误:将数据发送给服务器之前,没有使用encodeURIComponent()对数据进行编码
  • 在服务器响应的数据不正确时,也可能发生通信错误

区分致命错误和非致命错误

  • 非致命错误:不影响用户的主要任务、只影响页面的一部分、可以恢复、重复相同错误可以消除错误
  • 致命错误:程序无法继续运行、明显影响到用户的主要操作、会导致其他连带错误

把错误记录到服务器

  • 首先要在服务器创建一个页面,用于处理错误数据。
  • 只要是使用try-catch语句,就应该把响应错误记录到日志中

调试技术

将消息记录到控制台

  • 通过console对象向JavaScript控制台写入信息。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    function log(message){
    if (typeof console == "object"){
    console.log(message);
    } else if (typeof opera == "object"){
    opera.postError(message);
    } else if (typeof java == "object" && typeof java.lang == "object"){
    java.lang.System.out.println(message);
    }
    }

将消息记录到当前页面

  • 在页面中开辟一小块区域,用以显示消息
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    function log(message){
    var console = document.getElementById("debuginfo");
    if(console ===null){
    console = document.createElement("div");
    console.id = "debuginfo";
    console.style.background = "#dedede";
    console.style.border = "1px solid silver";
    console.style.padding = "5px";
    console.style.width = "400px";
    console.style.position = "absolute";
    console.style.right = "0px";
    console.style.top = "0px";
    document.body.appendChild(console);
    }
    console.innerHTML += "<p>" + message + "</p>";
    }

抛出错误

  • 如果错误消息很具体,基本上就可以把它当作确定错误来源的依据
  • 这种错误消息必须能够明确给出导致错误的原因,才能省去其他调试操作
  • 对于大型的应用程序来说,自定义的错误通常使用assert()函数抛出

常见的IE错误

  • 操作终止
  • 无效字符
  • 未找到成员
  • 未知运行时错误
  • 语法错误

JavaScript高级程序设计读书笔记16

发表于 2018-01-31 | 分类于 读书笔记 , JavaScript高级程序设计

第16章 HTML5脚本编程

跨文档信息传送

  • 跨文档消息传送(cross-document messaging),有时候也简称为XDM,指的是来自不同域的页面间传递消息。
  • XDM的核心是postMessage()方法:向另一个地方传递数据
  • postMessage()方法接收两个参数:一条消息和一个表示消息接收方来自哪个域的字符串。
  • 接收到XDM消息时,会触发window对象的message事件。这个事件是以异步形式触发的,因此从发送消息到接受消息(触发接受窗口的message事件)可能要经过一段时间的延迟。
  • 接受到消息后验证发送窗口的来源是至关重要的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    EventUtil.addHandler(window, "message", function (event) {
    //确保发送消息的域是已知的域
    if (event.origin == "http://www.w3cmm.com") {
    //处理接收到的数据
    processMessage(event.data);
    //可选:向来源窗口发送回执
    event.source.postMessage("Received!", "http://p2p.w3cmm.com");
    }
    });
  • event.source大多数情况下只是window对象的代理,并非实际的window对象。换句话说,不能通过这个代理对象访问window对象的其它任何信息。只通过这个代理调用postMessage()就好,这个方法永远不存在,永远可以调用。

原生拖放

拖放事件

  • 最关键的地方在于确定那里发生了拖放事件,有些事件是在被拖动的元素上触发的,而有些事件是在放置目标上触发的。
  • 拖动某些元素时,将一次触发下列事件:dragstart、drag、dragend
  • 当元素被拖放到一个有效的放置目标上时,下列事件会依次发生:dragenter、dragover、dragleave或drop
  • 上述三个事件的目标都是作为放置目标的元素。

自定义放置目标

  • 可以把任何元素变成有效的放置目标,方法是重写dragenter和dragover事件的默认行为。
    1
    2
    3
    4
    5
    6
    7
    var droptarget = document.getElementById("droptarget");
    EventUtil.addHandler(droptarget, "dragover", function (event) {
    EventUtil.preventDefault(event);
    });
    EventUtil.addHandler(droptarget, "dragenter", function (event) {
    EventUtil.preventDefault(event);
    });

dataTransfer对象

  • dataTransfer对象,它是事件对象的一个属性,用于从被拖动元素向放置目标传递字符串格式的数据。
  • getData()和setData()方法

    1
    2
    3
    4
    5
    6
    //设置和接收文本数据
    event.dataTransfer.setData("text", "some text");
    var text = event.dataTransfer.getData("text");

    //设置和接收URL
    event.dataTransfer.setData("URL", "https://www.w3cmm.com/");
  • 在拖放链接或图像时,会调用setData()方法并保存URL。然后,在这些元素被拖放到放置目标时,就可以通过getData()读到这些数据。

  • 保存在dataTransfer对象中的数据只能在drop事件处理程序中读取。

dropEffect与effectAllowed

  • 利用dataTransfer对象,可不光是能够传输数据,还能通过它来确定被拖动的元素以及作为放置目标的元素能够接受什么操作。
  • dropEffect:可以知道被拖动的元素能够执行哪种放置行为。
  • effectAllowed:只有搭配effectAllowed属性才有用。effectAllowed属性表示允许拖放元素的哪种dropEffect。

可拖动

  • 默认情况下,图像、链接和文本是可以拖动的
  • draggable属性,表示元素是否可以拖动。

媒体元素

  • <audio>和<video>标签
  • 自定义媒体播放器
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <div class="mediaplayer"> 
    <div class="video">
    <video id="player" src="movie.mov" poster="mymovie.jpg"
    width="300" height="200">
    Video player not available.
    </video>
    </div>
    <div class="controls">
    <input type="button" value="Play" id="video-btn">
    <span id="curtime">0</span>/<span id="duration">0</span>
    </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
25
//取得元素的引用
var player = document.getElementById("player"),
btn = document.getElementById("video-btn"),
curtime = document.getElementById("curtime"),
duration = document.getElementById("duration");

//更新播放时间
duration.innerHTML = player.duration;


//为按钮添加事件处理程序
EventUtil.addHandler(btn, "click", function(event){
if (player.paused){
player.play();
btn.value = "Pause";
} else {
player.pause();
btn.value = "Play";
}
});

//定时更新当前时间
setInterval(function(){
curtime.innerHTML = player.currentTime;
}, 250);
  • 检测编解码器的支持情况:canPlayType()方法,该方法接收一种格式/编解码器字符串,返回”probably”、”maybe”或””( 空字符串)。
  • 传入了一种MIME类型,则返回值很可能是”maybe”或空字符串。
  • 在同时传入MIME类型和编解码器的情况下,可能性就会增加,返回的字符串会变成”probably”。

历史状态管理

  • 通过状态管理API,能够在不加载新页面的情况下改变浏览器的URL。
  • history.pushState()方法,该方法可以接收三个参数:状态对象、新状态的标题和可选的相对URL。
  • 要更新当前状态,可以调用replaceState()
  • ​

JavaScript高级程序设计读书笔记15

发表于 2018-01-31 | 分类于 读书笔记 , JavaScript高级程序设计

第15章 使用Canvas绘图

基本用法

  • 要使用canvas元素,必须先设置其width和height属性,指定绘图区域的大小。
  • getContext():取得绘图上下文
  • toDataURL():导出在canvas元素元素上绘制的图像

2D上下文

  • 2D上下文的坐标开始于canvas元素的左上角,圆点坐标为(0,0),所有坐标都是基于这个圆点计算,x值越大越靠右,y值越大越靠下。

填充和描边

  • 2D上下文的两种基本绘图操作是填充和描边
  • fillStyle(填充)和strokeStyle(描边)属性:属性的值可以是字符串、渐变对象或模式对象

绘制矩形

方法名 参数 说明
fillRect() 矩形的x坐标、矩形的y坐标、矩形的宽度和矩形的高度 在画布上绘制的矩形会填充指定的颜色
strokeRect() 矩形的x坐标、矩形的y坐标、矩形的宽度和矩形的高度 在画布上绘制的矩形会使用指定的颜色描边
clearRect() 矩形的x坐标、矩形的y坐标、矩形的宽度和矩形的高度 清除画布上的矩形区域


1
2
3
4
5
6
7
8
9
10
11
12
var drawing=document.getElementById("drawing");
//确定浏览器支持<canvas>元素
if(drawing.getContext) {
//取得绘图上下文对象的引用,“2d”是取得2D上下文对象
var context = drawing.getContext("2d");
//绘制红色矩形
context.fillStyle = "#ff0000";
context.fillRect(10,10,50,50);
//绘制半透明的蓝色矩形
context.fillStyle="rgba(0,0,255,0.5)";
context.fillRect(30,30,50,50);
}


1
2
3
4
5
6
7
8
9
10
11
12
var drawing=document.getElementById("drawing");
//确定浏览器支持<canvas>元素
if(drawing.getContext) {
//取得绘图上下文对象的引用,“2d”是取得2D上下文对象
var context = drawing.getContext("2d");
//绘制红色矩形
context.strokeStyle = "#ff0000";
context.strokeRect(10,10,50,50);
//绘制半透明的蓝色矩形
context.strokeStyle="rgba(0,0,255,0.5)";
context.strokeRect(30,30,50,50);
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
var drawing=document.getElementById("drawing");
//确定浏览器支持<canvas>元素
if(drawing.getContext) {
//取得绘图上下文对象的引用,“2d”是取得2D上下文对象
var context = drawing.getContext("2d");
//绘制红色矩形
context.fillStyle = "#ff0000";
context.fillRect(10,10,50,50);
//绘制半透明的蓝色矩形
context.fillStyle="rgba(0,0,255,0.5)";
context.fillRect(30,30,50,50);
//在两个矩形冲抵的地方清除一个小矩形
context.clearRect(40,40,10,10);
}

绘制路径

  • 要绘制路径,首先必须调用beginPath()方法,表示要开始绘制新路径。
  • strock()方法对路径描边。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    var drawing=document.getElementById("drawing");
    //确定浏览器支持<canvas>元素
    if(drawing.getContext){
    //取得绘图上下文对象的引用,“2d”是取得2D上下文对象
    var context=drawing.getContext("2d");
    //开始路径
    context.beginPath();
    //绘制外圆
    context.arc(100,100,99,0,2*Math.PI,false);
    //绘制内圆
    context.moveTo(194,100);
    context.arc(100,100,94,0,2*Math.PI,false);
    //绘制分针
    context.moveTo(100,100);
    context.lineTo(100,15);
    //绘制时针
    context.moveTo(100,100);
    context.lineTo(35,100);
    context.strokeStyle="rgba(0,0,255,0.5)";
    //最后必须调用stroke()方法,这样才能把图像绘制到画布上。
    context.stroke();
    }

绘制文本

  • fillText(),strokeText():接受四个参数:要绘制的文本,x坐标,y坐标和可选的最大像素
  • fillText()由于可以模仿在网页中正常显示文本,所有应用的更多。
    1
    2
    3
    4
    5
    //添加文字“12”
    context.font="bold 14px Arial";
    context.textAlign="center"; //文本的对齐方式
    context.textBaseline="middle";//文本的基线
    context.fillText("12",100,20);

变换

  • save()、restore()可以跟踪上下文的状态变化
  • save保存设置到一个栈结构,restore恢复前一级保存的状态
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    var context=drawing.getContext("2d");
    context.fillStyle="#ff0000";
    context.save();

    context.fillStyle="#00ff00";
    context.translate(100,100);
    context.save();

    context.fillStyle="#0000ff";//蓝色
    context.fillRect(0,0,100,200); //点(110,110)

    context.restore();//绿色
    context.fillRect(10,10,100,200); //点(110,110)

    context.restore();//红色
    context.fillRect(0,0,100,200); //点(0,0)

绘制图像

  • drawImage():把一幅图像绘制到画布上。
  • 三种不同的调用方式:

    1.传入一个元素,绘制图像起点的x,y坐标。
    2.还可以再传两个参数:目标宽度,目标高度。以此来缩放图像。
    3.传入9个参数:要绘制的图像,源图像的x坐标,源图像的y坐标,源图像的宽度,源图像的高度,目标图像的x坐标,目标图像的y坐标,目标图像的宽度,目标图像的高度。

阴影

  • shadowColor:用Css颜色格式表示的阴影颜色,默认为黑色。
  • shadowOffsetX:形状或路径x轴的阴影偏移量,默认为0.
  • shadowOffsetY:形状或路径ya轴的阴影偏移量,默认为0.
  • shadowBlur:模糊的像素数,默认为0,及不模糊。

渐变

  • 渐变由CanvasGradient实例表示。通过2D上下文来创建和修改:
  • createLinearGradient():创建一个新的线性渐变。4个参数:起点的x,y坐标,终点的x,y坐标。返回CanvasGradient实例。
  • 创建完后,使用addColorStop():指定色标。两个参数:色标位置(0-1)和CSS颜色值。
1
2
3
4
5
6
var context = drawing.getContext("2d"),
gradient = context.createLinearGradient(30, 30, 70, 70);
gradient.addColorStop(0, "white");
gradient.addColorStop(1, "black");
context.fillStyle = gradient;
context.fillRect(30, 30, 50, 50);

模式

  • 其实就是重复的图像,可以用来填充或描边图形。
  • createPattern():2个参数,一个HTML<img>元素和一个表示如何重复图像的字符串

使用图像数据

  • getImageData():取得原始图像数据。4个参数,要取得其数据的画面区域的x,y坐标以及该区域的像素宽度和高度。 返回一个ImageData的实例。
  • 每个ImageData对象有三个属性。width,height和data。
  • data属性是一个数组,保存着图像中每个像素的数据,每个像素用4个元素保存,红,绿,蓝,透明度

合成

  • globalAlpah:介于0-1的值,用于指定所有绘制的透明度。
  • globalCompositionOperation:表示后绘制的图形怎样与先绘制的结合

WebGL

  • 针对Canvas的3D上下文

类型化数组

  • 类型化数组也是数组,只不过其元素被设置为特性类型的值
  • ArrayBuffer(数组缓冲器类型):表示内存中指定的字节数,但是不会指定这些字节用于保存什么类型的数据

WebGL上下文

  • 用GLSL编写的顶点和片段着色器
  • 支持类型化数组,即能够将数组中的数据限定为某种特定的数据类型
  • 创建和操作纹理

JavaScript高级程序设计读书笔记14

发表于 2018-01-30 | 分类于 读书笔记 , JavaScript高级程序设计

第14章 表单脚本

表单的基础知识

  • 在HTML中,表单是由<form>表示的,在JS中,表单对应的是HTMLFormElemnt类型
  • 取得form表单元素的方法:1.添加id属性 2.通过数值索引 3.通过name值取得
  • 表单的id和name属性,它们的值不一定相同

提交表单

  • 提交表单的方式:
  • 通用提交按钮 <input type="submit" value="Submit Form">
  • 自定义提交按钮<button type="submit">Submit Form</button>
  • 图像按钮<input type="image" src="graphic.gif">
  • 以编程方式提交表单form.submit()
  • 提交表单最大的问题是重复提交表单,解决方法:1.第一次提交表单后就禁用提交按钮2.利用onsubmit事件处理程序取消后续的表单提交操作

重置表单

  • 重置表单的方式:
  • 通用提交按钮 <input type="reset" value="Reset Form">
  • 自定义提交按钮<button type="reset">Reset Form</button>

表单字段

  • 每个表单都有elements属性,该属性是表单中所有表单元素的集合
  • 解决重复提交表单问题,在第一次单击后就禁用提交按钮

    1
    2
    3
    4
    5
    6
    7
    8
    EventUtil.addHandler(form,"submit",function(event){
    event=EventUtil.getEvent(event);
    var target=EventUtil.getTarget(event);
    //取得提交按钮
    var btn=target.elements["submit-btn"];
    //禁用它
    btn.disabled=true;
    });
  • focus和blur事件以某种方式改变用户界面,要么是向用户给出视觉提示,要么是向界面中添加额外的功能。change事件经常用于验证用户在字段中的输入数据。

文本框脚本

  • 在HTML中,有两种方式来表现文本框:1.使用<input>元素的单行文本框 2.使用<textarea>的多行文本框。
  • 这两种文本框都会将用户输入的内容保存在value属性中。可以通过这个属性读取和设置文本框的值。

选择文本

  • select():上述两种文本框都支持,用于选择文本框中的所有文本。
  • 在文本框获得焦点时选择其所有文本,特别是在文本框包含默认值的时候。这样做可以让用户不必一个一个地删除文本。
    1
    2
    3
    4
    5
    EventUtil.addHandler(textbox,"focus",function(event){
    event = EventUtil.getEvent(event);
    var target = EventUtil.getTarget(event);
    target.select();
    });

选择事件

  • 在选择了文本框中的文本时,就会触发select事件。
  • 在调用select()方法时也会触发select事件。

取得选择的文本

  • 通过select事件我们可以知道用户什么时候选择了文本,但是不知道用户选择了什么文本。HTML5 通过一些扩展方案解决了这个问题,以便更顺利的取得选择的文本。
  • 该规范采取的办法:添加两个属性,selectionStart和selectionEnd。这两个属性中保存的是基于0的数值,表示所选择文本的范围(即文本选区开头和结尾的偏移量)。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    function getSelectedText(textbox){
    //IE9+、Firefox、Safari、Chrome和Opera
    if(typeof textbox.selectionStart == "number"){
    return textbox.value.substring(textbox.selectionStart,textbox.selectionEnd);
    //IE8 及更早版本
    } else if (document.selection){
    return document.selection.createRange().text;
    }
    }

选择部分文本

  • setSelectionRange()方法,参数:要选择的第一个字符的索引和要选择的最后一个字符之后的字符的索引
  • IE8 及更早的版本支持使用范围选择部分文本。

    1.首先使用createTextRange()方法创建一个范围,并将其放在恰当的位置上。

    1. 使用collapse()将范围折叠到文本框的开始位置。
      3.再使用moveStart()和moveEnd()这两个范围方法将范围移动到位。此时,moveStart()将范围的起点和终点移动到了相同的位置,只要给moveEnd()传入要选择的字符总数即可。
      4.最后,使用范围的select()方法选择文本。
  • 跨浏览器实现选择部分文本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function selectText(textbox, startIndex, stopIndex) {
    if (textbox.setSelectionRange) {
    textbox.setSelectionRange(startIndex, stopIndex);
    } else if (textbox.createTextRange) {
    var range = textbox.createTextRange();
    range.collapse(true);
    range.moveStart("character", startIndex);
    range.moveEnd("character", stopIndex - startIndex);
    range.select();
    }
    textbox.focus();
    }

过滤输入

屏蔽字符

1
2
3
4
5
6
7
8
9
10
EventUtil.addHandler(textbox, "keypress", function (event) {
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
var charCode = EventUtil.getCharCode(event);
//屏蔽非数值字符,不屏蔽也会触发keypress事件的基本按键,确保用户没有按下ctrl键
if (!/\d/.test(String.fromCharCode(charCode)) && charCode > 9 &&
!event.ctrlKey) {
EventUtil.preventDefault(event);
}
});

操作剪贴板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var EventUtil = {
//省略的代码
getClipboardText: function (event) {
var clipboardData = (event.clipboardData || window.clipboardData);
return clipboardData.getData("text");
},
setClipboardText: function (event, value) {
if (event.clipboardData) {
return event.clipboardData.setData("text/plain", value);
} else if (window.clipboardData) {
return window.clipboardData.setData("text", value);
}
}
};
var textbox = document.forms[0].elements["textbox1"]
EventUtil.addHandler(textbox, "paste", function (event) {
event = EventUtil.getEvent(event);
var text = EventUtil.getClipboardText(event);

if (!/^\d*$/.test(text)) {
EventUtil.preventDefault(event);
}
});

自动切换焦点

1
2
3
<input type="text" name="tel1" id="txtTel1" size="3" maxlength="3" />
<input type="text" name="tel2" id="txtTel2" size="3" maxlength="3" />
<input type="text" name="tel3" id="txtTel3" size="4" maxlength="4" />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
(function () {
function tabForward(event) {
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
if (target.value.length == target.maxLength) { // 确定是否已经达到最大长度
var form = target.form;
for (var i = 0, len = form.elements.length; i < len; i++) {
if (form.elements[i] == target) {
form.elements[i + 1].focus(); //将焦点切换到下一个文本框
return;
}
}
}
}

var textbox1 = document.getElementById("txtTel1");
var textbox2 = document.getElementById("txtTel2");
var textbox3 = document.getElementById("txtTel3");
//这个函数指定为每个文本框的onkeyup事件处理程序
EventUtil.addHandler(textbox1, "keyup", tabForward);
EventUtil.addHandler(textbox2, "keyup", tabForward);
EventUtil.addHandler(textbox3, "keyup", tabForward);
})();

HTML5约束验证API

  • 必填字段:required
  • 其他输入类型:email、url
  • 数值范围:number、range、datetime、datetime-local、date、month、week、time、stepUp()方法、stepDown()方法
  • 输入模式:pattern
  • 检测有效性:checkValidity()、validity
  • 禁用验证:novalidate、formnovalidate

选择框脚本

  • 在DOM中,每个<option>元素都有一个HTMLOptionElement对象表示。

选择选项

  • 对于只能选择一项的选项,访问选中项的方式是使用选择框的selectedIndex属性。对于多选项,selectedIndex只返回第一项的索引值。
  • 多选的情况下可以设置多个选项的selected属性为true
  • 以下适用于单选和多选选择框
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function getSelectedOptions(selectbox) {
    var result = new Array();
    var option = null;
    for (var i = 0,len=selectbox.options.length; i <len; i++) {
    option =selectbox.options[i];
    if (option.selected) {
    result.push(option);
    }
    }
    return result;
    }

添加项

  • 有三种方法:第一种方法DOM方法(appendChild方法),第二种方法Option构造函数(接收两个参数:text,value),第三种方法使用add函数(接收两个参数:新选项,位于新选项最后的选项;如果要插入成为最后的选项,第二个参数应该设置为undefined)

移除选项

  • 第一种方法DOM方法(利用removeChild方法),第二种方法用选择框的remove方法,第三种为设置null。

移动和重排选项

  • 如果为appendChild()方法传入一个文档中已有的元素,那么久从该元素的父节点中移除它,再把它添加到指定的位置,移动选项会充值每一个选项的index属性
  • 重排选项次序最好的方法是DOM:insertBefore()

表单序列化

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
function serialize(form) {
var parts = [],
field = null,
i,
len,
j,
optLen,
option,
optValue;

for (i = 0, len = form.elements.length; i < len; i++) {
field = form.elements[i];

switch (field.type) {
case "select-one":
case "select-multiple":

if (field.name.length) {
for (j = 0, optLen = field.options.length; j < optLen; j++) {
option = field.options[j];
if (option.selected) {
optValue = "";
if (option.hasAttribute) {
optValue = (option.hasAttribute("value") ? option.value : option.text);
} else {
optValue = (option.attributes["value"].specified ? option.value : option.text);
}
parts.push(encodeURIComponent(field.name) + "=" + encodeURIComponent(optValue));
}
}
}
break;

case undefined:
//字段集
case "file":
//文件输入
case "submit":
//提交按钮
case "reset":
//重置按钮
case "button":
//自定义按钮
break;

case "radio":
//单选按钮
case "checkbox":
//复选框
if (!field.checked) {
break;
}

default:
//不包含没有名字的表单字段
if (field.name.length) {
parts.push(encodeURIComponent(field.name) + "=" + encodeURIComponent(field.value));
}
}
}
return parts.join("&");
}

富文本编辑

  • 有两种编辑富文本的方式,一种是使用iframe元素,另一种是使用contenteditable属性

    1.在页面中嵌入一个包含空HTML页面的iframe。通过设置designMode属性位“on”,这个空白的HTML页面可以被编辑,
    2.把contenteditable属性应用给页面中的任何元素,然后用户立即就可以编辑该元素

  • document.execCommand():可以对文档执行预定义的命令

  • queryCommandEnabled():查询浏览器中指定的编辑指令是否可用
  • queryCommandState():确定是否已将指令命令应用到了选择的文本
  • queryCommandValue():取得执行命令时传入的值
  • 使用框架(iframe)的getSelection()方法可以确定实际选择的文本,返回一个Selection对象
  • 富文本编辑器中的HTML不会被自动提交给服务器,而需要手工提取并提交HTML。在提交表单之前,从iframe中提取出HTML,并将其插入到隐藏的字段中。
1234…6
Xyy

Xyy

I want to get ahead in a step-by-step

59 日志
16 分类
8 标签
© 2018 Xyy
由 Hexo 强力驱动
|
主题 — NexT.Gemini v5.1.4