文章目录
JavaScript
字符串
JavaScript的字符串就是用 ''
或""
括起来的字符表示。
如果'
本身也是一个字符,那就可以用""
括起来,比如 "I'm OK"
包含的字符是 I , ’ , m ,空格, O , K 这6个字符。
如果字符串内部既包含'
又包含"
怎么办?可以用转义字符\
来标识,比如:
'I\'m \"OK\"!';
常用方法
JavaScript为字符串提供了一些常用方法,注意,调用这些方法本身不会改变原有字符串的内容,而是返 回一个新字符串:
-
toUpperCase() 把一个字符串全部变为大写
var s = 'Hello'; s.toUpperCase(); // 返回'HELLO'
-
toLowerCase() 把一个字符串全部变为小写
var s = 'Hello'; var lower = s.toLowerCase(); // 返回'hello'并赋值给变量lower lower; // 'hello'
-
indexOf() 会搜索指定字符串出现的位置
var s = 'hello, world'; s.indexOf('world'); // 返回7 s.indexOf('World'); // 没有找到指定的子串,返回-1
-
substring() 返回指定索引区间的子串
var s = 'hello, world' s.substring(0, 5); // 从索引0开始到5(不包括5),返回'hello' s.substring(7); // 从索引7开始到结束,返回'world'
数组
JavaScript的 Array
可以包含任意数据类型,并通过索引来访问每个元素。
请注意,直接给 Array
的length
赋一个新的值会导致Array
大小的变化
var arr = [1, 2, 3];
arr.length; // 3
arr.length = 6;
arr; // arr变为[1, 2, 3, undefined, undefined, undefined]
arr.length = 2;
arr; // arr变为[1, 2]
如果通过索引赋值时,索引超过了范围,同样会引起 Array 大小的变化:
var arr = [1, 2, 3];
arr[5] = 'x';
arr; // arr变为[1, 2, 3, undefined, undefined, 'x']
常用方法
-
indexOf:与String类似, Array 也可以通过 indexOf() 来搜索一个指定的元素的位置:
var arr = [10, 20, '30', 'xyz']; arr.indexOf(10); // 元素10的索引为0 arr.indexOf(20); // 元素20的索引为1 arr.indexOf(30); // 元素30没有找到,返回-1 arr.indexOf('30'); // 元素'30'的索引为2
注意了,数字 30 和字符串 ‘30’ 是不同的元素。
-
slice:slice() 就是对应String的 substring() 版本,它截取 Array 的部分元素,然后返回一个 新的 Array.
var arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G']; arr.slice(0, 3); // 从索引0开始,到索引3结束,但不包括索引3: ['A', 'B', 'C'] arr.slice(3); // 从索引3开始到结束: ['D', 'E', 'F', 'G']
如果不给 slice() 传递任何参数,它就会从头到尾截取所有元素。利用这一点,我们可以很容 易地复制一个 Array :
var arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G']; var aCopy = arr.slice(); aCopy; // ['A', 'B', 'C', 'D', 'E', 'F', 'G'] aCopy === arr; // false
-
push和pop:push() 向 Array 的末尾添加若干元素, pop() 则把 Array 的最后一个元素删除掉
var arr = [1, 2]; arr.push('A', 'B'); // 返回Array新的长度: 4 arr; // [1, 2, 'A', 'B'] arr.pop(); // pop()返回'B' arr; // [1, 2, 'A'] arr.pop(); arr.pop(); arr.pop(); // 连续pop 3次 arr; // [] arr.pop(); // 空数组继续pop不会报错,而是返回undefined arr; // []
-
unshift和shift:如果要往 Array 的头部添加若干元素,使用 unshift() 方法, shift() 方法则把 Array 的第一个元素删掉:\
var arr = [1, 2]; arr.unshift('A', 'B'); // 返回Array新的长度: 4 arr; // ['A', 'B', 1, 2] arr.shift(); // 'A' arr; // ['B', 1, 2] arr.shift(); arr.shift(); arr.shift(); // 连续shift 3次 arr; // [] arr.shift(); // 空数组继续shift不会报错,而是返回undefined arr; // []
-
sort:sort() 可以对当前 Array 进行排序,它会直接修改当前 Array 的元素位置,直接调用 时,按照默认顺序排序
var arr = ['B', 'C', 'A']; arr.sort(); arr; // ['A', 'B', 'C']
-
reverse:reverse() 把整个 Array 的元素给掉个个,也就是反转
var arr = ['one', 'two', 'three']; arr.reverse(); arr; // ['three', 'two', 'one']
-
splice:splice() 方法是修改 Array 的“万能方法”,它可以从指定的索引开始删除若干元素,然后再 从该位置添加若干元素
var arr = ['Microsoft', 'Apple', 'Yahoo', 'AOL', 'Excite', 'Oracle']; // 从索引2开始删除3个元素,然后再添加两个元素: arr.splice(2, 3, 'Google', 'Facebook'); // 返回删除的元素 ['Yahoo', 'AOL', 'Excite'] arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle'] // 只删除,不添加: arr.splice(2, 2); // ['Google', 'Facebook'] arr; // ['Microsoft', 'Apple', 'Oracle'] // 只添加,不删除: arr.splice(2, 0, 'Google', 'Facebook'); // 返回[],因为没有删除任何元素 arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']
-
concat:concat() 方法把当前的 Array 和另一个 Array 连接起来,并返回一个新的 Array
var arr = ['A', 'B', 'C']; var added = arr.concat([1, 2, 3]); added; // ['A', 'B', 'C', 1, 2, 3] arr; // ['A', 'B', 'C']
concat() 方法并没有修改当前 Array ,而是返回了一个新的 Array 。 实际上, concat() 方法可以接收任意个元素和 Array ,并且自动把 Array 拆开,然后全 部添加到新的 Array 里:
var arr = ['A', 'B', 'C']; arr.concat(1, 2, [3, 4]); // ['A', 'B', 'C', 1, 2, 3, 4]
-
join:join() 方法是一个非常实用的方法,它把当前 Array 的每个元素都用指定的字符串连接起 来,然后返回连接后的字符串:
var arr = ['A', 'B', 'C', 1, 2, 3]; arr.join('-'); // 'A-B-C-1-2-3'
-
多维数组:如果数组的某个元素又是一个 Array ,则可以形成多维数组,例如:
var arr = [[1, 2, 3], [400, 500, 600], '-'];
上述 Array 包含3个元素,其中头两个元素本身也是 Array 。
练习:如何通过索引取到 500 这个值:
var x = arr[1][2]; console.log(x); // x应该为500
对象
JavaScript的对象是一种无序的集合数据类型,它由若干键值对组成。
由于JavaScript的对象是动态类型,你可以自由地给一个对象添加或删除属性:
var xiaoming = {
name: '小明'
};
xiaoming.age; // undefined
xiaoming.age = 18; // 新增一个age属性
xiaoming.age; // 18
delete xiaoming.age; // 删除age属性
xiaoming.age; // undefined
delete xiaoming['name']; // 删除name属性
xiaoming.name; // undefined
delete xiaoming.school; // 删除一个不存在的school属性也不会报错
如果我们要检测对象是否拥有某一属性,可以用 in 操作符:
'name' in xiaoming; // true
'grade' in xiaoming; // false
要判断一个属性是否是 xiaoming 自身拥有的,而不是继承得到的,可以用 hasOwnProperty() 方法:
xiaoming.hasOwnProperty('name'); // true
xiaoming.hasOwnProperty('toString'); // false
流程控制
if判断
var age = 3;
if (age >= 18) {
alert('adult');
} else if (age >= 6) {
alert('teenager');
} else {
alert('kid');
}
for循环
基础语法
var x = 0;
var i;
for (i=1; i<=10000; i++) {
x = x + i;
}
x; // 50005000
遍历数组
var arr = ['Apple', 'Google', 'Microsoft'];
var i, x;
for (i=0; i<arr.length; i++) {
x = arr[i];
console.log(x);
}
无限循环
var x = 0;
for (;;) { // 将无限循环下去
if (x > 100) {
break; // 通过if判断来退出循环
}
x ++;
}
for … in , 它可以把一个对象的所有属性依次循环出来:
var o = {
name: 'Jack',
age: 20,
city: 'Beijing'
};
for (var key in o) {
if (o.hasOwnProperty(key)) {
console.log(key); // 'name', 'age', 'city'
}
}
由于 Array 也是对象,而它的每个元素的索引被视为对象的属性,所以遍历出来是下标
var a = ['A', 'B', 'C'];
for (var i in a) {
console.log(i); // '0', '1', '2'
console.log(a[i]); // 'A', 'B', 'C'
}
请注意,for ... in
对 Array
的循环得到的是String
而不是Number
。
while循环
基本操作
var x = 0;
var n = 99;
while (n > 0) {
x = x + n;
n = n - 2;
}
x; // 2500
do…while
var n = 0;
do {
n = n + 1;
} while (n < 100);
n; // 100
在编写循环代码时,务必小心编写初始条件和判断条件,尤其是边界值。 特别注意 i < 100 和 i <= 100 是不同的判断逻辑。
Map 和 Set
ES6规范引入了新的数据类型 Map 和Set。
Map
Map 是一组键值对的结构,具有极快的查找速度。
var m = new Map(); // 空Map
m.set('Adam', 67); // 添加新的key-value
m.set('Bob', 59);
m.has('Adam'); // 是否存在key 'Adam': true
m.get('Adam'); // 67
m.delete('Adam'); // 删除key 'Adam'
m.get('Adam'); // undefined
Set
Set 和 Map 类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在 Set 中, 没有重复的key。
var s = new Set([1, 2, 3]);
s; // Set {1, 2, 3}
s.delete(3);
s; // Set {1, 2}
s.add(4);
Iterable
遍历 Array 可以采用下标循环,遍历Map 和 Set 就无法使用下标。 为了统一集合类型,ES6标准引入了新的 iterable 类型,Array,Map,Set 属于; 具有 iterable 类型的集合可以通过新的 for … of 循环来遍历。
var a = ['A', 'B', 'C'];
var s = new Set(['A', 'B', 'C']);
var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
for (var x of a) { // 遍历Array
console.log(x);
}
for (var x of s) { // 遍历Set
console.log(x);
}
for (var x of m) { // 遍历Map
console.log(x[0] + '=' + x[1]);
}
更好的方式是直接使用 iterable 内置的 forEach 方法,它接收一个函数,每次迭代就自动回调该 函数。以 Array 为例
a.forEach(function (element, index, array) {
// element: 指向当前元素的值
// index: 指向当前索引
// array: 指向Array对象本身
console.log(element + ', index = ' + index);
});
Set 没有索引,因此回调函数的前两个参数都是元素本身:
var s = new Set(['A', 'B', 'C']);
s.forEach(function (element, sameElement, set) {
console.log(element);
});
Map 的回调函数参数依次为 value 、 key 和 map 本身:
var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
m.forEach(function (value, key, map) {
console.log(value);
});
函数
函数定义和调用
//方式一
function abs(x) {
if (x >= 0) {
return x;
} else {
return -x;
}
}
//方式二
var abs = function (x) {
if (x >= 0) {
return x;
} else {
return -x;
}
};
//调用
abs(10); // 返回10
abs(-9); // 返回9
方法
function getAge() {
var y = new Date().getFullYear();
return y - this.birth;
}
var xiaoming = {
name: '小明',
birth: 1990,
age: getAge
};
xiaoming.age(); // 25, 正常结果
getAge(); // NaN
Date
在JavaScript中, Date 对象用来表示日期和时间。注意,当前时间是浏览器从本机操作系统获取的时间
var now = new Date();
now; // Wed Jun 24 2015 19:49:22 GMT+0800 (CST)
now.getFullYear(); // 2015, 年份
now.getMonth(); // 5, 月份,注意月份范围是0~11,5表示六月
now.getDate(); // 24, 表示24号
now.getDay(); // 3, 表示星期三
now.getHours(); // 19, 24小时制
now.getMinutes(); // 49, 分钟
now.getSeconds(); // 22, 秒
now.getMilliseconds(); // 875, 毫秒数
now.getTime(); // 1435146562875, 以number形式表示的时间戳
var d = new Date(1435146562875);
d.toLocaleString(); // '2015/6/24 下午7:49:22',本地时间(北京时区+8:00),显示的字
符串与操作系统设定的格式有关
d.toUTCString(); // 'Wed, 24 Jun 2015 11:49:22 GMT',UTC时间,与本地时间相差8小时
JSON
JSON 和 JavaScript 对象互转
-
要实现从JSON字符串转换为JavaScript 对象,使用 JSON.parse() 方法:
var obj = JSON.parse('{"a": "Hello", "b": "World"}'); //结果是 {a: 'Hello', b: 'World'}
-
要实现从JavaScript 对象转换为JSON字符串,使用 JSON.stringify() 方法:
var json = JSON.stringify({a: 'Hello', b: 'World'}); //结果是 '{"a": "Hello", "b": "World"}'
操作BOM
window
window
对象不但充当全局作用域,而且表示浏览器窗口。 window
对象有 innerWidth
和innerHeight
属性,可以获取浏览器窗口的内部宽度和高度。 内部宽高是指除去菜单栏、工具栏、边框等占位元素后,用于显示网页的净宽高。
兼容性:IE<=8不支持。
'use strict';
// 可以调整浏览器窗口大小试试:
console.log('window inner size: ' + window.innerWidth + ' x ' +
window.innerHeight);
对应的,还有一个outerWidth
和 outerHeight
属性,可以获取浏览器窗口的整个宽高。
navigator
navigator
对象表示浏览器的信息,最常用的属性包括:
- navigator.appName:浏览器名称;
- navigator.appVersion:浏览器版本;
- navigator.language:浏览器设置的语言;
- navigator.platform:操作系统类型;
- navigator.userAgent:浏览器设定的 User-Agent 字符串。
'use strict';
console.log('appName = ' + navigator.appName);
console.log('appVersion = ' + navigator.appVersion);
console.log('language = ' + navigator.language);
console.log('platform = ' + navigator.platform);
console.log('userAgent = ' + navigator.userAgent);
screen
screen
对象表示屏幕的信息,常用的属性有:
- screen.width:屏幕宽度,以像素为单位;
- screen.height:屏幕高度,以像素为单位;
- screen.colorDepth:返回颜色位数,如8、16、24。
location
location
对象表示当前页面的URL信息。例如,一个完整的URL:
http://www.example.com:8080/path/index.html?a=1&b=2#TOP
可以用 location.href
获取。要获得URL各个部分的值,可以这么写:
location.protocol; // 'http'
location.host; // 'www.example.com'
location.port; // '8080'
location.pathname; // '/path/index.html'
location.search; // '?a=1&b=2'
location.hash; // 'TOP'
要加载一个新页面,可以调用location.assign()
。如果要重新加载当前页面,调用 location.reload()
方法非常方便。
location.reload();
location.assign('https://blog.kuangstudy.com/'); // 设置一个新的URL地址
document
document
对象表示当前页面。由于HTML在浏览器中以DOM形式表示为树形结构, document
对 象就是整个DOM树的根节点。
document
的title
属性是从HTML文档中的 xxx 读取的,但是可以动态改变:请观察浏览器窗口标题的变化。
document.title = 'javascript';
要查找DOM树的某个节点,需要从 document
对象开始查找。最常用的查找是根据ID和Tag Name。 我们先准备HTML数据:
<dl id="code-menu" style="border:solid 1px #ccc;padding:6px;">
<dt>Java</dt>
<dd>Spring</dd>
<dt>Python</dt>
<dd>Django</dd>
<dt>Linux</dt>
<dd>Docker</dd>
</dl>
用 document 对象提供的 getElementById()
和 getElementsByTagName()
可以按ID获得一个 DOM节点和按Tag名称获得一组DOM节点:
var menu = document.getElementById('code-menu');
var drinks = document.getElementsByTagName('dt');
var i, s;
s = '提供的饮料有:';
for (i=0; i<drinks.length; i++) {
s = s + drinks[i].innerHTML + ',';
}
console.log(s);
document
对象还有一个 cookie
属性,可以获取当前页面的Cookie。
document.cookie; // 'v=123; remember=true; prefer=zh'
操作DOM
选择器
由于HTML文档被浏览器解析后就是一棵DOM树,要改变HTML的结构,就需要通过JavaScript来操作 DOM。
始终记住DOM是一个树形结构。操作一个DOM节点实际上就是这么几个操作:
- 更新:更新该DOM节点的内容,相当于更新了该DOM节点表示的HTML的内容;
- 遍历:遍历该DOM节点下的子节点,以便进行进一步操作;
- 添加:在该DOM节点下新增一个子节点,相当于动态增加了一个HTML节点;
- 删除:将该节点从HTML中删除,相当于删掉了该DOM节点的内容以及它包含的所有子节点。
在操作一个DOM节点前,我们需要通过各种方式先拿到这个DOM节点。最常用的方法是document.getElementById()
和 document.getElementsByTagName()
,以及CSS选择器 document.getElementsByClassName()
。
由于ID在HTML文档中是唯一的,所以 document.getElementById()
可以直接定位唯一的一个DOM 节点。 document.getElementsByTagName()
和 document.getElementsByClassName()
总是返 回一组DOM节点(数组)。要精确地选择DOM,可以先定位父节点,再从父节点开始选择,以缩小范围。(数组选择)
// 返回ID为'test'的节点:
var test = document.getElementById('test');
// 先定位ID为'test-table'的节点,再返回其内部所有tr节点:
var trs = document.getElementById('test-table').getElementsByTagName('tr');
// 先定位ID为'test-div'的节点,再返回其内部所有class包含red的节点:
var reds = document.getElementById('testdiv').getElementsByClassName('red');
// 获取节点test下的所有直属子节点:
var cs = test.children;
// 获取节点test下第一个、最后一个子节点:
var first = test.firstElementChild;
var last = test.lastElementChild;
第二种方法是使用 querySelector()
和 querySelectorAll()
,需要了解selector语法,然后使 用条件来获取节点,更加方便:
// 通过querySelector获取ID为q1的节点:
var q1 = document.querySelector('#q1');
// 通过querySelectorAll获取q1节点内的符合条件的所有节点:
var ps = q1.querySelectorAll('div.highlighted > p');
注意:低版本的IE<8不支持 querySelector 和 querySelectorAll 。IE8仅有限支持。
更新DOM
拿到一个DOM节点后,我们可以对它进行更新。
可以直接修改节点的文本,方法有两种:
一种是修改 innerHTML
属性,这个方式非常强大,不但可以修改一个DOM节点的文本内容,还可以 直接通过HTML片段修改DOM节点内部的子树:
// 获取<p id="p-id">...</p>
var p = document.getElementById('p-id');
// 设置文本为abc:
p.innerHTML = 'ABC'; // <p id="p-id">ABC</p>
// 设置HTML:
p.innerHTML = 'ABC <span style="color:red">RED</span> XYZ';
// <p>...</p>的内部结构已修改
用 innerHTML
时要注意,是否需要写入HTML。如果写入的字符串是通过网络拿到了,要注意对字符 编码来避免XSS攻击。
第二种是修改innerText
属性,这样可以自动对字符串进行HTML编码,保证无法设置任何HTML标 签:
// 获取<p id="p-id">...</p>
var p = document.getElementById('p-id');
// 设置文本:
p.innerText = '<script>alert("Hi")</script>';
// HTML被自动编码,无法设置一个<script>节点:
// <p id="p-id"><script>alert("Hi")</script></p>
修改CSS也是经常需要的操作。DOM节点的style
属性对应所有的CSS,可以直接获取或设置。因为 CSS允许 font-size
这样的名称,但它并非JavaScript有效的属性名,所以需要在JavaScript中改写为 驼峰式命名fontSize
:
// 获取<p id="p-id">...</p>
var p = document.getElementById('p-id');
// 设置CSS:
p.style.color = '#ff0000';
p.style.fontSize = '20px';
p.style.paddingTop = '2em';
插入DOM
appendChild
<!-- HTML结构 -->
<p id="js">JavaScript</p>
<div id="list">
<p id="java">Java</p>
<p id="python">Python</p>
<p id="scheme">Scheme</p>
</div>
很多的时候我们会从零创建一个新的节点,然后插入到指定位置:
var
list = document.getElementById('list'),
haskell = document.createElement('p');
haskell.id = 'haskell';
haskell.innerText = 'Haskell';
list.appendChild(haskell);
这样我们就动态添加了一个新的节点:
<!-- HTML结构 -->
<div id="list">
<p id="java">Java</p>
<p id="python">Python</p>
<p id="scheme">Scheme</p>
<p id="haskell">Haskell</p>
</div>
举个例子,下面的代码动态创建了一个 节点,然后把它添加到 节点的末尾,这样就动态地给文档添加了新的CSS定义:
var d = document.createElement('style');
d.setAttribute('type', 'text/css');
d.innerHTML = 'p { color: red }';
//head 头部标签
document.getElementsByTagName('head')[0].appendChild(d);
insertBefore
如果我们要把子节点插入到指定的位置怎么办?可以使用 parentElement.insertBefore(newElement, referenceElement);
,子节点会插入到 referenceElement
之前。
还是以上面的HTML为例,假定我们要把 Haskell 插入到 Python 之前:
<!-- HTML结构 -->
<div id="list">
<p id="java">Java</p>
<p id="python">Python</p>
<p id="scheme">Scheme</p>
</div>
可以这么写:
var
list = document.getElementById('list'),
ref = document.getElementById('python'),
haskell = document.createElement('p');
haskell.id = 'haskell';
haskell.innerText = 'Haskell';
list.insertBefore(haskell, ref);
新的HTML结构如下:
<!-- HTML结构 -->
<div id="list">
<p id="java">Java</p>
<p id="haskell">Haskell</p>
<p id="python">Python</p>
<p id="scheme">Scheme</p>
</div>
删除DOM
删除一个DOM节点就比插入要容易得多。 要删除一个节点,首先要获得该节点本身以及它的父节点,然后,调用父节点的 removeChild
把自 己删掉:
// 拿到待删除节点:
var self = document.getElementById('to-be-removed');
// 拿到父节点:
var parent = self.parentElement;
// 删除:
var removed = parent.removeChild(self);
removed === self; // true
注意到删除后的节点虽然不在文档树中了,但其实它还在内存中,可以随时再次被添加到别的位置。
当你遍历一个父节点的子节点并进行删除操作时,要注意, children 属性是一个只读属性,并且它 在子节点变化时会实时更新。
例如,对于如下HTML结构:
<div id="parent">
<p>First</p>
<p>Second</p>
</div>
当我们用如下代码删除子节点时:
var parent = document.getElementById('parent');
parent.removeChild(parent.children[0]);
parent.removeChild(parent.children[1]); // <-- 浏览器报错
浏览器报错: parent.children[1]
不是一个有效的节点。原因就在于,当 First 节点被删除 后, parent.children 的节点数量已经从2变为了1,索引 [1] 已经不存在了。 因此,删除多个节点时,要注意 children 属性时刻都在变化。
操作表单
用JavaScript操作表单和操作DOM是类似的,因为表单本身也是DOM树。
HTML表单的输入控件主要有以下几种:
- 文本框,对应的
<input type="text">
,用于输入文本; - 口令框,对应的
<input type="password">
,用于输入口令; - 单选框,对应的
<input type="radio">
,用于选择一项; - 复选框,对应的
<input type="checkbox">
,用于选择多项; - 下拉框,对应的
<select>
,用于选择一项; - 隐藏文本,对应的
<input type="hidden">
,用户不可见,但表单提交时会把隐藏文本发送到 服务器。
获取值
如果我们获得了一个<input>
节点的引用,就可以直接调用 value 获得对应的用户输入值:
// <input type="text" id="email">
var input = document.getElementById('email');
input.value; // '用户输入的值'
这种方式可以应用于 text
、password
、 hidden
以及 select
。但是,对于单选框和复选 框,value
属性返回的永远是HTML预设的值,而我们需要获得的实际是用户是否“勾上了”选项,所 以应该用 checked
判断:
// <label><input type="radio" name="weekday" id="monday" value="1">Monday</label>
// <label><input type="radio" name="weekday" id="tuesday" value="2">Tuesday</label>
var mon = document.getElementById('monday');
var tue = document.getElementById('tuesday');
mon.value; // '1'
tue.value; // '2'
mon.checked; // true或者false
tue.checked; // true或者false
设置值
设置值和获取值类似,对于 text
、password
、 hidden
以及 select
,直接设置 value
就可以:
// <input type="text" id="email">
var input = document.getElementById('email');
input.value = 'test@example.com'; // 文本框的内容已更新
对于单选框和复选框,设置 checked
为 true
或false
即可。
提交表单
JavaScript可以以两种方式来处理表单的提交(AJAX方式在后面章节介绍)。
方式一是通过 <form>
元素的 submit()
方法提交一个表单,例如,响应一个 button
的click
事件,在JavaScript代码中提交表单:
<!-- HTML -->
<form id="test-form">
<input type="text" name="test">
<button type="button" onclick="doSubmitForm()">Submit</button>
</form>
<script>
function doSubmitForm() {
var form = document.getElementById('test-form');
// 可以在此修改form的input...
// 提交form:
form.submit();
}
</script>
这种方式的缺点是扰乱了浏览器对form的正常提交。浏览器默认点击 时<button type="submit">
提交表单,或者用户在最后一个输入框按回车键。因此,第二种方式是响应<form>
本身的onsubmit
事件,在提交form时作修改:
<!-- HTML -->
<form id="test-form" onsubmit="return checkForm()">
<input type="text" name="test">
<button type="submit">Submit</button>
</form>
<script>
function checkForm() {
var form = document.getElementById('test-form');
// 可以在此修改form的input...
// 继续下一步:
return true;
}
</script>
注意要 return true
来告诉浏览器继续提交,如果return false
,浏览器将不会继续提交 form,这种情况通常对应用户输入有误,提示用户错误信息后终止提交form。
在检查和修改 <input>
时,要充分利用<input type="hidden">
来传递数据。
例如,很多登录表单希望用户输入用户名和口令,但是,安全考虑,提交表单时不传输明文口令,而是 口令的MD5。要想不改变用户的输入,可以利用<input type="hidden">
实现:
<!-- HTML -->
<form id="login-form" method="post" onsubmit="return checkForm()">
<input type="text" id="username" name="username">
<input type="password" id="input-password">
<input type="hidden" id="md5-password" name="password">
<button type="submit">Submit</button>
</form>
<script>
function checkForm() {
var input_pwd = document.getElementById('input-password');
var md5_pwd = document.getElementById('md5-password');
// 把用户输入的明文变为MD5:
md5_pwd.value = md5(input_pwd.value);
// 继续下一步:
return true;
}
</script>
jQuery
jQuery这么流行,肯定是因为它解决了一些很重要的问题。实际上,jQuery能帮我们干这些事情:
- 消除浏览器差异:你不需要自己写冗长的代码来针对不同的浏览器来绑定事件,编写AJAX等代码;
- 简洁的操作DOM的方法:写 $(’#test’) 肯定比 document.getElementById(‘test’) 来得 简洁;
- 轻松实现动画、修改CSS等各种操作。
使用jQuery只需要在页面的head
引入jQuery文件即可:
公式:$(selector).action()
- 美元符号定义 jQuery
- 选择符(selector)“查询"和"查找” HTML 元素
- jQuery 的 action() 执行对元素的操作
选择器
按ID查找
如果某个DOM节点有 id 属性,利用jQuery查找如下:
// 查找<div id="abc">:
var div = $('#abc');
按tag查找
按tag查找只需要写上tag名称就可以了:
var ps = $('p'); // 返回所有<p>节点
ps.length; // 数一数页面有多少个<p>节点
按class查找
按class查找注意在class名称前加一个 . :
var a = $('.red'); // 所有节点包含`class="red"`都将返回
// 例如:
// <div class="red">...</div>
// <p class="green red">...</p>
按属性查找
一个DOM节点除了id
和class
外还可以有很多属性,很多时候按属性查找会非常方便,比如在一 个表单中按属性来查找:
var email = $('[name=email]'); // 找出<??? name="email">
var passwordInput = $('[type=password]'); // 找出<??? type="password">
var a = $('[items="A B"]'); // 找出<??? items="A B">
当属性的值包含空格等特殊字符时,需要用双引号括起来。 按属性查找还可以使用前缀查找或者后缀查找:
var icons = $('[name^=icon]'); // 找出所有name属性值以icon开头的DOM
// 例如: name="icon-1", name="icon-2"
var names = $('[name$=with]'); // 找出所有name属性值以with结尾的DOM
// 例如: name="startswith", name="endswith"
操作DOM
修改Text和HTML
jQuery对象的text()
和html()
方法分别获取节点的文本和原始HTML文本,例如,如下的HTML 结构:
<!-- HTML结构 -->
<ul id="test-ul">
<li class="js">JavaScript</li>
<li name="book">Java & JavaScript</li>
</ul>
分别获取文本和HTML:
$('#test-ul li[name=book]').text(); // 'Java & JavaScript'
$('#test-ul li[name=book]').html(); // 'Java & JavaScript'
如何设置文本或HTML?jQuery的API设计非常巧妙:无参数调用 text()
是获取文本,传入参数就变 成设置文本,HTML也是类似操作,自己动手试试:
var j1 = $('#test-ul li.js');
var j2 = $('#test-ul li[name=book]');
j1.html('<span style="color: red">JavaScript</span>');
j2.text('JavaScript & ECMAScript');
个jQuery对象可以包含0个或任意个DOM对象,它的方法实际上会作用在对应的每个DOM节点上。在 上面的例子中试试:
$('#test-ul li').text('JS'); // 是不是两个节点都变成了JS?
修改css
jQuery对象有“批量操作”的特点,这用于修改CSS实在是太方便了。考虑下面的HTML结构:
<!-- HTML结构 -->
<ul id="test-css">
<li class="lang dy"><span>JavaScript</span></li>
<li class="lang"><span>Java</span></li>
<li class="lang dy"><span>Python</span></li>
<li class="lang"><span>Swift</span></li>
<li class="lang dy"><span>Scheme</span></li>
</ul>
要高亮显示动态语言,调用jQuery对象的 css('name', 'value')
方法,我们用一行语句实现:
$('#test-css li.dy>span').css('background-color', '#ffd351').css('color',
'red');
jQuery对象的 css() 方法可以这么用:
var div = $('#test-div');
div.css('color'); // '#000033', 获取CSS属性
div.css('color', '#336699'); // 设置CSS属性
div.css('color', ''); // 清除CSS属性
css()
方法将作用于DOM节点的 style
属性,具有最高优先级。如果要修改 class
属性,可 以用jQuery提供的下列方法:
var div = $('#test-div');
div.hasClass('highlight'); // false, class是否包含highlight
div.addClass('highlight'); // 添加highlight这个class
div.removeClass('highlight'); // 删除highlight这个class
显示和隐藏DOM
要隐藏一个DOM,我们可以设置CSS的 display 属性为 none ,利用 css() 方法就可以实现。 不过,要显示这个DOM就需要恢复原有的 display 属性,这就得先记下来原有的 display 属性到 底是 block 还是 inline 还是别的值。
考虑到显示和隐藏DOM元素使用非常普遍,jQuery直接提供 show()
和 hide()
方法,我们不用关 心它是如何修改display
属性的,总之它能正常工作:
var a = $('a[target=_blank]');
a.hide(); // 隐藏
a.show(); // 显示
获取DOM信息
利用jQuery对象的若干方法,我们直接可以获取DOM的高宽等信息,而无需针对不同浏览器编写特定代 码:
// 浏览器可视窗口大小:
$(window).width(); // 800
$(window).height(); // 600
// HTML文档大小:
$(document).width(); // 800
$(document).height(); // 3500
// 某个div的大小:
var div = $('#test-div');
div.width(); // 600
div.height(); // 300
div.width(400); // 设置CSS属性 width: 400px,是否生效要看CSS是否有效
div.height('200px'); // 设置CSS属性 height: 200px,是否生效要看CSS是否有效
attr()
和 removeAttr()
方法用于操作DOM节点的属性:
// <div id="test-div" name="Test" start="1">...</div>
var div = $('#test-div');
div.attr('data'); // undefined, 属性不存在
div.attr('name'); // 'Test'
div.attr('name', 'Hello'); // div的name属性变为'Hello'
div.removeAttr('name'); // 删除name属性
div.attr('name'); // undefined
操作表单
对于表单元素,jQuery对象统一提供val()
方法获取和设置对应的 value
属性:
/*
<input id="test-input" name="email" value="">
<select id="test-select" name="city">
<option value="BJ" selected>Beijing</option>
<option value="SH">Shanghai</option>
<option value="SZ">Shenzhen</option>
</select>
<textarea id="test-textarea">Hello</textarea>
*/
var
input = $('#test-input'),
select = $('#test-select'),
textarea = $('#test-textarea');
input.val(); // 'test'
input.val('abc@example.com'); // 文本框的内容已变为abc@example.com
select.val(); // 'BJ'
select.val('SH'); // 选择框已变为Shanghai
textarea.val(); // 'Hello'
textarea.val('Hi'); // 文本区域已更新为'Hi'
可见,一个 val()
就统一了各种输入框的取值和赋值的问题。
添加DOM
要添加新的DOM节点,除了通过jQuery的 html()
这种暴力方法外,还可以用 append()
方法,例 如:
<div id="test-div">
<ul>
<li><span>JavaScript</span></li>
<li><span>Python</span></li>
<li><span>Swift</span></li>
</ul>
</div
如何向列表新增一个语言?首先要拿到 <ul>
节点:
var ul = $('#test-div>ul');
然后,调用append()
传入HTML片段:
ul.append('<li><span>Haskell</span></li>');
append()
把DOM添加到最后, prepend()
则把DOM添加到最前。
如果要把新节点插入到指定位置,例如,JavaScript和Python之间,那么,可以先定位到JavaScript,然 后用 after()
方法:
var js = $('#test-div>ul>li:first-child');
js.after('<li><span>Lua</span></li>');
也就是说,同级节点可以用 after()
或者before()
方法。
删除节点
要删除DOM节点,拿到jQuery对象后直接调用 remove()
方法就可以了。如果jQuery对象包含若干 DOM节点,实际上可以一次删除多个DOM节点:
var li = $('#test-div>ul>li');
li.remove(); // 所有<li>全被删除
事件
jQuery能够绑定的事件主要包括:
鼠标事件
- click: 鼠标单击时触发;
- dblclick:鼠标双击时触发;
- mouseenter:鼠标进入时触发;
- mouseleave: 鼠标移出时触发;
- mousemove:鼠标在DOM内部移动时触发;
- hover:鼠标进入和退出时触发两个函 数,相当于mouseenter加上mouseleave。
键盘事件
键盘事件仅作用在当前焦点的DOM上,通常是和
。
- keydown:键盘按下时触发;
- keyup:键盘松开时触发;
- keypress:按一次键后触发。
其他事件
- focus:当DOM获得焦点时触发;
- blur:当DOM失去焦点时触发;
- change:当 、 或 的内容改变时 触发;
- submit:当 提交时触发;
- ready:当页面被载入并且DOM树完成初始化后触发。
初始化事件
$(function () {
// init...
});
例子:
$(function () {
// init...
$('#testForm).submit(function () {
alert('submit!');
});
});
事件参数
有些事件,如 mousemove
和 keypress
,我们需要获取鼠标位置和按键的值,否则监听这些事件就 没什么意义了。所有事件都会传入 Event
对象作为参数,可以从Event
对象上获取到更多的信 息:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
#testMouseMoveDiv{
width: 300px;
height: 300px;
border: 1px solid black;
}
</style>
</head>
<body>
mousemove: <span id="testMouseMoveSpan"></span>
<div id="testMouseMoveDiv">
在此区域移动鼠标试试
</div>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
<script>
$(function () {
$('#testMouseMoveDiv').mousemove(function (e) {
$('#testMouseMoveSpan').text('pageX = ' + e.pageX + ', pageY = '
+ e.pageY);
});
});
</script>
</body>
</html>