歷史狀態管理是現在web應用開發的一個難點,用戶每一次操作不一定會打開一個全新的頁面,因此后退和前進按鈕也就失去了作用,解決這個問題首選hashchange事件,通過該事件可以知道URL參數什么時候發生了變化用法如下:
window.onhashchange=function(){
alert(1)
}
而通過狀態管理API,能夠在不加載新頁面的情況下改變瀏覽器的URL,為此需要使用history.pushState()方法,3個參數狀態對象,新狀態標題,和可選的相對URL。
history.pushState({name:"test"},"test","a.html");
注意只是pushState是不能改變網頁標題的哦。
執行該方法后,新的狀態信息就會被加入歷史狀態棧,但是瀏覽器并不會真的向服務器發送請求,第二個參數目前沒有瀏覽器實現,因此也可以傳入空字符串。
因為pushState()會創建新的歷史狀態,所以按下后退按鈕會觸發window對象的popstate事件。popstate事件對象有一個state屬性,這個屬性就包含第一個參數pushState()狀態對象
window.onpopstate=function(e){
console.log(e.state)
}
注意瀏覽器加載的第一個頁面沒有狀態,因此單擊后退按鈕返回瀏覽器加載的第一個頁面時,之為null
要更新當前狀態,可以調用replaceState();傳入的參數與pushState()的前兩個參數相同。調用這個方法不會再歷史狀態棧中創建新狀態。只會重寫當前狀態。
history.replaceState({name:"re'd"},"red");
內容是《Web前端開發之Javascript視頻》的課件,請配合大師哥《Javascript》視頻課程學習。
事件有很多類型,不同的事件類型具有不同的信息;
DOM2級事件規定了下列5種事件:
DOM3級在DOM2的基礎上重新定義了這些事件,并增加了一些事件:
HTML5規范也定義了大量的事件:歷史管理、拖放、跨文本通信,以及多媒體等事件;
除了這些事件,有些瀏覽器會在DOM和BOM中也實現其他專有事件;
從另外一個角度來分,事件大致可以分成幾大類:
依賴于設備的輸入型事件:這些事件依賴于特定的輸入設備,比如鼠標和鍵盤,也包括觸摸事件;例如,mousedown、mousemove之類的,只能通過鼠標設備,而keydown或keypress只能通過鍵盤設備,touchmove或gesturechange也只能通過觸摸設備實現;
獨立于設備的輸入型事件:不依賴于特定的輸入設備,比如click事件,既可以通過鼠標單擊實現,也可以通過鍵盤或觸摸設備實現;再如textinput事件,即可以通過鍵盤按鍵實現,也可以在剪切和粘貼實現,甚至通過手寫設備來實現;
用戶界面事件:通常指的是一些注冊在表單控件上的事件,例如文本框獲取焦點的focus事件,控件改變值的change事件或提交表單的submit事件;
狀態變化事件:表示某些生命周期或相關狀態的變化的事件,比如,文檔加載的load事件或DOMContentLoaded事件或文檔狀態readystatechange事件;再如HTML5的歷史管理的popstate事件,online或offline事件;
特定API事件:HTML5及相關的規范中定義的事件,例如:拖放事件,多媒體的相關事件;
UIEvent事件:
UIEvent事件表示簡單的用戶界面事件,但包含一些不一定與用戶操作有關的事件,主要與元素的焦點有關;
該事件類繼承自Event類;由其也派生出其他子類,如MouseEvent、TouchEvent、FocusEvent、KeyboradEvent、WheelEvent、InputEvent和CompositionEvent;
其自身定義了一些屬性,如:detail、layerX、layerY、pageX、pageY、sourceCapabilities、view和which;
UIEvent相關的事件:
以上這些事件,在DOM2級事件中,除了DOMActive之外,其他事件都屬于HTML事件,所以,在確定瀏覽器是否支持DOM2級事件,最后檢測一下,如:
var isSupported = document.implementation.hasFeature("HTMLEvents","2.0");
確定瀏覽器是否支持DOM3級事件,如:
var isSupported = document.implementation.hasFeature("UIEvent","3.0");
另外,這些事件,多數與window對象或表單控件相關,所以有些地方把這些事件也稱為window事件,就是因為這些事件的發生多數與瀏覽器窗口有關;
load加載事件:當頁面完全加載后(包括所有圖像、Javascript文件和CSS文件等外部資源),就會觸發window對象的load事件,這也是Javascript中最常使用的事件;
有兩種定義onload事件處理程序的方式:
window.addEventListener("load", function(event){
console.log("Loaded");
console.log(event); // Event
},false);
// 或在IE10以下
window.attachEvent("onload", function(event){
console.log("Loaded");
console.log(window.event);
});
為<body>元素添加onload特性;
<body onload="console.log(event);">
一般來說,在window上發生的任何事件都可以在<body>元素中通過相應的特性來指定,因為在HTML中無法訪問window元素;
圖像也可以觸發load事件,無論是在DOM中的圖像還是在HTML中的圖像元素;
<img src="images/1.jpg" onload="alert('圖片已加載')" />
或:
var img = document.querySelector("img");
img.addEventListener("load", function(event){
console.log(event.target.src);
});
// 或
EventUtil.addHandler(img, "load", function(event){
event = EventUtil.getEvent(event);
console.log(EventUtil.getTarget(event).src);
});
在創建新的<img>元素時,可以為其指定一個事件處理程序,只需要指定src屬性就可以下載,并不需要添加到DOM樹中,如:
window.addEventListener("load", function(event){
var img = document.createElement("img");
img.addEventListener("load", function(e){
console.log(e.target.src);
});
img.src = "images/1.jpg";
document.body.appendChild(img);
});
// 或者
EventUtil.addHandler(window, "load", function(){
var img = document.createElement("img");
EventUtil.addHandler(img, "load", function(event){
event = EventUtil.getEvent(event);
console.log(EventUtil.getTarget(event).src);
});
img.src = "images/1.jpg";
document.body.appendChild(img);
});
還可以使用DOM0級的Image對象實現,如:
// 把
var img = document.createElement("img");
// 換成
var img = new Image();
還有一些元素也以非標準的方式支持load事件;在標準瀏覽器中,<script>元素也會觸發load事件,以確定動態加載的js文件是否加載完畢,在設置了<script>的scr屬性并且添加到文檔中,才會開始下載;
window.addEventListener("load", function(){
var script = document.createElement("script");
script.addEventListener("load", function(event){
console.log(event.target.src);
});
script.src = "scripts/main.js";
document.getElementsByTagName("head")[0].appendChild(script);
});
// 或
EventUtil.addHandler(window, "load", function(){
var script = document.createElement("script");
EventUtil.addHandler(script, "load", function(event){
console.log(event);
var target = EventUtil.getTarget(event);
console.log(target.src);
});
script.src = "scripts/main.js";
document.getElementsByTagName("head")[0].appendChild(script);
});
此時,event對象的target及currentTarget或srcElement引用都是<script>節點;
IE8及以下瀏覽器不支持<script>元素上的load事件,但以上代碼不會拋出異常;
瀏覽器還支持<link>元素上的load事件,如:
window.addEventListener("load", function(){
var link = document.createElement("link");
link.rel = "stylesheet";
link.addEventListener("load", function(event){
console.log(event.target.href);
});
link.href = "styles/css.css";
document.getElementsByTagName("head")[0].appendChild(link);
});
與<script>類似,指定其href屬性并且添加到文檔中才會下載;
unload事件:在頁面被完全卸載后觸發;或者從一個頁面切換到另一個頁面,或者刷新操作也會觸發unload事件,如:
<body onunload="alert('unload');">
拋出異常:blocked alert during unload,也就是頁面被阻塞了,而在unload事件處理中,是不允許阻塞的,諸如彈窗之類的,都會引起阻塞;正確的做法:
<body onunload="console.log('unload');">
或:
EventUtil.addHandler(window, "unload", function(event){
console.log("unload");
});
unload事件執行時間很短,并且是在一切都被卸載之后才觸發,所以不適合處理常規代碼,一般是取消(或清除)該頁面上的對象引用,以避免內存泄漏;
var obj = {name:"wangwei",age:18};
window.addEventListener("unload", function(event){
var oDiv = document.getElementById("mydiv");
console.log(oDiv);
var img = document.getElementsByTagName("img")[0];
console.log(img);
console.log(obj);
obj = null;
console.log(obj);
debugger;
});
var obj = {name:"wangwei",age:18};
window.addEventListener("unload", function(event){
localStorage.setItem("unload","頁面被unload了");
localStorage.setItem("person", obj);
});
示例:統計一個頁面停留的時長,如:
window.addEventListener("load", function(){
var start = new Date();
console.log(start);
window.addEventListener("unload", function(){
var end = new Date();
var duration = (end.getTime() - start.getTime()) / 1000;
var pageView = {
pageStayTime: duration,
pageUrl: location.href
};
console.log("保存到數據庫中");
});
});
另外,unload事件是不冒泡的,也是不可取消的;
同樣,DOM2級事件規定應該在<body>元素而非window對象上觸發unload事件,但所有瀏覽器都在window上實現了unload事件;
error事件:window.onerror屬性看起來像事件處理程序,并且當Javascript出錯時就會觸發它,但是,它并不是真正的事件處理程序,因為它的參數不是event對象,而是5個參數,由這5個參數可以獲取詳細的錯誤信息;
window.onerror = function(sMessage, sUrl, sLine, sColumn, error){
console.log("Error:" + sMessage + " URL:" + sUrl + " Line:" + sLine + " Column:" + sColumn);
console.log(error);
return true;
}
但如果使用DOM2級事件處理程序,其中的參數就是event對象;
window.onload = function(){
num1 + num2;
}
window.addEventListener("error", function(event){
console.log(event); // ErrorEvent
return true;
});
ErrorEvent類繼承自Event類,其定義了如下屬性:
這5個屬性也對應著window.onerror屬性的5個參數;
如果是圖片的onerror事件,就是一個真正的事件,其中只有一個參數,就是一個event對象;
var img = document.getElementsByTagName("img")[0];
img.onerror = function(event){
console.log(event); // Event type為error
}
abort事件:當一個資源的加載已中止時,將觸發該事件;
var video = document.querySelector("video");
videoSrc = "https://www.zeronetwork.cn/video.webm";
video.addEventListener("abort", function(event){
console.log(event);
console.log("下載中止:" + videoSrc);
});
var source = document.createElement("source");
source.setAttribute("src", videoSrc);
source.setAttribute("type", "video/webm");
video.appendChild(source);
source.addEventListener("error", function(event){
console.log(event);
return true;
});
resize事件:當瀏覽器窗口的大小被調整,就觸發resize事件,該事件在window上觸發;
EventUtil.addHandler(window, "resize", function(event){
console.log(event);
});
在標準瀏覽器中,event對象有target屬性,且值為window對象,但IE未提供任何屬性;
瀏覽器的大小改變1像素就會觸發resize事件,然后再隨著變化不斷重復觸發;
最大化或最小化窗口時,也會觸發resize事件;但是部分瀏覽器會在最大化或最小化時觸發兩次或以上的該事件,此時可以使用setTimeout()解決,也就是延遲執行某些代碼,如:
function callBack(){
console.log("callBack");
}
window.addEventListener("resize", function(event){
var target = this;
if(target.resizeFlag)
clearTimeout(target.resizeFlag);
target.resizeFlag = setTimeout(function(){
callBack();
console.log("resize");
target.resizeFlag = null;
},200);
});
示例:隨窗口大小的變化而變化,如:
var mydiv = document.getElementById("mydiv");
var w = mydiv.clientWidth,
h = mydiv.clientHeight,
dx = w / h,
dw = document.documentElement.clientWidth,
scale = w / dw;
window.addEventListener("resize", function(event){
var dw = document.documentElement.clientWidth;
mydiv.style.width = dw * scale + "px";
var w = mydiv.clientWidth;
mydiv.style.height = w * dx * scale + "px";
console.log(mydiv.style.width);
console.log(mydiv.style.height);
});
示例:
<style>
*{margin:0;padding:0;}
.leftDiv,.rightDiv{
width: 40%; overflow-y: scroll; float: left; margin-right: 10px; background-color: purple;
}
</style>
<div class="leftDiv">leftDiv ...</div>
<div class="rightDiv">rightDiv ...</div>
<script>
var leftDiv, rightDiv, dHeight;
window.addEventListener("load", function(){
leftDiv = document.querySelector(".leftDiv");
rightDiv = document.querySelector(".rightDiv");
dHeight = document.documentElement.clientHeight;
leftDiv.style.height = dHeight + "px";
rightDiv.style.height = dHeight + "px";
});
window.addEventListener("resize", function(event){
dHeight = document.documentElement.clientHeight;
leftDiv.style.height = dHeight + "px";
rightDiv.style.height = dHeight + "px";
});
</script>
resize事件目前只能注冊在window對象上,它不支持注冊在DOM元素上,如果要監聽DOM元素的resize事件,最好的方案就是使用自定義事件;
scroll事件:雖然是在window對象上發生,但它實際表示的是頁面中相應元素的變化;即可以在滾動窗口或其他元素時,跟蹤變化來確保某些內容一直在屏幕上可見;
window.addEventListener("scroll", function(event){
console.log(event); // Event
});
該事件對象并沒有提供滾動相關的信息,只是一個普通的Event的對象;
在混雜模式下,可以通過<body>元素的scrollLeft和scrollTop來監控到這一變化;在標準模式下,會通過<html>元素來反映這一變化;
window.onscroll = function(){
console.log("x:" + document.documentElement.scrollLeft + ", y:" + document.documentElement.scrollTop);
}
// 或
window.addEventListener("scroll", function(event){
if(document.compatMode == "CSS1Compat")
console.log(document.documentElement.scrollTop);
else
console.log(document.body.scrollTop);
});
與resize事件類似,scroll事件會在文檔被滾動活動期間會被重復觸發,因此在scroll事件處理程序中盡量保持簡單的代碼;
scroll事件示例:
<div id="mydiv" style="width:200px;height:200px;background-color: purple; position: absolute;top:50px;right:50px;"></div>
<div style="height: 2000px; background-color:lightgreen;"></div>
<script>
window.onscroll = function(){
var oDiv = document.getElementById("mydiv");
oDiv.style.top = document.documentElement.scrollTop + "px";
}
</script>
scroll事件也可以注冊到Element元素上;如:
var oDiv = document.getElementById("mydiv");
console.log(oDiv.scrollHeight);
oDiv.addEventListener("scroll", function(event){
// console.log(event); // Event
console.log(event.target.scrollTop);
});
事件防抖(debounce)和節流(throttle):
防止事件被頻繁觸發;相關的事件有:mousemove、keydown、keypress、keyup、resize、scroll等;
防抖:觸發高頻事件后n秒內函數只執行一次,如果n秒內高頻事件再次觸發,則重新計算時間;如:
var timer = null; // 創建一個標記用來存放定時器的返回值
window.addEventListener("resize", function(){
if(timer){
clearTimeout(timer); // 每當resize時把之前的一個setTimeout清除
timer = null;
}
if(!timer){
timer = setTimeout(function(){ // 再創建一個新的定時器
console.log("防抖");
},500);
}
});
在事件防抖中,清除定時器的時機很關鍵,必須在新定時器生成之前,如果在之后,會將所有定時器都清除,目標函數一次都不執行;或:
function debounce(callback, delay){
var timeout = 0;
return function(){
var arg = arguments;
// 一直觸發,就一直清除
clearTimeout(timeout);
// 直到事件不再觸發,最后一個定時器沒有清除,delay后就會執行定時器,
// 如此,就確保只執行一次
timeout = setTimeout(function(){
callback.apply(this, arg);
}, delay);
};
}
function fn(e){
console.log("fn" + e);
}
window.addEventListener("resize", debounce(fn, 500));
示例:輸入框驗證
function checkEmail(callback,delay){
var t = null;
return function(){
var arg = arguments;
clearTimeout(t);
t = setTimeout(function(){
callback.apply(this, arg);
}, delay);
}
}
function emailHander(e){
var reg = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
if(!reg.test(e.target.value)){
e.target.style.backgroundColor = "red";
setTimeout(function(){
e.target.style.backgroundColor = "";
},800);
console.log("格式不正確");
}else{
e.target.style.backgroundColor = "green";
console.log("正確");
}
}
var email = document.getElementById("email");
email.addEventListener("keyup", checkEmail(emailHander,800), false);
節流:防抖是多次觸發事件,目標函數只執行一次,不管觸發這些事件用了多少時間;而節流是在一定時間內只會執行一次,稀釋函數的執行頻率,進而達到緩慢執行目標函數的目的;如:
使用setTimeout():
var timeoutId;
window.addEventListener("resize", function(event){
if(!timeoutId){
timeoutId = setTimeout(function(){
console.log("延遲執行"); // 只執行一次
clearTimeout(timeoutId);
// timeoutId = null; // 換成這一句,500ms執行一次
},500);
}
});
// 或
function resizeHandler(event){
// 先解除綁定
window.removeEventListener("resize", resizeHandler);
console.log(document.documentElement.clientWidth);
// 重新綁定
setTimeout(function(){
window.addEventListener("resize", resizeHandler);
},500);
}
window.addEventListener("resize", resizeHandler);
使用setInterval():
將事件處理程序放在setInterval()函數中,每隔一段時間,去監測一次是否發生了scroll事件,然后執行,而scroll事件只是改變scrolled的值,不會影響性能,如:
var scrolled = false;
window.addEventListener("scroll", function(){
scrolled = true;
});
setInterval(function(){
if(scrolled){
// 代碼處理
console.log("scroll");
scrolled = false;
}
},500);
使用時間戳:
var startTime = new Date();
var flag = false;
var delay = 200;
window.addEventListener("resize", function(){
startTime = new Date();
if(flag === false){
flag = true;
setTimeout(resizeEnd, delay);
}
});
function resizeEnd(){
if(new Date() - startTime <= delay)
setTimeout(resizeEnd, delay);
else{
flag = false;
console.log(document.documentElement.clientWidth);
}
}
或:
function thorttle(callback, delay){
var timeout = 0;
var now = new Date() - 0;
return function(){
var arg = arguments;
var last = new Date() - 0;
clearTimeout(timeout);
if(last - now >= delay){
now = last; // 將上次執行的時間賦值給now
}else{
// 目標函數在這里執行
timeout = setTimeout(function(){
callback.apply(this, arg);
}, delay);
}
};
}
function fn(e){
console.log("節流" + e);
}
window.addEventListener("resize", thorttle(fn, 500));
使用開關:
設置一個開關,一次只能有一個觸發執行,并對執行設置計時一段時間再執行,執行完畢之后再解鎖;如:滾動事件;
function loadMore(){
var canRun = true;
return function(){
if(!canRun) return;
canRun = false;
setTimeout(function(){
var docHeight = document.documentElement.offsetHeight; // 文檔高度
var winHeight = window.innerHeight; // 窗口高度
var scrollDistance = document.documentElement.scrollTop; // 滾動距離
// 當滾動到底部時
if(docHeight - (winHeight + scrollDistance) <= 100){
console.log("Loading...");
}
canRun = true;
},600);
}
}
window.addEventListener("scroll", loadMore());
兩者比較:
節流在某個時間段內,目標函數能執行一次,限制目標函數的執行頻率,不管事件觸發了多少次;
防抖是多次觸發事件,目標函數只執行一次,不管觸發了這些事件用了多少時間;
節流函數限制目標函數的執行頻率,有連續變化的效果,適用于關注變化過程的操作,可以調整目標函數執行頻率使得變化更加平滑,比如動畫、改變窗口時執行某些操作等,常用事件resize、scroll、mouseWheel、touchmove、mouseover等;
防抖函數適用于更關注結果的操作,不太關注操作過程,常見的事件有 input、keyup等;
FocusEvent焦點事件:
焦點事件會在頁面元素獲得或失去焦點時觸發,或某些對象調用了focus()和blur()方法也會觸發該事件;利用這些事件并與document.hasFocus()方法及document.activeElement屬性配合,可以知曉用戶在頁面上的行蹤;
有以下6個焦點事件:
要確定瀏覽器是否支持這些事件,可以檢測:
var isSupported = document.implementation.hasFeature("FocusEvent", "3.0");
可以獲得焦點事件的元素一般指的是window或表單控件或是超鏈接或是可編輯元素,稱為focusable元素;但在IE中普通元素也可以獲得焦點;
window.onblur = function(){ document.title = "你離開了";};
window.onfocus = function(){ document.title = "你來了";};
var oDiv = document.getElementById("mydiv");
oDiv.contentEditable = true;
// oDiv.tabIndex = 1; // 或者添加tabIndex屬性也可以
oDiv.focus();
console.log(document.activeElement);
oDiv.addEventListener("focus", function(event){
console.log(event.target.id);
});
這一類事件中最主要的兩個是focus和blur,它們都是Javascript早期就得到所有瀏覽器都支持的事件,這兩個事件的最大問題是它們不冒泡,因此,才出現了IE的focusin和focusout與Opera的DOMFocusIn和DOMFocusOut這兩對事件,后來IE的方式被DOM3級事件納為標準方式;
var btn = document.getElementById("btn");
btn.addEventListener("focus", function(event){
console.log(event); // FocusEvent
});
FocusEvent類:
表示和焦點相關的事件類,其繼承自UIEvent類;
其自身只添加了一個屬性relatedTarget,代表此次事件的相關目標,但在實用中,例如切換瀏覽器tab標簽時,為了安全起見,所有瀏覽器都會返回null;
當焦點從頁面中的一個元素移動到另一個元素,會依次觸發下列事件:
blur在失去焦點的元素上觸發、focusout在失去焦點的元素上觸發、DOMFocusOut在失去焦點的元素上觸發、focus在獲得焦點的元素上觸發、focusin在獲得焦點的元素上觸發、DOMFocusIn在獲得焦點的元素上觸發;但IE不遵循此順序;
function handler(event){
console.log(event.target.id + ":" + event.type);
}
var btn = document.getElementById("btn");
btn.addEventListener("DOMFocusIn", handler, false);
btn.addEventListener("DOMFocusOut", handler, false);
btn.addEventListener("focus", handler, false);
btn.addEventListener("blur", handler, false);
btn.addEventListener("focusin", handler, false);
btn.addEventListener("focusout", handler, false);
btn.focus();
var txt = document.getElementById("txt");
txt.addEventListener("focus", handler, false);
txt.addEventListener("blur", handler, false);
txt.addEventListener("DOMFocusIn", handler, false);
txt.addEventListener("DOMFocusOut", handler, false);
txt.addEventListener("focusin", handler, false);
txt.addEventListener("focusout", handler, false);
其中,blur、DOMFocusOut和focusout的事件目標是失去焦點的元素,而focus、DOMFocusIn和focusin的事件目標是獲得焦點的元素;
另外,如果同時注冊了focusin和DOMFocusIn或focusout和DOMFocusOut,在IE中只會觸發focusin和focusout;
另外,除了focus和blur事件,其他事件只能通過DOM2級事件添加,如:
var btn = document.getElementById("btn");
btn.onfocusin = function(event){
console.log(event); // 不會觸發
};
btn.onDOMFocusIn = function(event){
console.log(event); // 不會觸發,IE會觸發
};
onfocusin和onfocusout事件,在HTML事件處理中,除FF,其它都支持;DOMFocusIn和DOMFocusOut,在HTML事件處理中,都不支持;
小應用:改變文本框樣式,如:
<style>
.focusInput{border:1px solid purple;outline: none; background-color: rgba(0, 0, 0, .6);}
</style>
<script>
function focusInput(focusClass){
var elements = document.getElementsByTagName("input");
for(var i=0,len=elements.length; i<len; i++){
var elt = elements[i];
if(elt.type == "text"){
elt.onfocus = function(){
this.className = focusClass;
};
elt.onblur = function(){
this.className = "";
}
}
}
}
window.onload = function(){
focusInput('focusInput');
}
</script>
focusin和focusout事件是冒泡的,因此可以使用事件代理,如:
<form id="myform" action="demo.jsp">
<input type="text" id="firstname" value="firstname" />
<input type="text" id="lastname" value="lastname" />
</form>
<script>
var myform = document.getElementById("myform");
myform.addEventListener("focusin", function(event){
event.target.className = "focused";
});
myform.addEventListener("focusout", function(event){
event.target.className = "";
});
</script>
示例:驗證數據,如:
<style>
.invalid{border-color:red;}
#error{color:red}
</style>
<p>郵箱:<input type="email" id="email" /></p>
<div id="error"></div>
<script>
var email = document.getElementById("email");
var error = document.getElementById("error");
email.addEventListener("blur", function(event){
if(!event.target.value.includes("@")){
event.target.classList.add("invalid");
error.innerHTML = "請輸入正確的郵箱";
}
});
email.addEventListener("focus", function(event){
if(this.classList.contains("invalid")){
this.classList.remove("invalid");
error.innerHTML = "";
}
});
</script>
或者使用focus()和blur()方法,如:
<style>
.error{background-color: red !important;}
</style>
<p>郵箱:<input type="email" id="email" /></p>
<script>
var email = document.getElementById("email");
email.addEventListener("blur", function(event){
if(!event.target.value.includes("@")){
event.target.classList.add("error");
event.target.focus();
}else
event.target.classList.remove("error");
});
</script>
示例:一個HTML編輯器;
<style>
.container,.editor{width:400px; height: 150px; display: block;}
.container{padding: 2px; border:1px solid;}
.editor{padding: 0; border:2px solid blue;}
.editor:focus{outline: none;}
</style>
<div id="container" class="container">
<h1>Web前端開發</h1>
</div>
<script>
var area = null;
var container = document.getElementById("container");
container.addEventListener("click", function(event){
editStart();
},false);
function editStart(){
area = document.createElement("textarea");
area.className = "editor";
area.value = container.innerHTML;
area.addEventListener("keydown", function(event){
if(event.keyCode == 13)
this.blur();
},false);
area.addEventListener("blur", function(event){
editEnd();
});
container.replaceWith(area);
area.focus();
}
function editEnd(){
container.innerHTML = area.value;
area.replaceWith(container);
}
</script>
當用戶按下Enter鍵或失去焦點時,<textarea>變回<div>,其內容在<div>中變為HTML;
.應用程序標簽
DataList(數據列表)
Progress(進度條)
Meter(數值顯示器) 示例:
<html lang="en"><head> <meta charset="UTF-8"> <title>應用程序標簽</title> <style> .my-progress{ -webkit-appearance: none; } </style></head><body><!--數據列表,呈現需要載體--><input type="text" list="data-list"><datalist id="data-list"> <option value="張三"></option> <option value="李四"></option></datalist><!--進度條--><progress></progress><!--progress默認最大值是1,可以設定自己想要的值--><progress class="my-progress" value="30" max="100"></progress><meter min="0" max="100" low="40" high="90" optimum="100" value="91">A+</meter></body>
可以利用數據列表(data-list)做一個那種帶下拉框的輸入框,可以根據用戶輸入的內容匹配下拉框的內容(select2也可以實現這種需求,而且最低可以兼容到IE8,推薦用select2),后面的progress和meter樣式不好控制,用的不太多。
2.自定義屬性(data-*)
在html5中可以自定義數據,以data開頭,利用這個自定義標簽我們可以把屬性暫時存儲到頁面中,在js中可以使用,例子如下:
<html lang="en"><head> <meta charset="UTF-8"> <title>data屬性</title></head><body> <ul id="list"> </ul></body><script> // 鍵是ID 值是信息 var data = { 01: { name: "張三1", age: 18 }, 02: { name: "張三2", age: 19 }, 03: { name: "張三3", age: 20 } }; var list = document.getElementById("list"); for(var id in data){ var item = data[id]; var liem = document.createElement("li"); liem.innerHTML = item.name; //自定義data-*屬性 liem.setAttribute("data-name",item.name); liem.setAttribute("data-age",item.age); list.appendChild(liem); //點擊獲取dataset屬性 liem.addEventListener("click",function(){ //下面這兩種方式均可以,都不帶data- alert(this.dataset['name']); alert(this.dataset.name); }) }</script>
下面這篇文章總結的很好,可以參考:http://blog.csdn.net/qq_31851435/article/details/53100691
3.新的表單提交方式
傳統的一共有9中表單提交方式,html5新增了幾種,目前只用在移動端,pc端兼容性有問題,而且在輸入的時候并沒有進行驗證,比如類型是email的,并沒有校驗郵箱格式,還是需要我們自己手動寫正則表達式驗證的,只是在手機端當喚起輸入法的時候會自動切換到英文輸入法。
<head> <meta charset="UTF-8"> <title>新的表單提交方式</title></head><body><h3>傳統的表單提交方式(9種)</h3>用戶名:<input type="text"> <br>密碼:<input type="password"><br>性別:<input type="radio"><br>課程:<input type="checkbox"><br>隱藏:<input type="hidden">文件:<input type="file"><br>按鈕:<input type="button" value="按鈕"><br>表單:<input type="submit"><br>重置:<input type="reset"><br><hr><h3>新的表單提交方式(目前只用在移動端)</h3>顏色:<input type="color"><br>網址:<input type="url"><br>搜索:<input type="search" results="10" placeholder="Search..."><br>郵箱:<input type="email" pattern="^\*.com"><br>日期:<input type="date" min="2015-09-01" max="2018-09-01"><br>圖片:<input type="image"><br>范圍:<input type="range" min="0" max="50" value="10"><br>數字:<input type="number"></body>
4.新的選擇器
html5提供新的選擇器,querySelector選擇單個的元素,返回滿足條件的第一個元素,是一個dom元素,querySelectorAll選擇全部的元素,返回滿足條件的全部元素,是一個dom數組,里面可以是id,標簽或class類。
<html lang="en"><head> <meta charset="UTF-8"> <title>新選擇器</title></head><body><h3>新選擇器</h3><ul> <li class="item">item1</li> <li class="item">item2</li> <li class="item">item3</li> <li class="item">item4</li> <li class="item">item5</li> <li class="item">item6</li> <li class="item">item7</li> <li class="item">item8</li> <li class="item">item9</li> <li class="item">item10</li></ul></body><script> //匿名函數,可以將作用域分隔開 (function(){ var liem = document.querySelector(".item"); //只選擇第一個 console.log(liem.innerHTML); var lis = document.querySelectorAll("ul>li"); //選擇全部 for(var i=0;i<lis.length;i++){ console.log(lis[i]); lis[i].addEventListener("click",function(){ debugger; //可以調試代碼。類似于瀏覽器打斷點 console.log(this.innerHTML); }) } })()</script>
5.元素類列表(Element.classList)
新H5中DOM對象多了一個classList屬性,是一個數組:
add 添加一個新的類名
remove 刪除一個類名
contains 判斷是否包含一個指定的類名
toggle 切換一個類名。 element.toggle('class-name',[addorremove]),第一個參數是類名,第二個參數是布爾值,如果是true,則添加類名,如果是false,則去掉類名。示例:
<head> <meta charset="UTF-8"> <title>元素類列表</title> <link rel="stylesheet" href="css/bootstrap.css"></head><body><div class="container"> <div class="collapse navbar-collapse"> <nav class="navbar navbar-default"> <a class="navbar-brand" href="#">Brand</a> <ul class="nav navbar-nav"> <li><a href="#">Home</a></li> <li><a href="#">Link</a></li> <li><a href="#">Link</a></li> </ul> </nav> </div></div><script> //匿名函數 (function(){ /** 點擊時添加和刪除類名**/ var lis = document.querySelectorAll(".nav li"); for(var i=0;i<lis.length;i++){ lis[i].addEventListener("click",function(e){ //去掉原先所有的樣式 for(var j=0;j<lis.length;j++){ lis[j].classList.remove("active"); } //添加點擊樣式 this.classList.add("active"); e.preventDefault(); //取消事件的默認操作 }) } /** 點擊時存在類名則刪除,不存在類名則添加**/ var elem = document.querySelector(".navbar-brand"); //點擊時如果存在active這個屬性則去掉,如果不存在這個屬性則加上 elem.addEventListener("click",function(e){ //判斷是否包含active這個類名 var isExist = elem.classList.contains("active"); this.classList.toggle("active",!isExist); e.preventDefault(); }) })()</script></body>
6.訪問歷史Api
在HTML5中可以通過window.history操作訪問歷史狀態,讓一個頁面可以有多個歷史狀態
window.history.forward(); // 前進
window.history.back(); // 后退
window.history.go(); // 刷新
history.pushState(放入歷史中的狀態數據, 設置title(現在瀏覽器不支持), 改變歷史狀態),通過JS可以加入一個訪問狀態
<head> <meta charset="UTF-8"> <title>歷史記錄</title></head><body><input type="button" value="測試歷史記錄" onclick="addHistory()"></body><script> //添加歷史記錄 function addHistory(){ //判斷瀏覽器是否支持歷史記錄,畢竟是h5的新特性,低版本瀏覽器不支持 if(window.history && history.pushState){ //支持 history.pushState(new Date().toLocaleDateString(),"設置歷史記錄的標簽,但是目前瀏覽器還不支持","?demo="+new Date().toLocaleTimeString()); }else{ console.log("抱歉,瀏覽器不支持歷史記錄") } } //下面這個事件是點擊瀏覽器中的前進或后退時觸發 window.addEventListener("popstate",function(e){ console.log("點擊了歷史按鈕"+e.state); })</script>
7.全屏Api
H5中可以用requestFullScreen()方法實現指定元素的全屏顯示(類似于瀏覽器中按F11)。首先需要獲取需要全屏顯示的元素,然后判斷瀏覽器是屬于谷歌內核還是火狐內核或者其它瀏覽器,然后調用響應方法即可。(注意:目前這個特性瀏覽器兼容性還比較差,谷歌和火狐可以)
var elem = 需要全屏的元素;if (elem.webkitRequestFullScreen) { elem.webkitRequestFullScreen();} else if (elem.mozRequestFullScreen) { elem.mozRequestFullScreen();} else if (elem.requestFullScreen){ elem.requestFullScreen();}
<head> <meta charset="UTF-8"> <title>全屏顯示</title></head><body><h3 style="cursor: pointer">點我全屏顯示</h3><p>測試全屏顯示測試全屏顯示測試全屏顯示測試全屏顯示測試全屏顯示測試全屏顯示 測試全屏顯示測試全屏顯示測試<br> 全屏顯示測試全屏顯示測試全屏顯示測試全屏顯示測試全屏顯示</p><script> //匿名函數 (function () { //獲取事件源和要全屏顯示的元素 var elem = document.querySelector("p"); document.querySelector("h3").addEventListener("click",function(e){ //判斷瀏覽器是否支持全屏顯示 if (elem.webkitRequestFullScreen) { elem.webkitRequestFullScreen(); } else if (elem.mozRequestFullScreen) { elem.mozRequestFullScreen(); } else if (elem.requestFullScreen){ elem.requestFullScreen(); }else{ console.log("瀏覽器不支持全屏顯示") } }); })()</script></body>
8.Application Cache離線訪問技術
在H5中可以利用Application Cache實現離線訪問技術,測試時可以通過谷歌瀏覽器——NetWork——No throttling(Disable cache后面的那個下拉框)——下拉選擇Offline(模擬未聯網的情形)。示例如下:
html代碼:<html lang="en" manifest="cache.manifest"><head> <meta charset="UTF-8"> <title>離線訪問</title> <link rel="stylesheet" href="style.css"></head><body><p>這是html頁面中的內容</p><script src="script.js"></script></body>style.css文件代碼body::before{ content: "這是Css中引入的內容"; font-size: 40px;}script.js文件代碼:document.write("這是js中引入的內容")cache.manifest文件代碼:CACHE MANIFEST# version 1.0.1CACHE: 08離線訪問.html script.js style.cssNETWORK: *
解釋:在html代碼中通過css的偽類和js的document.write函數分別輸出兩句話,模擬請求其它文件,然后在manifest(緩存清單)中定義了一個cache.manifest的緩存清單文件,文件里首先定義了當前軟件的版本,下面的CACHE,代表斷網情況下,從下面定義的文件中讀取文件,NETWORK代表聯網時候要讀取的文件,星號代表全部。即斷網時從我定義的文件中讀取文件,聯網時,讀取全部文件,這樣便可實現離線訪問。
9.web網頁存儲
h5中提供sessionStorage和localStorage兩種數據存儲方式,前者存儲的內容只在本次會話中存儲,瀏覽器關閉數據消失,后者是可以永久存儲在瀏覽器中,除非手動或通過程序刪除。這兩種存儲方式比cookie存儲的數據量大,而且可以存儲對象數據(cookie只能存儲字符串數據)。
<head> <meta charset="UTF-8"> <title>網頁存儲</title></head><body><textarea rows="5" cols="30" id="txt-data"></textarea><input type="button" value="設置數據" id="set-data"><input type="button" value="獲取數據" id="get-data"><script> (function(){ var txtValue = document.querySelector("#txt-data"); console.log(txtValue.value); //設置數據 document.querySelector("#set-data").addEventListener("click",function (e) { //判斷是否支持存儲 if(window.sessionStorage){ sessionStorage.setItem("key",txtValue.value); }else{ console.log("還在用渣渣IE嗎?升級吧。。。") } }) //獲取數據 document.querySelector("#get-data").addEventListener("click",function (e) { //判斷是否支持存儲 if(window.sessionStorage){ txtValue.value = sessionStorage.getItem("key"); }else{ console.log("還在用渣渣IE嗎?升級吧。。。") } }) })()</script></body>
10.操作文件Api
通過file表單選擇文件,之后利用h5提供的文件api可以獲取文件名,大小,類型,最后修改時間等文件信息。
<html lang="en"><head> <meta charset="UTF-8"> <title>操作文件</title> <link rel="stylesheet" href="css/bootstrap.css"></head><body> <div class="container"> <form> <input type="file" class="form-control hidden" id="btn_file" multiple> <input type="button" value="選擇文件" class="btn btn-success" id="btn_select"> <div class="list-group" id="ul_list"> <!--待追加數據位置--> </div> </form> </div></body><script> (function(){ var btn_file = document.querySelector("#btn_file"); var btn_select = document.querySelector("#btn_select"); var ul_list = document.querySelector("#ul_list"); //利用“hidden”類將文件輸入框隱藏,當點擊“選擇文件按鈕時”,調用選擇文件按鈕,選擇文件, // 因為input type="file"這個無法修改樣式,把它隱藏,點擊其它按鈕時,在其它按鈕中調用點擊上傳文件 btn_select.addEventListener("click",function(){ btn_file.click(); }) //選擇文件完成后(即文件內容發生改變后) btn_file.addEventListener("change",function(e){ var files = btn_file.files; //獲取所有文件,并遍歷 for(var i=0;i<files.length;i++){ console.log(files[i]); var liem = document.createElement("li"); liem.setAttribute("class","list-group-item"); liem.innerHTML = ' <h4 class="list-group-item-heading">'+files[i].name+'</h4>'+ '<p class="list-group-item-text">'+files[i].lastModifiedDate.toLocaleTimeString()+' '+(files[i].size/1024).toFixed(2)+'kb</p>' ul_list.appendChild(liem); } }) })()</script>
這里是用到了bootstrap的樣式,因為file表單比較丑,而且無法改變樣式,所以我們可以把文件表單隱藏(用hidden隱藏樣式),提供一個按鈕,當點擊這個按鈕時去雕砌選文件的彈框,multiple屬性支持一次選擇多個文件。
11.拖拽文件
拖拽文件時需要依次注冊以下事件:
dragenter 注冊拖拽進入事件
dragleave 注冊拖拽離開事件
dragover 注冊滑動事件(在這里面阻止默認事件)
drop 注冊松手落地事件。 在拖動松手落地事件中判斷拖進來的是文件還是圖片地址還是文字,分別進行處理。e.dataTransfer獲取拖動進來的信息。
<head> <meta charset="UTF-8"> <title>拖拽文件</title> <link rel="stylesheet" href="css/bootstrap.css"> <style> #target{ height: 200px; border: 5px dashed #c0c0c0; color: #505050; padding: 20px; font-size: 40px; cursor: pointer; text-align: center; -webkit-user-select: none; } #target.actived{ border-color: #888; color: #080808; box-shadow: 0 0 80px #e0e0e0 inset; } </style></head><body><div class="container"> <div class="page-header"> <h3>拖拽文件</h3> </div> <div class="jumbotron"> <p>我們這里測試拖拽文件</p> <img src="test.png"> </div> <div id="target"> 拖拽文件進來 <ul id="result" class="list-group"></ul> </div></div></body><script> (function(){ var target = document.querySelector("#target"); var fileList = document.querySelector('#result'); //注冊拖拽進入事件 target.addEventListener("dragenter",function(){ this.classList.add("actived"); //添加樣式 }) //注冊拖拽文件離開事件 target.addEventListener("dragleave",function(){ this.classList.remove("actived"); //添加樣式 }) //注冊落地事件之前必須要先阻止默認事件,這里在滑動里面阻止默認事件 target.addEventListener("dragover",function(e){ e.preventDefault(); //阻止默認事件 e.stopPropagation(); }) //注冊松手落地事件 target.addEventListener("drop",function(e){ //判斷拖入進來的是文件,圖片還是文字,分別處理 if(e.dataTransfer.files.length){ //拖入的是文件 var files = e.dataTransfer.files; for (var i = 0; i < files.length; i++) { var li = document.createElement('li'); li.setAttribute('class', 'list-group-item'); // 創建信息的子節點 li.innerHTML = '<h5 class="list-group-item-heading">' + files[i].name + '</h5><p class="list-group-item-text">' + files[i].lastModifiedDate.toLocaleDateString() + ' ' + files[i].lastModifiedDate.toLocaleTimeString() + ' ' + (files[i].size / 1024).toFixed(2) + 'KB</p>'; fileList.appendChild(li); } }else{ //不是文件 var data = e.dataTransfer.getData('text/plain'); //判斷拖入的是文本還是圖片 if(data){ //拖入的是文本,直接置換 target.innerHTML = data; }else{ //拖入的是圖片 var imgElem = document.createElement("img"); imgElem.src = data; target.appendChild(imgElem); } } }) })()</script>
12.訪問設備信息
h5提供了許多可以訪問設備的Api,但是目前基本都應用在手機端,pc端使用非常有限。
獲取網絡狀態。
// × 所有PC瀏覽器現在都不支持 只有手機端的Firefoxvar connectionInfo = navigator.connection;或者下面這個方法:if (navigator.onLine){ console.log('online');}else{ console.log('offline');}
獲取重力感應方向
window.addEventListener('deviceorientation', function(event) { var a = event.alpha; // Y軸 上下方向 var b = event.beta; // Z軸 東西方向 var g = event.gamma; // X軸 南北方向});
加速度計
window.addEventListener('devicemotion', function(e) { // 獲取加速計數據 {x,y,z} var acceleration = e.accelerationIncludingGravity;});
地理坐標
navigator.geolocation.getCurrentPosition(function(e) { // e.coords.longitude : 經度, e.coords.latitude : 緯度 document.querySelector('#result').innerHTML = JSON.stringify(e.coords);}, function(e) { document.querySelector('#result').innerHTML = JSON.stringify(e);});navigator.geolocation.watchPosition(success, error);
H5還有其它的一些特性,包括語義化標簽,ARIA無障礙互聯網應用,多媒體(包括音頻,視頻)等等,真正使用的時候再詳細研究。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。