<script>
var variable = undefined;
console.log(variable + 'pink'); //undefinedpink
console.log(variable + 1); //NaN
</script>
<script>
var variable = null;
console.log(variable + 'pink'); //nullpink
console.log(variable + 1); //1
</script>
详见下方三。
<script>
var num = 10;
console.log(typeof num); //number
var str = 'pink';
console.log(typeof str); //string
var flag = true;
console.log(typeof flag); //boolean
var variable = undefined;
console.log(typeof variable); //undefined
var timer = null;
console.log(typeof timer); //object
</script>
<script>
var num = 10;
console.log(typeof num.toString()); //string
console.log(typeof String(num)); //string
//隐式转换
console.log(typeof(num + '')); //string
</script>
<script>
// 1.parseInt
var age = '18';
console.log(typeof parseInt(age)); //number
console.log(parseInt('3.14')); //3 取整
console.log(parseInt('3.94')); //3 取整
console.log(parseInt('120px')); //120 会去掉单位
// 2.parseFloat
console.log(parseFloat('3.14')); //3.14
// 3.Number
console.log(Number('123')); //123
// 4.利用算术运算 - * / 隐式转换
console.log(typeof('12' - 0)); //number
console.log('123' - '120'); //3
console.log('123' * 1); //123
</script>
<script>
var arr = [4, 1, 3, 5, 9];
for (var i = 0; i <= arr.length - 1; i++) {
for (var j = 0; j < arr.length - -1 - i; j++) {
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
console.log(arr);//[1,3,4,5,9]
</script>
function getMax() {
var max = arguments[0];
for (var i = 0; i < arguments.length; i++) {
if (max < arguments[i]) {
max = arguments[i];
}
}
return max;
}
console.log(getMax(9, 7, 12, 3));
// 1. 利用函数关键字 自定义函数(命名函数)
// 声明提前
fn(); //我是自定义函数
function fn() {
console.log('我是自定义函数');
}
fn(); //我是自定义函数
fun(); // fun is not a function
// 2. 函数表达式
var fun = function() {
console.log('我是函数表达式');
};
fun();
var num = 10;
console.log(num); //10
function fn() {
console.log(num); //10
num2 = 20; //全局变量
}
fn();
console.log(num2); //20
function fun(aru) {
var num1 = 10;
}
fun();
console.log(aru); //aru is not defined
console.log(num1); // num1 is not defined
JS引擎运行JS时,分为两步:
预解析
和代码执行
。
(1)预解析
JS引擎会把JS 里面所有的var还有function提升到当前作用域的最前面。
(2)代码执行
按照代码书写的顺序从上往下执行。
// 变量提升:把所有的变量声明 提升到 当前作用域 的最前面,不提升赋值操作
// 坑1
console.log(num); //undefined
var num = 10;
// 上诉两行代码相当于执行了以下代码:
var num;
console.log(num);
num = 10;
// 坑2
fun();
var fun = function() {
console.log(22); // fun is not a function
};
// 上诉三行代码相当于执行了以下代码:
var fun;
fun();
fun = function() {
console.log(22); // fun is not a function
}
// 函数提升:把所有的函数声明 提升到 当前作用域 的最前面,不调用函数
fn();
function fn() {
console.log(11);
}
// 上诉三行代码相当于执行了以下代码:
function fn() {
console.log(11);
}
fn();
案例:
链接: https://www.bilibili.com/video/BV1hT4y1u7qb?p=141.
//案例1:
f1();
console.log(c);
console.log(b);
console.log(a);
function f1() {
var a = b = c = 9;
// 相当于 var a = 9;b=9;c=9 c和b直接赋值 没有var声明 当 全局变量看
console.log(a);
console.log(b);
console.log(c);
}
// 上诉代码相当于执行了以下代码:
function f1() {
var a;
a = 9;
b = 9;
c = 9
console.log(a); //9
console.log(b); //9
console.log(c); //9
}
f1();
console.log(c); //9
console.log(b); //9
console.log(a); //a is not defined
//案例2:
var a = 18;
f1();
function f1() {
var b = 9;
console.log(a);
console.log(b);
var a = '123';
}
// 上诉代码相当于执行了以下代码:
var a;
function f1() {
var b;
var a;
b = 9;
console.log(a); //undefined
console.log(b); //9
a = '123';
};
a = 18;
f1();
对象字面量{}
创建对象var obj = {
uname: '吴心怡',
age: '21',
sex: '女',
sayHi: function() {
console.log('hi');
}
}
console.log(obj.uname);
console.log(obj['age']);
obj.sayHi();
new Object
创建对象var obj = new Object();
obj.uname = 'wxy';
obj.age = 21;
obj.sex = '女';
obj.sayHi = function() {
console.log('hi');
}
console.log(obj.uname);
obj.sayHi();
构造函数
创建对象function Star(uname, age, sex) {
this.uname = uname;
this.age = age;
this.sex = sex;
this.sing = function(sang) {
console.log(sang);
}
}
var ldh = new Star('刘德华', 18, '男');
console.log(typeof ldh);
console.log(ldh.uname);
ldh.sing('冰雨');
<script>
var obj = {
uname: 'wxy',
age: '21',
sex: '女',
sayHi: function() {
console.log('hi');
}
}
for (var k in obj) {
console.log(k); //uname;k输出得到的是属性名
console.log(obj[k]); //wxy;obj[k]得到的是属性值
}
</script>
JS中的对象分为3种:自定义对象、内置对象、浏览器对象。
JS提供了多个内置对象:Math、Date、Array、String等。
MDN:https://developer.mozilla.org/zh-CN/.
Math 不是一个构造函数
,不需要new来调用;而是直接使用里面的属性和方法即可。
<script>
console.log(Math.PI); //3.141592653589793
console.log(Math.max(1, 9, 4, 7)); //9
console.log(Math.max(-10, -20)); //-10
console.log(Math.max(10, 50, 'wxy')); //NaN
console.log(Math.max()); //-Infinity 负无穷大
console.log(Math.abs(-1)); //1 绝对值
console.log(Math.abs('-1')); //隐式转换 会把 字符串型 -1 转换为整数型
console.log(Math.abs('pink')); //NaN
console.log(Math.floor(3.5)); //3 向下取整
console.log(Math.ceil(3.5)); //4 向上取整
console.log(Math.round(3.5)); //4 四舍五入
console.log(Math.round(3.4)); //3
console.log(Math.round(-1.1)); //-1
console.log(Math.round(-1.5)); //-1 其他数字都是四舍五入 但是 .5 往大了取
</script>
<script>
//Math.random(); 返回的是 一个[0,1)的随机小数
Math.random();
//得到两个数之间的随机整数 并且包含这两个整数
function getRandom(min, max) {
// min = Math.ceil(min);
// max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1) + min); //含最大值、最小值
}
console.log(getRandom(1, 10)); //【1,10】之间的数
</script>
Date是一个构造函数
,必须使用new
来调用创建日期对象。
var date = new Date(); //Date没有跟参数返回当前系统的当前时间
console.log(date); //Sun Jul 26 2020 23:18:13 GMT+0800 (中国标准时间)
var date1 = new Date(2020, 7, 26);
console.log(date1); //Wed Aug 26 2020 00:00:00 GMT+0800 (中国标准时间) 返回的是八月不是七月
var date2 = new Date('2020-7-26 23:19:26');
console.log(date2); //Sun Jul 26 2020 23:19:26 GMT+0800 (中国标准时间)
var year = date.getFullYear(); //年
var month = date.getMonth() + 1; //月
var dates = date.getDate(); //日
var day = date.getDay(); //星期几
var hours = date.getHours(); //时
hours = hours < 10 ? '0' + hours : hours;
var min = date.getMinutes(); //分
min = min < 10 ? '0' + min : min;
var s = date.getSeconds(); //秒
s = s < 10 ? '0' + s : s;
var days = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
console.log('今天是' + year + '年' + month + '月' + dates + '日 ' + days[day] + ' ' + hours + ':' + min + ':' + s); //今天是2020年7月26日 星期日 23:51:35
// 1. 通过 getTime()
console.log(date.getTime());
// 2. 通过 valueOf()
console.log(date.valueOf());
// 3. 通过 +new Date()
var date1 = +new Date();
console.log(date1);
// 4.通过 H5 新增的 获取总的毫秒数
console.log(Date.now());
function countDown(time) {
var nowTime = +new Date(); //当前秒数
var inputTime = +new Date(time); //用户输入的时间
var times = (inputTime - nowTime) / 1000; //剩余的秒数
var d = parseInt(times / 60 / 60 / 24); //天
d = d < 10 ? '0' + d : d;
var h = parseInt(times / 60 / 60 % 24); //时
h = h < 10 ? '0' + h : h;
var m = parseInt(times / 60 % 60); //分
m = m < 10 ? '0' + m : m;
var s = parseInt(times % 60); //秒
s = s < 10 ? '0' + s : s;
return d + '天' + h + '时' + m + '分' + s + '秒';
}
console.log(countDown('2020-7-27 15:02:02')); //00天14时14分42秒
// 1.利用 数组字面量
var arr = [1, 2, 3];
console.log(arr); //[1, 2, 3]
// 2.利用 new Array()
var arr1 = new Array(2, 3);
console.log(arr1); // [2, 3]
var arr1 = new Array(2);
console.log(arr1); // [empty × 2]
// 1) instanceof 判断arr是否为数组类型
console.log(arr instanceof Array); //true
console.log(arr instanceof String); //false
// 2) H5新增的方法 Array.isArray(参数)
console.log(Array.isArray([1, 2, 3])); //true
console.log(Array.isArray({})); //false
var arr = [1, 2, 3];
//1. push 在末尾添加一个或多个元素
// (1)返回结果是 新数组的长度
// (2)原数组也会发生变化
arr.push(4);
arr.push(5, 'wxy');
console.log(arr); //[1, 2, 3, 4, 5, "wxy"]
console.log(arr.push(6, 'abc')); //8
//2. unshift 在前面添加一个或多个元素
// (1)返回结果是 新数组的长度
// (2)原数组也会发生变化
var arr = [1, 2, 3];
arr.unshift('red', 'purple');
console.log(arr); // ["red", "purple", 1, 2, 3]
console.log(arr.unshift('green', 4)); //7
//3. pop 一次只能删除一个末尾的元素
// (1)返回结果是 删除的元素
// (2)原数组也会发生变化
// (3)()里面不能加参数
console.log(arr); //["green", 4, "red", "purple", 1, 2, 3]
arr.pop();
console.log(arr); //["green", 4, "red", "purple", 1, 2]
console.log(arr.pop()); //2
// console.log(arr.pop()); //返回的是删除的最后一个元素的值
//4. shift 删除数组第一个元素
// (1)返回结果是 删除的元素
// (2)原数组也会发生变化
// (3)()里面不能加参数
arr.shift();
console.log(arr); //[4, "red", "purple", 1]
console.log(arr.shift()); //4
//5. splice 删除、增加 任意元素
// (1)返回结果是 被删除的元素组成的新数组
// (2)原数组也会发生变化
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
// (3) 结构:数组 splice(start,deletedCount)
console.log(arr.splice(2, 2)); //[3, 4]
console.log(arr); //[1, 2, 5, 6, 7, 8, 9]
arr.splice(3, 0, 'wxy');
// (4) 结构:数组 splice(start,deletedCount,item)
console.log(arr); //[1, 2, 5, "wxy", 6, 7, 8, 9]
//6. slice 删除、增加 任意元素
// (1)返回结果是 被删除的元素组成的新数组
// (2)原数组不会发生变化
// (3) 结构:数组 splice(start,end) 包括atart下标不包括end下标
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(arr.slice(2, 5)); //[3, 4, 5]
console.log(arr); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
// 翻转数组
var arr = ['red', 'green', 'blue'];
arr.reverse();
console.log(arr); //["blue", "green", "red"]
// 排序
var arr1 = [13, 4, 77, 1, 7];
console.log(arr1.sort()); //[1, 13, 4, 7, 77]
arr1.sort(function(a, b) {
return a - b; //升序
// return b - a; //降序
});
console.log(arr1); // [1, 4, 7, 13, 77]
var arr = ['red', 'green', 'blue', 'purple', 'blue'];
// 1. indexof 是从前往后查找
console.log(arr.indexOf('blue')); // 2
console.log(arr.indexOf('pink')); // -1
// 2. lastIndexof 是从后往前查找
console.log(arr.lastIndexOf('blue')); // 4
console.log(arr.lastIndexOf('pink')); // -1
function unique(arr) {
var newArr = [];
for (var i = 0; i < arr.length; i++) {
if (newArr.indexOf(arr[i]) === -1) {
newArr.push(arr[i]);
}
}
return newArr;
}
var demo = unique(['a', 'c', 'a', 'd', 'g', 's', 'c', 'd']);
var demo1 = unique([1, 2, 3, 4, 5, 4, 2, 1]);
console.log(demo); //["a", "c", "d", "g", "s"]
console.log(demo1); //[1, 2, 3, 4, 5]
//1.toString()将我们的数组转换为字符串
var arr = [1, 2, 3];
console.log(arr.toString()); // 1,2,3
//2.join(分隔符)
var arr1 = ['blue', 'red', 'green', 'purple'];
console.log(arr1.join()); //默认逗号分隔 blue,red,green,purple
console.log(arr1.join('-')); // blue-red-green-purple
console.log(arr1.join('&')); // blue&red&green&purple
var str = "abcoefoxyozzopp";
var index = str.indexOf('o');
var num = 0;
while (index !== -1) {
num++;
console.log(index);
index = str.indexOf('o', index + 1);//3 6 9 12
}
console.log(num);// 4
<script>
var str = 'andy';
// 1. charAt(index) 根据位置返回字符串
console.log(str.charAt(2)); //2
// 遍历所有的字符
for (var i = 0; i < str.length; i++) {
console.log(str.charAt(i)); // a n d y
}
// 2. charCodeAt(index) 返回相应索引号字符的ASCILL值
console.log(str.charCodeAt(0)); //97
// 3. str[index] H5新增方法
console.log(str[1]); //n
</script>
var str = 'red';
// 1.拼接
console.log(str.concat('pink')); //redpink
// 2.截取
var str2 = '小猪佩奇真可爱'
console.log(str2.substr(2, 2)); //佩奇 从2号位置开始取两个字符
// 1. 替换字符 replace 只会替换第一个字符
var str = "andya";
console.log(str.replace('a', 'b')); //只替换第一个a bndya
// 2. 字符转换为数组 split('分隔符')
//join 数组转换为字符串
var arr = [1, 2, 3];
var str;
str = arr.join('-');
console.log(str); //1-2-3
//split 字符串转换为数组
var str1 = 'red&pink&blue';
console.log(str1.split('&')); //有’&‘的地方就用,分隔开 ["red", "pink", "blue"]
注意:null返回的是object类型
,如果有个变量我们以后打算存储为对象,暂时没想好放啥,这个时候就给null。
课件视频链接:
链接: https://www.bilibili.com/video/BV1hT4y1u7qb?p=188.
使用
getElementById()
方法可以获取带有ID的元素对象。
<body>
<div id="time">2020-5-13</div>
<script>
var timer = document.getElementById('time');
console.log(timer); //2020-5-13
console.log(typeof timer); //object 返回的是一个对象
console.dir(timer);//打印我们返回的元素对象 更好的查看里面的属性和方法
</script>
</body>
使用
getElementsByTagName()
方法可以返回带有指定标签名的对象的集合
。
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<ul id="nav">
<li>abc</li>
<li>abc</li>
<li>abc</li>
<li>abc</li>
<li>abc</li>
</ul>abc
<script>
// 1. 返回的是 获取过来元素对象的集合 以伪数组的形式存储
var lis = document.getElementsByTagName('li');
console.log(lis); // HTMLCollection(10) [li, li, li, li, li, li, li, li, li, li]
console.log(lis[0]); // 1
for (var i = 0; i < lis.length; i++) {
console.log(lis[i]);
}
// 2. element.getElementsByTagName() 可以得到这个元素里面的某些标签
var nav = document.getElementById('nav');
var navlis = nav.getElementsByTagName('li');
console.log(navlis); // HTMLCollection(5) [li, li, li, li, li]
</script>
</body>
<body>
<div class="box">盒子1</div>
<div class="box">盒子2</div>
<div class="nav">
<ul>
<li>首页</li>
<li>产品</li>
</ul>
</div>
<script>
//1. getElementsByClassName 根据类名返回元素对象集合
var boxs = document.getElementsByClassName('box');
console.log(boxs); // HTMLCollection(2) [div.box, div.box]
//2. querySelector 根据指定选择器返回第一个元素对象 切记里面的选择器需要加符号
var box = document.querySelector('.box');
console.log(box); // 盒子1
//3. querySelectorAll 根据指定选择器返回所有对象
var boxall = document.querySelectorAll('.box');
console.log(boxall); //NodeList(2) [div.box, div.box]
</script>
</body>
// 1.获取body元素
var bodyEle = document.body;
console.log(bodyEle);
// 2.获取html元素
var htmlEle = document.documentElement;
console.log(htmlEle);
<body>
<button id="wxy">吴心怡</button>
<script>
// 1. 事件三要素:事件源 事件类型 事件处理程序
//(1) 事件源 事件被触发的对象
var wxy = document.getElementById('wxy');
//(2) 事件类型 点击事件
//(3) 事件处理程序 通过一个函数赋值的方式 完成
wxy.onclick = function() {
alert('无限可能');
}
</script>
</body>
<body>
<button>显示当前系统时间</button>
<div id="div">某个时间</div>
<div id="div1">2</div>
<p>
我是文字
<span>123</span>
</p>
<script>
var btn = document.querySelector('button');
var div1 = document.querySelector('div');
var div2 = document.getElementById('div1');
btn.onclick = function() {
div1.innerText = getDate(); //今天是2020年7月28日星期二
// innerText 和 innerHTML的区别
// 1. innerText 不识别html标签 非标准
div1.innerText = '今天是2019'; //今天是2019
// 2. innerHTML 识别html标签 W3C标准
div2.innerHTML = '今天是2019'; //今天是2019
// 3. 这两个属性是可读写的 可以获取元素里面的内容
// 3.1 innerText 去除空格和换行
var p = document.querySelector('p');
console.log(p.innerText); //我是文字 123
// 3.2 innerHTML 保留空格和换行
console.log(p.innerHTML);
/*
我是文字
123
*/
}
function getDate() {
var date = new Date();
var year = date.getFullYear();
var month = date.getMonth() + 1;
var dates = date.getDate();
var day = date.getDay();
var days = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
return '今天是' + year + '年' + month + '月' + dates + '日' + days[day];
}
</script>
</body>
<body>
<button id='right'>对</button>
<button id='error'>错</button>
<img src="../images/right.png" alt="" title="对">
<script>
var right = document.getElementById('right');
var error = document.getElementById('error');
var img = document.querySelector('img');
error.onclick = function() {
img.src = '../images/wrong.png';
img.title = '错';
}
right.onclick = function() {
img.src = '../images/right.png';
img.title = '对';
}
</script>
</body>
<div class="box">
<input type="password">
<span><img src="../images/close.png" alt=""></span>
</div>
<script>
var img = document.querySelector('img');
var input = document.querySelector('input');
var flag = 0;
img.onclick = function() {
if (flag == 0) {
input.type = 'text';
this.src = '../images/open.png';
flag = 1;
} else {
input.type = 'password';
this.src = '../images/close.png';
flag = 0;
}
}
</script>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>样式属性</title>
<style>
div {
width: 200px;
height: 200px;
background-color: pink;
}
.change {
background-color: purple;
font-size: 25px;
margin-top: 100px;
color: #fff;
}
</style>
</head>
<body>
<div class="first">文本</div>
<script>
var div = document.querySelector('div');
div.onclick = function() {
//1. 使用element.style 里面的属性采取驼峰命名法 css的权重更高
// 样式较少 功能简单的情况下使用
this.style.backgroundColor = 'blue';
//2. 通过修改元素的className更改元素的样式 适合于样式较多或者功能复杂的情况下,会覆盖原先的类名
this.className = 'change';
//3 .如果要保留原先的类名
this.className = 'first change';
}
</script>
</body>
仿新浪注册页面:
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
width: 600px;
margin: 100px auto;
}
.message {
display: inline-block;
font-size: 12px;
color: #999;
background: url(../images/mess.png) no-repeat left center;
padding-left: 20px;
}
.wrong {
color: red;
background-image: url(../images/wrong.png);
}
.right {
color: green;
background-image: url(../images/right.png);
}
</style>
</head>
<body>
<div class="register">
<input type="password" class="ipt">
<p class="message">请输入6~16位密码</p>
</div>
<script>
var ipt = document.querySelector('.ipt');
var message = document.querySelector('.message');
ipt.onblur = function() {
if (ipt.value.length < 6 || ipt.value.length > 16) {
message.className = 'message wrong';
message.innerHTML = '您输入的位数不对,要求6~16位';
} else {
message.className = 'message right';
message.innerHTML = '您输入的正确';
}
}
</script>
</body>
结果演示:
隔行变色:
<body>
<table>
<thead>
<tr>
<th>编号</th>
<th>姓名</th>
<th>年龄</th>
</tr>
</thead>
<tbody>
<tr>
<td>001</td>
<td>wxy</td>
<td>21</td>
</tr>
<tr>
<td>002</td>
<td>wxy</td>
<td>21</td>
</tr>
<tr>
<td>003</td>
<td>wxy</td>
<td>21</td>
</tr>
<tr>
<td>004</td>
<td>wxy</td>
<td>21</td>
</tr>
<tr>
<td>005</td>
<td>wxy</td>
<td>21</td>
</tr>
</tbody>
</table>
<script>
var trs = document.querySelector('tbody').querySelectorAll('tr');
for (var i = 0; i < trs.length; i++) {
// 鼠标经过
trs[i].onmousemove = function() {
this.style.backgroundColor = 'pink';
};
// 鼠标离开
trs[i].onmouseout = function() {
this.style.backgroundColor = '';
}
}
</script>
</body>
表单全选取消全选:
<body>
<div class="wrap">
<table>
<thead>
<tr>
<th>
<input type="checkbox" id="j_cbAll">
</th>
<th>商品</th>
<th>价格</th>
</tr>
</thead>
<tbody id="j_tb">
<tr>
<td>
<input type="checkbox">
</td>
<td>iphone8</td>
<td>8000</td>
</tr>
<tr>
<td>
<input type="checkbox">
</td>
<td>iphone8</td>
<td>8000</td>
</tr>
<tr>
<td>
<input type="checkbox">
</td>
<td>iphone8</td>
<td>8000</td>
</tr>
<tr>
<td>
<input type="checkbox">
</td>
<td>iphone8</td>
<td>8000</td>
</tr>
</tbody>
</table>
</div>
<script>
var j_cbAll = document.getElementById('j_cbAll');
var j_tbs = document.getElementById('j_tb').getElementsByTagName('input');
j_cbAll.onclick = function() {
for (var i = 0; i < j_tbs.length; i++) {
j_tbs[i].checked = this.checked;
}
}
for (var j = 0; j < j_tbs.length; j++) {
j_tbs[j].onclick = function() {
var flag = true;
for (var k = 0; k < j_tbs.length; k++) {
if (j_tbs[k].checked == false) {
flag = false;
break;
}
}
j_cbAll.checked = flag;
}
}
</script>
</body>
<body>
<div class="tab">
<div class="tab_list">
<ul>
<li class="current">商品介绍</li>
<li>规格与包装</li>
<li>售后保障</li>
<li>商品评价</li>
<li>手机社区</li>
</ul>
</div>
<div class="tab_con">
<div class="item" style="display: block;">
商品介绍模块内容
</div>
<div class="item">
规格与包装模块内容
</div>
<div class="item">
售后保障模块内容
</div>
<div class="item">
商品评价模块内容
</div>
<div class="item">
手机社区模块内容
</div>
</div>
</div>
<script>
var tab_list = document.querySelector('.tab_list');
var items = document.querySelectorAll('.item');
var lis = tab_list.querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
//给五个li设置index自定义属性
lis[i].setAttribute('index', i);
lis[i].onclick = function() {
for (var j = 0; j < lis.length; j++) {
lis[j].className = '';
}
this.className = 'current';
for (var k = 0; k < items.length; k++) {
items[k].style.display = 'none';
}
//找到相应的index 将其显示出来
items[this.getAttribute('index')].style.display = 'block';
}
}
</script>
</body>
<body>
<div gettime="18" data-index="2" data-list-name="wxy"></div>
<script>
var div = document.querySelector('div');
console.log(div.gettime); //undefined gettime不是元素本身自带的属性
console.log(div.getAttribute('gettime')); //18
console.log(div.dataset.index); //2
console.log(div.dataset['index']); //2
console.log(div.getAttribute('data-list-name')); //wxy
// 如果自定义属性有多个短横线连接的单词,获取时要采取驼峰命名
console.log(div.dataset.listName); //wxy
console.log(div.dataset['listName']); //wxy
</script>
</body>
<body>
<ul class="box">
<li>
<a href="">微博</a>
<ul>
<li>私信</li>
<li>评论</li>
<li>@我</li>
</ul>
</li>
<li>
<a href="">微博</a>
<ul>
<li>私信</li>
<li>评论</li>
<li>@我</li>
</ul>
</li>
<li>
<a href="">微博</a>
<ul>
<li>私信</li>
<li>评论</li>
<li>@我</li>
</ul>
</li>
<li>
<a href="">微博</a>
<ul>
<li>私信</li>
<li>评论</li>
<li>@我</li>
</ul>
</li>
</ul>
<script>
var box = document.querySelector('.box');
var lis = box.children;
for (var i = 0; i < lis.length; i++) {
lis[i].onmouseover = function() {
this.children[1].style.display = 'block';
}
lis[i].onmouseout = function() {
this.children[1].style.display = 'none';
}
}
</script>
</body>
<body>
<table cellspacing="0">
<thead>
<tr>
<th>姓名</th>
<th>科目</th>
<th>成绩</th>
<th>操作</th>
</tr>
</thead>
<tbody></tbody>
</table>
<script>
var datas = [{
name: '魏璎珞',
subject: 'html',
score: 100
}, {
name: '弘历',
subject: 'javascript',
score: 100
}, {
name: '傅恒',
subject: 'css',
score: 100
}, {
name: '明玉',
subject: 'vue',
score: 100
}, {
name: '大猪蹄子',
subject: 'vue',
score: 100
}];
var tbody = document.querySelector('tbody');
for (var i = 0; i < datas.length; i++) {
//根据对象个数创建行
var tr = document.createElement('tr');
tbody.appendChild(tr);
//根据属性创建列
for (var k in datas[i]) {
var td = document.createElement('td');
td.innerHTML = datas[i][k];
tr.appendChild(td);
}
var operater = document.createElement('td');
operater.innerHTML = '删除';
tr.appendChild(operater);
}
//删除操作
var as = document.querySelectorAll('a');
for (var j = 0; j < as.length; j++) {
as[j].onclick = function() {
tbody.removeChild(this.parentNode.parentNode);
}
}
</script>
</body>
关于dom操作,我们主要针对于元素的操作。主要有创建、增、删、改、查、属性操作、事件操作。
<body>
<ul>
<li>123</li>
<li>123</li>
<li>123</li>
<li>123</li>
</ul>
<a href="http://www.baidu.com">百度</a>
<script>
var ul = document.querySelector('ul');
ul.addEventListener('click', function(e) {
// 1. 谁绑定了该事件,this就指向谁
console.log(this); //ul
// 2. 谁被点击,e.target就指向谁
console.log(e.target); //li
// 3. 返回事件类型 不带on
console.log(e.type); //click
});
// 4. 阻止默认行为:让链接不跳转 或者 让表单不提交
var a = document.querySelector('a');
a.addEventListener('click', function(e) {
e.preventDefault();
});
// 5. 阻止冒泡
var lis = document.querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
lis[i].addEventListener('click', function(e) {
alert('li');
e.stopPropagation();
})
}
</script>
</body>
链接: https://www.bilibili.com/video/BV1hT4y1u7qb?p=256.
//1. 禁止鼠标右键菜单
document.addEventListener('contextmenu', function(e) {
e.preventDefault();
});
//2. 禁止选中文字
document.addEventListener('selectstart', function(e) {
e.preventDefault();
})
document.addEventListener('click', function(e) {
console.log(e.clientX); //距离可视区顶部
console.log(e.clientY);
console.log(e.pageX); //距离顶部
console.log(e.pageY);
console.log(e.screenX);//距离电脑屏幕
console.log(e.screenY);
})
//5秒后自动关闭广告
var ad = document.querySelector('.ad');
setTimeout(function() {
ad.style.display = 'none';
}, 5000);
//停止定时器
var btn = document.querySelector('button');
var timer = setTimeout(function() {
console.log('爆炸了');
}, 5000);
btn.addEventListener('click', function() {
clearTimeout(timer);
})
<body>
<span class="hour">1</span>
<span class="minute">2</span>
<span class="second">3</span>
<script>
var hour = document.querySelector('.hour');
var minute = document.querySelector('.minute');
var second = document.querySelector('.second');
var inputTime = +new Date('2020-8-1 00:00:00'); //用户输入的时间:目标时间
countDown(); // 先调用一次这个函数,防止第一次刷新页面有空白
setInterval(countDown, 1000);
function countDown() {
var nowTime = +new Date(); //当前毫秒数
var times = (inputTime - nowTime) / 1000; //剩余的秒数
var h = parseInt(times / 60 / 60 % 24); //时
h = h < 10 ? '0' + h : h;
hour.innerHTML = h;
var m = parseInt(times / 60 % 60); //分
m = m < 10 ? '0' + m : m;
minute.innerHTML = m;
var s = parseInt(times % 60); //秒
s = s < 10 ? '0' + s : s;
second.innerHTML = s;
}
</script>
</body>
案例:短信发送倒计时
<body>
手机号码:<input type="number"><button>发送</button>
<script>
var btn = document.querySelector('button');
var time = 10; //剩下的秒数
btn.addEventListener('click', function() {
btn.disabled = true; //禁用
var timer = setInterval(function() {
if (time == 0) {
clearInterval(timer);
btn.disabled = false;
btn.innerHTML = '发送';
time = 10; //需要重新赋值
} else {
btn.innerHTML = '还剩下' + time + '秒';
time--;
}
}, 1000);
});
</script>
</body>
案例:5秒钟之后自动跳转页面
<body>
<button>点击</button>
<div></div>
<script>
var btn = document.querySelector('button');
var div = document.querySelector('div');
var time = 5;
// fn();
var timer = setInterval(fn, 1000);
function fn() {
if (time == 0) {
location.href = 'http://www.baidu.com';
// clearInterval(timer);
} else {
div.innerHTML = '您将在' + time + '秒钟之后跳转到首页';
time--;
}
}
</script>
</body>
btn.addEventListener('click', function() {
//1. 记录浏览历史,可以后退
// location.assign('http://www.baidu.com');
//2. 不记录浏览历史,不能后退
// location.replace('http://www.baidu.com');
//3. 重新加载
location.reload(); //默认不填是f5,填写true是强制刷新 ctrl+f5
})
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>offset 属性</title>
<style>
* {
margin: 0;
padding: 0;
}
.father {
/* position: relative; */
width: 200px;
height: 200px;
background-color: pink;
margin: 150px;
}
.son {
width: 100px;
height: 100px;
background-color: purple;
margin-left: 45px;
}
.w {
width: 200px;
height: 200px;
background-color: skyblue;
margin: 0 auto 200px;
padding: 10px;
border: 15px solid red;
}
</style>
</head>
<body>
<div class="father">
<div class="son"></div>
</div>
<div class="w"></div>
<script>
// offset 系列
var father = document.querySelector('.father');
var son = document.querySelector('.son');
// 1.可以得到元素的偏移 位置 返回的不带单位的数值
console.log(father.offsetTop); //150
console.log(father.offsetLeft); //150
// 它以带有定位的父亲为准 如果没有父亲或者父亲没有定位 则以 body 为准
console.log(son.offsetLeft); // 195
var w = document.querySelector('.w');
// 2.可以得到元素的大小 宽度和高度 是包含padding + border + width
console.log(w.offsetWidth); //250
console.log(w.offsetHeight); //250
// 3. 返回带有定位的父亲 否则返回的是body
console.log(son.offsetParent); //body 返回带有定位的父亲 否则返回的是body
console.log(son.parentNode); // 返回父亲 是最近一级的父亲 亲爸爸 不管父亲有没有定位
</script>
</body>
② offset 与 style 区别
③ 案例:仿京东放大镜效果
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>仿京东放大镜效果</title>
<style>
.preview_img {
position: relative;
width: 400px;
height: 400px;
border: 1px solid #ccc;
margin: 100px 100px;
}
.preview_img img {
width: 100%;
}
.mask {
display: none;
position: absolute;
top: 0;
left: 0;
width: 300px;
height: 300px;
background-color: #ffde4f;
/* 半透明 */
opacity: .5;
border: 1px solid #ccc;
cursor: move;
}
.big {
display: none;
position: absolute;
left: 410px;
top: 0;
width: 500px;
height: 500px;
background-color: pink;
border: 1px solid #ccc;
overflow: hidden;
}
.big img {
position: absolute;
top: 0;
left: 0;
width: 800px;
height: 800px;
}
</style>
</head>
<body>
<div class="preview_img">
<img src="s.png" alt="">
<div class="mask"></div>
<div class="big">
<img src="big.jpg" alt="" class='bigimg'>
</div>
</div>
<script>
window.addEventListener('load', function() {
var preview_img = document.querySelector('.preview_img');
var mask = document.querySelector('.mask');
var big = document.querySelector('.big');
preview_img.addEventListener('mousemove', function() {
mask.style.display = 'block';
big.style.display = 'block';
})
preview_img.addEventListener('mouseout', function() {
mask.style.display = 'none';
big.style.display = 'none';
})
preview_img.addEventListener('mousemove', function(e) {
var x = e.pageX - this.offsetLeft; //鼠标在盒子内的位置
var y = e.pageY - this.offsetTop;
var maskX = x - mask.offsetWidth / 2;
var maskY = y - mask.offsetHeight / 2;
var maskMaxx = this.offsetWidth - mask.offsetWidth;
var maskMaxy = this.offsetHeight - mask.offsetHeight;
if (maskX <= 0) {
maskX = 0;
} else if (maskX >= maskMaxx) {
maskX = maskMaxx;
}
if (maskY <= 0) {
maskY = 0;
} else if (maskY >= maskMaxy) {
maskY = maskMaxy;
}
mask.style.left = maskX + 'px';
mask.style.top = maskY + 'px';
//大图片的最大移动距离
var bigimg = document.querySelector('.bigimg');
var bigMaxx = bigimg.offsetWidth - big.offsetWidth;
var bigMaxy = bigimg.offsetHeight - big.offsetHeight;
//大图片移动距离x
var bigX = maskX * bigMaxx / maskMaxx;
//大图片移动距离y
var bigY = maskY * bigMaxy / maskMaxx;
bigimg.style.left = -bigX + 'px';
bigimg.style.top = -bigY + 'px';
})
})
</script>
</body>
2)元素可视区 client 系列
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>client</title>
<style>
div {
width: 200px;
height: 200px;
background-color: pink;
border: 10px solid red;
padding: 10px;
}
</style>
</head>
<body>
<div></div>
<script>
// client 宽度 和我们offsetWidth 最大的区别就是 包含padding、内容的宽度,不包含边框
var div = document.querySelector('div');
console.log(div.clientWidth);//220
console.log(div.clientTop); //10 上边框大小
</script>
</body>
3)元素滚动 scroll 系列
① 元素 scroll 系列属性

② 元素被卷去的头部
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>scroll属性</title>
<style>
div {
width: 200px;
height: 200px;
background-color: pink;
border: 10px solid red;
padding: 10px;
/* 自动显示滚动条 */
overflow: auto;
}
</style>
</head>
<body>
<div>
我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容
</div>
<script>
// scroll 系列
var div = document.querySelector('div');
console.log(div.scrollHeight); //311 实际内容的大小:包含padding
console.log(div.clientHeight); //220 设置的大小:包含padding
// scroll滚动事件当我们滚动条发生变化会触发的事件
div.addEventListener('scroll', function() {
console.log(div.scrollTop); //该元素内容被卷去的顶部部分
})
</script>
</body>
③ 案例分析
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>仿淘宝固定侧边栏</title>
<style>
.slider-bar {
position: absolute;
left: 50%;
top: 300px;
margin-left: 600px;
width: 45px;
height: 130px;
background-color: pink;
}
.w {
width: 1200px;
margin: 10px auto;
}
.header {
height: 150px;
background-color: purple;
}
.banner {
height: 250px;
background-color: skyblue;
}
.main {
height: 1000px;
background-color: yellowgreen;
}
span {
display: none;
position: absolute;
bottom: 0;
}
</style>
</head>
<body>
<div class="slider-bar">
<span class="goBack">返回顶部</span>
</div>
<div class="header w">头部区域</div>
<div class="banner w">banner区域</div>
<div class="main w">主体部分</div>
<script>
var slider_bar = document.querySelector('.slider-bar');
var banner = document.querySelector('.banner');
// console.log(banner.offsetTop);
var bannerTop = banner.offsetTop;
var slider_barTop = slider_bar.offsetTop - bannerTop;
var main = document.querySelector('.main');
var mainTop = main.offsetTop;
var goback = document.querySelector('.goBack');
document.addEventListener('scroll', function() {
console.log(window.pageYOffset); //页面被卷去的头部部分
if (window.pageYOffset >= bannerTop) {
slider_bar.style.position = 'fixed';
slider_bar.style.top = slider_barTop + 'px';
} else {
slider_bar.style.position = 'absolute';
slider_bar.style.top = 300 + 'px';
}
if (window.pageYOffset >= mainTop) {
goback.style.display = 'block';
} else {
goback.style.display = 'none';
}
})
</script>
</body>
④ 页面被卷去的头部兼容性解决方案
4) 三大系列总结
5) 动画函数封装
① 缓动动画原理
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>缓动动画公式:(目标值-现在的位置)/10</title>
<style>
div {
position: absolute;
left: 0;
width: 100px;
height: 100px;
background-color: pink;
}
</style>
</head>
<body>
<button class="btn400">点击走到400</button>
<button class="btn800">点击走到800</button>
<div></div>
<script>
// 简单动画函数封装obj目标对象 target 目标位置
function animate(obj, target, callback) {
// 避免在内存中开辟空间
clearInterval(obj.timer); //只有一个定时器运行
obj.timer = setInterval(() => {
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (obj.offsetLeft == target) {
//停止定时器
clearInterval(obj.timer);
if (callback) {
callback(); //调用函数
}
}
obj.style.left = obj.offsetLeft + step + 'px';
}, 15);
}
var div = document.querySelector('div');
var btn400 = document.querySelector('.btn400');
var btn800 = document.querySelector('.btn800');
btn400.addEventListener('click', function() {
animate(div, 400);
})
btn800.addEventListener('click', function() {
animate(div, 800, function() {
div.style.backgroundColor = 'purple';
});
})
</script>
</body>
② 动画函数封装到单独JS文件里面 animate.js
// 简单动画函数封装obj目标对象 target 目标位置
function animate(obj, target, callback) {
// 避免在内存中开辟空间
clearInterval(obj.timer); //只有一个定时器运行
obj.timer = setInterval(() => {
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (obj.offsetLeft == target) {
//停止定时器
clearInterval(obj.timer);
if (callback) {
callback(); //调用函数
}
}
obj.style.left = obj.offsetLeft + step + 'px';
}, 15);
}
6) 常见网页特效案例
① 网页轮播图
第二步:

第三步:

第五步:

第六步:
② 节流阀
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>轮播图</title>
<link rel="stylesheet" href="./index.css">
<script src="./animate.js"></script>
<script src="./index.js"></script>
</head>
<body>
<!-- 焦点图模块 -->
<div class="focus">
<!-- 左侧按钮 -->
<a href="javascript:;" class="arrow-l"><</a>
<!-- 右侧按钮 -->
<a href="javascript:;" class="arrow-r"> > </a>
<!-- 核心的滚动区域 -->
<ul>
<li>
<a href="#"><img src="../img/focus.png" alt=""></a>
</li>
<li>
<a href="#"><img src="../img/focus1.jpg" alt=""></a>
</li>
<li>
<a href="#"><img src="../img/focus2.jpg" alt=""></a>
</li>
<li>
<a href="#"><img src="../img/focus3.jpg" alt=""></a>
</li>
</ul>
<!-- 小圆圈 -->
<ol class="circle"></ol>
</div>
</body>
</html>
index.css
* {
margin: 0;
padding: 0;
}
li {
list-style: none;
}
a {
text-decoration: none;
}
.focus {
position: relative;
width: 721px;
height: 455px;
margin: 100px auto;
background-color: purple;
overflow: hidden;
}
.focus ul {
/* 加了定位之后才能使用动画函数 */
position: absolute;
top: 0;
left: 0;
width: 600%;
}
.focus ul li {
float: left;
}
.arrow-l,
.arrow-r {
display: none;
position: absolute;
top: 50%;
margin-top: -20px;
width: 24px;
height: 40px;
background: rgba(0, 0, 0, .3);
text-align: center;
line-height: 40px;
color: #fff;
font-family: 'icomoon';
font-size: 18px;
z-index: 2;
}
.arrow-r {
right: 0;
}
.circle {
position: absolute;
bottom: 10px;
left: 50px;
}
.circle li {
float: left;
width: 8px;
height: 8px;
/* background-color: #fff; */
border: 2px solid rgba(255, 255, 255, 0.5);
margin: 0 3px;
border-radius: 50%;
/*鼠标经过显示小手*/
cursor: pointer;
}
.current {
background-color: #fff;
}
animate.js
// 简单动画函数封装obj目标对象 target 目标位置
function animate(obj, target, callback) {
// 避免在内存中开辟空间
clearInterval(obj.timer); //只有一个定时器运行
obj.timer = setInterval(() => {
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (obj.offsetLeft == target) {
//停止定时器
clearInterval(obj.timer);
/* if (callback) {
callback(); //调用函数
} */
//短路运算
callback && callback();
}
obj.style.left = obj.offsetLeft + step + 'px';
}, 15);
}
index.js
window.addEventListener('load', function() {
//1. 获取元素
var focus = document.querySelector('.focus');
var arrow_l = document.querySelector('.arrow-l');
var arrow_r = document.querySelector('.arrow-r');
// 获取图片的宽度
var focusWidth = focus.offsetWidth;
//2. 鼠标经过focus 就显示隐藏左右按钮,并清除自动播放定时器
focus.addEventListener('mouseenter', function() {
arrow_l.style.display = 'block';
arrow_r.style.display = 'block';
clearInterval(timer);
timer = null; //清除定时器变量
})
focus.addEventListener('mouseleave', function() {
arrow_l.style.display = 'none';
arrow_r.style.display = 'none';
//鼠标离开,开启自动播放定时器
timer = setInterval(function() {
//手动调用 右侧按钮点击事件
arrow_r.click();
}, 2000)
})
//3. 动态生成小圆圈
var ol = document.querySelector('.circle');
var ul = focus.querySelector('ul');
for (var i = 0; i < ul.children.length; i++) {
//3.1 创建li,并设置自定义属性:索引,插入到ol
var li = document.createElement('li');
//为小圆圈设置自定义属性:索引
li.setAttribute('data-index', i);
ol.appendChild(li);
//4. 创建li的同时,为它绑定事件
li.addEventListener('click', function() {
for (var j = 0; j < ol.children.length; j++) {
ol.children[j].className = '';
}
this.className = 'current';
//5. 点击小圆圈,移动图片 当然移动的是ul
//获取当前小圆圈的索引
var index = this.getAttribute('data-index');
//当点击 某个小圆圈的时候,就要把当前索引号给num,和circle
num = this.getAttribute('data-index');
circle = this.getAttribute('data-index');
//移动图片
animate(ul, -(index * focusWidth));
})
}
ol.children[0].className = 'current';
//6. 克隆第一张图片,放到ul最后面
var first = ul.children[0].cloneNode(true);
ul.appendChild(first);
//7. 点击右侧按钮,滚动一张照片(无缝滚动)
var num = 0;
//8.1 控制小圆圈的播放
var circle = 0;
//11.节流阀
var flag = true;
//8. 右侧按钮做法
arrow_r.addEventListener('click', function() {
if (flag) {
//11.1 关闭节流阀
flag = false;
// 如果走到了最后复制的一张图片,此时ul要快速复原,left改为0
if (num == ul.children.length - 1) {
ul.style.left = 0;
num = 0;
}
num++;
animate(ul, -(num * focusWidth), function() {
flag = true; //打开节流阀
});
// 8.1 点击右侧按钮,小圆圈跟随一起变化
circle++;
//8.2 如果circle==4说明走到克隆的最后一张图片了,circle复原为0
if (circle == ol.children.length) {
circle = 0;
}
circlrChange();
}
});
//9. 左侧按钮做法
arrow_l.addEventListener('click', function() {
if (flag) {
flag = false;
//如果走到了第一张图片,此时ul要快速定位到最后一张,left改为0
if (num == 0) {
ul.style.left = -(ul.children.length - 1) * focusWidth + 'px';
num = ul.children.length - 1;
}
num--;
animate(ul, -(num * focusWidth), function() {
flag = true;
});
// 8. 点击左侧按钮,小圆圈跟随一起变化
circle--;
//8.2 如果circle<0,说明走到第一张图片了,circle置为最后一个小圆圈的索引
if (circle < 0) {
circle = ol.children.length - 1;
}
circlrChange()
}
});
function circlrChange() {
//清除其余小圆圈的current类名
for (var j = 0; j < ol.children.length; j++) {
ol.children[j].className = '';
}
//留下当前小圆圈的current类名
ol.children[circle].className = 'current';
}
//10. 自动播放轮播图
var timer = setInterval(function() {
//手动调用 右侧按钮点击事件
arrow_r.click();
}, 2000)
})
11. 本地存储
1) 本地存储的概述

2) window.sessionStorage
<body>
<input type="text">
<button class="set">存储数据</button>
<button class="get">获取数据</button>
<button class="remove">删除数据</button>
<button class="del">清空所有数据</button>
<script>
var ipt = document.querySelector('input');
var set = document.querySelector('.set');
var get = document.querySelector('.get');
var remove = document.querySelector('.remove');
var del = document.querySelector('.del');
// 1. 存储数据
set.addEventListener('click', function() {
// 当我们点击了之后,就可以把表单里面的值存储起来
var val = ipt.value;
sessionStorage.setItem('uname', val);
sessionStorage.setItem('pwd', val);
});
// 2. 获取数据
get.addEventListener('click', function() {
// 当我们点击了之后,就可以把表单里面的值获取过来
console.log(sessionStorage.getItem('uname'));
});
// 3. 删除数据
remove.addEventListener('click', function() {
sessionStorage.removeItem('uname');
});
// 4. 清除所有数据
del.addEventListener('click', function() {
// 当我们点击了之后,清除所有的
sessionStorage.clear();
});
</script>
</body>
3) window.localStorage