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}">

<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);