html5
html5新特性
新的选择器
querySelector
选择一个(如果多个匹配返回第一个)
document.querySelector('#div');
document.querySelector('.div');
document.querySelector('div');
querySelectorAll
选择多个
getElementsByClass
JSON
JSON.parse
把json格式的字符串转化为js对象
json格式
{
"key":"value",
"key2": 1,
}
注意: 键值必须加双引号, 如果值是字符串,也必须加双引号
var str = '{"key": "value"}';
var obj = JSON.parse(str);
JSON.stringify
把js对象转化为json格式的字符串
使用JSON.parse和JSON.stringify做深拷贝
function deepCopy(obj){
var str = JSON.stringify(obj);
return JSON.parse(str);
}
var obj = { a: 1 };
var objClone = deepCopy(obj);
objClone.a = 2;
console.log(obj.a);//1
data自定义数据
在html元素上以data-
开头的属性,可以在相应的DOM对象上的dataset属性上得到其属性值
<div data-index="1" data-sub-title="hello"></div>
var oDiv = document.querySelector('div');
console.log( oDiv.dataset.index );//1
console.log( oDiv.dataset.subTitle );//hello sub-title这样的多个-分割的形式,对应的属性为驼峰命名
js加载
defer
延迟加载
<script src="a.js"></script>
<script src="b.js"></script>
<script src="c.js"></script>
<img src="" alt="">
默认js的加载是顺序执行的,先加载js才会加载图片
<script src="a.js" defer="defer"></script>
<script src="b.js"></script>
<script src="c.js"></script>
<img src="" alt="">
如果script标签加上defer属性表示延迟加载(在onload之间加载),也就是说a.js会在图片加载之后加载
没加defer的js依然顺序加载
<script src="a.js" defer="defer"></script>
<script src="b.js" defer="defer"></script>
<script src="c.js" defer="defer"></script>
<img src="" alt="">
等价于
<img src="" alt="">
<script src="a.js"></script>
<script src="b.js"></script>
<script src="c.js"></script>
async
异步加载
<script src="a.js" async="async"></script>
<script src="b.js" async="async"></script>
<script src="c.js" async="async"></script>
<img src="" alt="">
表示几个js文件和图片会同时加载
问题: 不能确定那个js文件先加载完成(适合独立js的加载)
历史管理 history
触发历史管理:
- 跳转页面
- 改变hash
- pushState
onhashchange事件
当hash值发生改变时出发
例子: 彩票选择
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button>随机选择</button>
<div></div>
<script>
var oDiv = document.querySelector('div');
var btn = document.querySelector('button');
var json = {};
btn.onclick = function(){
var hash = Math.random();
var arr = randomArr(35, 7);
json[ hash ] = arr;
oDiv.innerHTML = arr.join(',');
window.location.hash = hash;
}
window.onhashchange = function(){
var hash = window.location.hash.slice(1);
oDiv.innerHTML = json[hash];
}
function randomArr(maxNum , length){
var arr = [];
for(var i=0;i<length;i++){
arr.push( parseInt(Math.random()*maxNum) )
}
return arr;
}
</script>
</body>
</html>
history.pushState 和 onpopstate事件
history.pushState
有三个参数:state对象,标题(现在是被忽略,未作处理),URL(可选)
当history实体被改变时(后退或前进),popstate事件将会发生
如果history实体是有pushState方法产生的,popstate事件的state属性会包含一份来自history实体的state对象的拷贝
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button>随机选择</button>
<div></div>
<script>
var oDiv = document.querySelector('div');
var btn = document.querySelector('button');
var json = {};
btn.onclick = function(){
var arr = randomArr(35, 7);
history.pushState(arr, '');
oDiv.innerHTML = arr.join(',');
}
window.onpopstate = function(ev){
oDiv.innerHTML = ev.state.join(',');
}
function randomArr(maxNum , length){
var arr = [];
for(var i=0;i<length;i++){
arr.push( parseInt(Math.random()*maxNum) )
}
return arr;
}
</script>
</body>
拖放
在html标签上设置draggable="true", 元素久可以拖拽了
<div draggable="true"></div>
但是拖拽有点奇怪, 元素本身不会移动, 感觉是影子在移动, 跟拖拽图片的效果很像
拖拽元素事件
- dragstart 拖拽前触发
- drag 拖拽前、拖拽结束之间,连续触发
- dragend 拖拽结束触发
<style>
.drag{
width: 100px;
height: 100px;
background: red;
}
</style>
<div class="drag" draggable="true"></div>
<script>
var oDrag = document.querySelector('.drag');
oDrag.ondragstart = function(){
this.style.background = 'green';
}
oDrag.ondragend = function(){
this.style.background = 'red';
}
var i=0;
oDrag.ondrag = function(){
document.title = i++;
}
</script>
目标元素事件
- dragenter , 进入目标元素触发
- dragover ,进入目标、离开目标之间,连续触发
- dragleave , 离开目标元素触发
- drop , 在目标元素上释放鼠标触发(需要在dragover事件中阻止默认行为才能生效)
<style>
div{
width: 100px;
height: 100px;
}
.drag{
background: red;
}
.target{
background: green;
}
</style>
<div class="drag" draggable="true"></div>
<div class="target"></div>
<script>
var oTarget = document.querySelector('.target');
oTarget.ondragenter = function(){
this.style.background = '#111';
}
var i=0;
oTarget.ondragover = function(ev){
ev.preventDefault();
document.title = i++;
}
oTarget.ondragleave = function(){
this.style.background = 'green';
}
oTarget.ondrop = function(){
alert(1)
}
</script>
事件的执行顺序
drop不触发的时候:
dragstart > drag > dragenter > dragover > dragleave > dragend
drop触发的时候(dragover的时候阻止默认事件):
dragstart > drag > dragenter > dragover > drop > dragend
火狐下拖拽的问题
只设置draggable属性,在火狐下还是不能拖拽
还必须设置在dragstart事件中,设置dataTransfer对象上的setData方法才能拖拽除图片以外的其他元素
<div class="drag" draggable="true"></div>
<script>
var oDrag = document.querySelector('.drag');
oDrag.ondragstart = function(ev){
ev.dataTransfer.setData('key', 'value');
}
</script>
dataTransfer
在拖拽事件中,是事件对象上的一个属性
有两个方法:
- setData(key, value) key,value必须都是字符串
- getData(key)
var oDrag = document.querySelector('.drag');
oDrag.ondragstart = function(ev){
ev.dataTransfer.setData('a', 'b');
alert(ev.dataTransfer.getData('a'));//b
}
实例: 将li拖拽到div, 在ondrop是删除对应的li
<style>
div{
width: 100px;
height: 100px;
background: red;
}
.drag{
background: red;
}
.target{
background: green;
}
li{
display: inline-block;
background: yellow;
}
</style>
<ul>
<li draggable="true">aa</li>
<li draggable="true">bb</li>
<li draggable="true">cc</li>
</ul>
<div>
</div>
<script>
var aLi = document.querySelectorAll('li');
var oDiv = document.querySelector('div');
aLi.forEach(function(elem, i){
elem.i = i;
elem.ondragstart = function(ev){
ev.dataTransfer.setData('index', this.i);
}
});
oDiv.ondragover = function(ev){
ev.preventDefault();
}
oDiv.ondrop = function(ev){
var i = ev.dataTransfer.getData('index');
aLi[i].remove();
}
</script>
dataTransfer.effectAllowed
移入目标元素时,鼠标的样式
取值: none, copy, copyLink, copyMove,link,linkMove,move, all,uninitialized
oDrag.ondragstart = function(ev){
ev.dataTransfer.effectAllowed = 'link';
}
dataTransfer.setDragImage()
三个参数: 指定的元素, 坐标x, 坐标y
oDrag.ondragstart = function(ev){
ev.dataTransfer.setDragImage(oDiv, 0, 0);
}
dataTransfer.files
获取外部拖拽的文件, 返回一个filesList列表
filesList下每个文件对象有个type属性, 返回文件的类型
关于外部文件的拖拽, 这样我们就不需要操作拖拽元素了, 只需要操作目标元素
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
div{
width: 200px;
height: 200px;
background: red;
}
</style>
</head>
<body>
<div>将文件拖放到此区域</div>
<script>
var oDiv = document.querySelector('div');
oDiv.ondragenter = function(ev){
this.innerHTML = '可以释放了';
console.log(ev.dataTransfer.files[0].type);
}
oDiv.ondragover = function(ev){
ev.preventDefault();
};
oDiv.ondragleave = function(){
this.innerHTML = '将文件拖放到此区域';
}
oDiv.ondrop = function(ev){
ev.preventDefault();//阻止浏览器的默认行为, 默认拖放文件到浏览器,会将文件在浏览器中打开
};
</script>
</body>
</html>
FileReader
使用FileReader对象,web应用程序可以异步的读取存储在用户计算机上的文件(或者原始数据缓冲)内容
- fd.readAsDataURL(file);
以data: URL格式
的字符串以表示所读取文件的内容
参数为file对象,在拖拽中可以是ev.dataTransfer.files[0];
- fd.onload
读取文件成功的回调函数,
成功获fd对象的result属性代表了获取的文件数据, 如果是图片,则返回base64格式的图片数据
oDiv.ondrop = function(ev){
ev.preventDefault();
var fd = new FileReader();
fd.readAsDataURL(ev.dataTransfer.files[0]);
fd.onload=function(){
console.log(this.result);
}
};
或者在<input>
中
<input id="fileItem" type="file">
<script>
var fileItem = document.getElementById('fileItem');
fileItem.onchange = function(){
var file = this.files[0];
var fd = new FileReader();
fd.readAsDataURL(file);
fd.onload=function(){
console.log(this.result);
}
}
</script>
实例: 图片预览
<div>将文件拖放到此区域</div>
<ul></ul>
<script>
var oDiv = document.querySelector('div');
var oUl = document.querySelector('ul');
oDiv.ondragenter = function(ev){
this.innerHTML = '可以释放了';
}
oDiv.ondragover = function(ev){
ev.preventDefault();
};
oDiv.ondragleave = function(){
this.innerHTML = '将文件拖放到此区域';
}
oDiv.ondrop = function(ev){
ev.preventDefault();
var files = ev.dataTransfer.files;
files = [].slice.call(files);
files.forEach(function(file, i){
if( file.type.indexOf('image')!=-1){
var li = document.createElement('li');
var img = document.createElement('img');
li.appendChild(img);
var fd = new FileReader();
fd.readAsDataURL(file);
fd.onload = function(){
img.src= this.result;
oUl.appendChild(li);
}
}
else{
alert('请选择图片格式');
}
});
};
</script>
实例: 拖拽购物车
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
#cart{
border: 1px solid #111;
min-height: 500px;
min-width: 300px;
}
</style>
</head>
<body>
<ul id="product-list">
</ul>
<ul id="cart"></ul>
<script>
var products = [
{
img: 'http://g-search2.alicdn.com/img/bao/uploaded/i4/i2/TB1wSUANpXXXXXeXXXXXXXXXXXX_!!0-item_pic.jpg_80x80.jpg',
title: 'i7 6850K/华硕X99/GTX1080 VR游戏',
price: 10000
},
{
img: 'http://g-search3.alicdn.com/img/bao/uploaded/i4/i2/TB1UdPeOFXXXXa_XFXXXXXXXXXX_!!0-item_pic.jpg_80x80.jpg',
title: '松明微星宙斯盾Aegis B903-008CN i7/8G*2/GTX1070台式机',
price: 9999
},
{
img: 'http://g-search1.alicdn.com/img/bao/uploaded/i4/i1/TB1UOY_KpXXXXciXVXXXXXXXXXX_!!2-item_pic.png_80x80.jpg',
title: '海盗船 梦幻电脑250D 6700K/1151 多屏炒股HTPC办公台式电脑主机',
price: 8888
}
]
var cartData = {};
var str = '';
products.forEach(function(product, i){
str += `
<li draggable="true" data-index="${i}">
![](${product.img})
<p>${product.title}</p>
<p>¥${product.price}</p>
</li>
`
});
var oProduct = document.querySelector('#product-list');
var oCart = document.querySelector('#cart');
oProduct.innerHTML = str;
var aProductItem = document.querySelectorAll('#product-list li');
aProductItem.forEach(function(product, i){
product.ondragstart = function(ev){
ev.dataTransfer.setDragImage(this,0, 0);
console.log(this.dataset.index);
ev.dataTransfer.setData('index', this.dataset.index);
}
});
oCart.ondragover = function(ev){
ev.preventDefault();
}
oCart.ondrop = function(ev){
ev.preventDefault();
var index = ev.dataTransfer.getData('index');
var product = products[index];
var cartItem = null;
if( !cartData[index] ){
cartItem = {
title: product.title,
price: product.price,
count: 1,
li: document.createElement('li')
}
cartData[index] = cartItem;
oCart.appendChild(cartItem.li);
}
else{
cartItem = cartData[index];
cartItem.count++;
}
cartItem.li.innerHTML = `
<span class="count">${cartItem.count}</span>
<span class="title">${cartItem.title}</span>
<span class="total">¥${cartItem.price*cartItem.count}</span>
`;
}
</script>
</body>
</html>
canvas
基本使用
<!--不要使用css给canvas设置宽高-->
<canvas id="canvas" width="500" height="300">
你的浏览器不支持canvas
</canvas>
绘图环境
var canvas = document.querySelector('#canvas');
var context = canvas.getContext('2d');
目前只支持2d,不支持3d,如果要使用3d, 可以使用webgl(不过兼容性也不是很好)
绘制方块
context.fillRect
context.fillRect(left,top, width, height)
context.fillRect(50,50, 100, 200);
默认颜色是黑色
context.strokRect
strokeRect(left, top, width, height)
context.strokeRect(50,50, 100, 200);
默认是1像素黑色边框
但是显示出在ps中测量为2像素的边框,把画布看成一个坐标轴,正方形的顶点坐标为(50px, 50px)
正方形的边框为1像素宽,我们以左边框为例, 它是以50px为中心点,向右延伸0.5像素(49.5px),
向左延伸0.5像素(50.5px); 所以做边框为(49.5px~50.5px)
但是,用canvas绘图跟我们的ps一样, 最小的但是就是1px,没有0.5px, 所以就将出现了2px的边框
我们可以这样写
context.strokeRect(50.5,50.5, 100, 200);
这样边框的中心点在1px的一半,刚刚就可以是1px
设置绘图
fillStyle 填充颜色
strokeStyle 边框颜色
lineWidth 线宽
context.fillStyle = 'red';
context.strokeStyle = 'blue';
context.lineWidth = 10;
//注意: 顺序不同,效果不同
context.fillRect(50,50,100,50);
context.strokeRect(50,50,100,50);
边界绘制
lineJoin 边界连接点样式
miter(默认)(斜接) round(圆角) bevel(斜角)
lineCap 端点样式
butt(默认) round(圆角) square()
绘制路径
beginPath
closePath
moveTo
lineTo
stroke
fill
rect
clearRect
save
restore
实例: 鼠标画线
var canvas = document.querySelector('#canvas');
var context = canvas.getContext('2d');
canvas.onmousedown = function(ev){
context.moveTo(ev.clientX - canvas.offsetLeft, ev.clientY-canvas.offsetTop)
canvas.onmousemove = function(ev){
context.lineTo(ev.clientX - canvas.offsetLeft, ev.clientY-canvas.offsetTop);
context.stroke();
}
canvas.onmouseup = function(){
this.onmousemove = null;
this.onmouseup = null;
}
}
实例: 方块移动
var canvas = document.querySelector('#canvas');
var context = canvas.getContext('2d');
var iNow = 0;
setInterval(function(){
iNow++;
context.clearRect(0, 0, 500, 300);
context.fillRect(iNow, iNow, 100, 100);
}, 50);
绘制圆
arx(x, y, 半径, 起始弧度, 终止弧度, 旋转方向)
- 弧度与角度的关系: 弧度=角度*Math.PI/180
- 旋转方向: 顺时针false(默认), 逆时针true
实例: 时钟
var canvas = document.querySelector('#canvas');
var context = canvas.getContext('2d');
var r = 100;
var x = 150;
var y = 150;
function drawClock(){
context.clearRect(0, 0, 500, 300);
var now = new Date();
var hour = now.getHours();
var min = now.getMinutes();
var sec = now.getSeconds();
console.log(hour, min, sec);
var hourAngle = hour*30 + min*(30/60) - 90;
var minAngle = min*6+sec*(6/60) - 90;
var secAngle = sec*6 -90;
//分钟刻度
for(var i=0;i<60;i++){
context.beginPath();
context.moveTo(x,y);
context.arc(x,y,r, 6*i*Math.PI/180, 6*(i+1)*Math.PI/180);
context.stroke();
context.closePath();
}
context.beginPath();
context.fillStyle ='white';
context.arc(x,y,r*(19/20), 0, 360*Math.PI/180);
context.fill();
context.closePath();
// 时钟刻度
for(var i=0;i<12;i++){
context.beginPath();
context.lineWidth = 3;
context.moveTo(x,y);
context.arc(x,y,r, 30*i*Math.PI/180, 30*(i+1)*Math.PI/180);
context.stroke();
context.closePath();
}
context.beginPath();
context.fillStyle ='white';
context.arc(x,y,r*(17/20), 0, 360*Math.PI/180);
context.fill();
context.closePath();
// 时钟指针
context.beginPath();
context.moveTo(x,y);
context.arc(x,y,r*(12/20), hourAngle*Math.PI/180, hourAngle*Math.PI/180);
context.stroke();
context.closePath();
// 分钟刻度
context.beginPath();
context.moveTo(x,y);
context.arc(x,y,r*(14/20), minAngle*Math.PI/180, minAngle*Math.PI/180);
context.lineWidth = 2;
context.stroke();
context.closePath();
// 秒钟刻度
context.beginPath();
context.moveTo(x,y);
context.arc(x,y,r*(17/20), secAngle*Math.PI/180, secAngle*Math.PI/180);
context.lineWidth = 1;
context.stroke();
context.closePath();
}
drawClock();
setInterval(function(){
drawClock();
}, 1000);