javascript 中的事件機制

js之事件機制

1、事件初探

1.1 js事件的概述

JavaScript事件:JavaScript是基于事件驅(qū)動模型的,所有的內(nèi)容幾乎都可以和事件掛鉤乓搬。它首先定義了一些事件,然后在具體的某個環(huán)境觸發(fā)了該事件,再完成相應(yīng)的操作躯泰。

事件可以這么理解:它擁有事件三要素(事件源、事件华糖、監(jiān)聽器)麦向。

  • 事件源:在哪個元素上發(fā)生的,p客叉、a诵竭、div、form表單
  • 事件:到底發(fā)生了什么事件兼搏,click卵慰、mouseover、load佛呻、submit裳朋、focus
  • 監(jiān)聽器:如何應(yīng)對事件的發(fā)生,如何回應(yīng)發(fā)生的事件吓著,通常以函數(shù)的形式來出現(xiàn)再扭。

重申事件的幾個坑:

  • 事件實際上應(yīng)該稱之為事件模型氧苍,事件本身只是一個單純的行為。
  • 事件不是以 on 開頭的那個名稱泛范,如 onclick 不是事件让虐,click才是事件。onclick引用的是一個元素對象的屬性罢荡,它指向click事件類型綁定的實際處理函數(shù)赡突。

1.2 js事件的幾個概念

  • 事件:指的是文檔或者瀏覽器窗口中發(fā)生的一些特定交互瞬間。我們可以通過偵聽器(或者處理程序)來預(yù)定事件区赵,以便事件發(fā)生的時候執(zhí)行相應(yīng)的代碼惭缰。

  • 事件處理程序:我們用戶在頁面中進行的點擊這個動作、鼠標移動的動作笼才,網(wǎng)頁頁面加載完成的動作等漱受,都可以稱之為事件名稱,即:click骡送、mousemove昂羡、load等都是事件的名稱。響應(yīng)某個事件的函數(shù)則稱為事件處理程序摔踱,或者叫做事件偵聽器虐先。事件處理程序的名字是以“on”開頭,因此click事件的處理程序就是onclick派敷。

  • 事件類型:

    • UI事件:如load蛹批、unload、error篮愉、resize腐芍、scroll、select试躏、DOMActive猪勇,是用戶與頁面上的元素交互時觸發(fā)的。
    • 焦點事件:如blur冗酿、DOMFocusIn埠对、DOMFocusOut络断、focus裁替、focusin、focusout貌笨,在元素獲得或失去焦點的時候觸發(fā)弱判,這些事件當中,最為重要的是blur和focus锥惋,有一點需要引起注意昌腰,這一類事件不會發(fā)生冒泡开伏!
    • 鼠標與滾輪事件:如click、dblclick遭商、mousedown固灵、mouseenter、mouseleave劫流、mousemove巫玻、mouseout、mouseover祠汇、mouseup仍秤,是當用戶通過鼠標在頁面執(zhí)行操作時所觸發(fā)的。
    • 滾輪事件:mousewheel(IE6+均支持)可很、DOMMouseScroll(FF支持的诗力,與mousewheel效果一樣)。是使用鼠標滾輪時觸發(fā)的我抠。
    • 文本事件:textInput苇本,在文檔中輸入文本觸發(fā)。
    • 鍵盤事件:keydown屿良、keyup圈澈、keypress,當用戶通過鍵盤在頁面中執(zhí)行操作時觸發(fā)尘惧。
    • 合成事件:DOM3級新增康栈,用于處理IME的輸入序列。所謂IME喷橙,指的是輸入法編輯器啥么,可以讓用戶輸入在物理鍵盤上找不到的字符。compositionstart贰逾、compositionupdate悬荣、compositionend三種事件。
    • 變動事件:DOMsubtreeModified疙剑、DOMNodeInserted氯迂、DOMNodeRemoved、DOMAttrModified言缤、DOMCharacterDataModified等嚼蚀,當?shù)讓覦OM結(jié)構(gòu)發(fā)生變化時觸發(fā)。IE8-不支持管挟。
    • 變動名稱事件:指的是當元素或者屬性名變動時觸發(fā)轿曙,當前已經(jīng)棄用!

對于事件的基本類型,隨著HTML5的出現(xiàn)和發(fā)展导帝,又新增了HTML5事件守谓、設(shè)備事件、觸摸事件您单、手勢事件

  • 事件流:描述的是從頁面中接收事件的順序斋荞。
    • 事件冒泡:事件開始的時候由最具體的元素(文檔中嵌套層次最深的那個節(jié)點)接收,然后逐級向上傳播到較為不具體的節(jié)點(文檔)虐秦。

    • 事件捕獲:事件開始的時候由最不具體的節(jié)點接收譬猫,然后逐級向下傳播到最具體的節(jié)點。

IE與原來的NetScape(網(wǎng)景)羡疗,對于事件流提出的是完全不同的順序染服。IE團隊提出的是事件冒泡流;NetScape的事件流是事件捕獲流叨恨。

  • 事件對象:在觸發(fā)DOM上的某個事件的時候柳刮,會產(chǎn)生一個事件對象event,而在這個對象當中會包含著所有與事件有關(guān)的信息痒钝。

  • 事件委托:給元素的父級或者祖級秉颗,甚至頁面綁定事件,然后利用事件冒泡的基本原理送矩,通過事件目標對象進行檢測蚕甥,然后執(zhí)行相關(guān)操作。

  • 移除事件處理程序:每當將一個事件處理程序指定給一個元素時栋荸,在運行中的瀏覽器代碼與支持頁面交互的JavaScript代碼之間就會建立一個連接菇怀。連接數(shù)量也直接影響著頁面的執(zhí)行速度。所以晌块,當內(nèi)存中存在著過時的“空事件處理程序”的時候爱沟,就會造成Web應(yīng)用程序的內(nèi)存和性能問題。

上述的這些概念會在下面幾個環(huán)節(jié)中逐個提出并解釋匆背,下面大概會有事件源(DOM)呼伸、事件綁定(處理程序)、事件對象钝尸、事件類型等幾個部分介紹括享。

2、事件源

之所以這環(huán)節(jié)說DOM珍促,一方面是因為它是事件三要素之一铃辖,另一方面可以看出javascript在操作HTML文檔時,中間折現(xiàn)出的一些原理踢星。下面對DOM只說大概歷程澳叉,不談具體方法。

2.1 DOM

Document Object Model:文檔對象模型沐悦,是一組描述腳本與結(jié)構(gòu)化文檔進行交互的web的標準成洗,它定義了一系列對象、方法和屬性藏否,用于訪問瓶殃、操作和創(chuàng)建文檔中的內(nèi)容、結(jié)構(gòu)和行為副签。

  • D:documnet遥椿,文檔,html文檔或者xml文檔
  • O:object淆储,對象冠场,在轉(zhuǎn)成樹模型的時候,得到的對象本砰,它有相應(yīng)的屬性和方法碴裙,利用他們可以完成任何操作。
  • M:model点额,模型舔株,樹模型,有節(jié)點構(gòu)成的一顆樹还棱。節(jié)點(元素载慈、屬性和文本)轉(zhuǎn)成對象。

在W3C的標準中珍手,DOM是獨于平臺和語言的接口办铡,它允許程序和腳本動態(tài)地訪問和更新文檔的內(nèi)容、結(jié)構(gòu)和樣式琳要。

W3C DOM由以下三部分組成:

  • 核心DOM - 針對任何結(jié)構(gòu)化文檔的標準模型
  • XML DOM - 針對 XML 文檔的標準模型
  • HTML DOM - 針對 HTML 文檔的標準模型

javascript是ECMAscript的產(chǎn)物料扰、DOM是W3C的產(chǎn)物、兩者結(jié)合相得益彰焙蹭。

2.1.1 DOM0晒杈、DOM1、DOM2孔厉、DOM3的區(qū)別

DOM0

實際上是未形成標準的試驗性質(zhì)的初級階段的DOM拯钻,現(xiàn)在習慣上被稱為DOM0。它定義了一些document的屬性和方法撰豺,提供了查詢和操作Web文檔的內(nèi)容API粪般。

常在使用的有:forms,cookie污桦,title亩歹,其它不建議使用。

W3C結(jié)合網(wǎng)景和IE的優(yōu)點于1998年10月推出了一個標準化的DOM,完成了第一級DOM小作,即:DOM1亭姥。W3C將DOM定義為一個與平臺和編程語言無關(guān)的接口,通過這個接口程序和腳本可以動態(tài)的訪問和修改文檔的內(nèi)容顾稀、結(jié)構(gòu)和樣式达罗。

DOM1級主要定義了HTML和XML文檔的底層結(jié)構(gòu)。在DOM1中静秆,DOM由兩個模塊組成:DOM Core(DOM核心)和DOM HTML粮揉。其中,DOM Core規(guī)定了基于XML的文檔結(jié)構(gòu)標準抚笔,通過這個標準簡化了對文檔中任意部分的訪問和操作扶认。DOM HTML則在DOM核心的基礎(chǔ)上加以擴展,添加了針對HTML的對象和方法殊橙,如:JavaScript中的Document對象蝠引。

DOM2級在原來DOM的基礎(chǔ)上又擴充了鼠標、用戶界面事件蛀柴、范圍螃概、遍歷等細分模塊,而且通過對象接口增加了對CSS的支持鸽疾。

在DOM2中引入了下列模塊吊洼,在模塊包含了眾多新類型和新接口:

  • DOM視圖(DOM Views):定義了跟蹤不同文檔視圖的接口
  • DOM事件(DOM Events):定義了事件和事件處理的接口
  • DOM樣式(DOM Style):定義了基于CSS為元素應(yīng)用樣式的接口
  • DOM遍歷和范圍(DOM Traversal and Range):定義了遍歷和操作文檔樹的接口

DOM3進一步擴展了DOM,在DOM3中引入了以下模塊:

  • DOM加載和保存模塊(DOM Load and Save):引入了以統(tǒng)一方式加載和保存文檔的方法
  • DOM驗證模塊(DOM Validation):定義了驗證文檔的方法
  • DOM核心的擴展(DOM Style):支持XML 1.0規(guī)范制肮,涉及XML Infoset冒窍、XPath和XML Base

其整體的發(fā)展歷程如下:

DOM發(fā)展圖

3、事件綁定

3.1 事件的綁定方式

第一種方式:DOM0級豺鼻,即以屬性的方式直接寫在行內(nèi)综液。一般的驗證碼切換就有這樣的機制。

<a href="#" id="dom0" onclick="changeCaptcha();">

第二種方式:DOM1級儒飒,給元素添加屬性(例:onclick)谬莹,屬性的值就是一個具體的函數(shù)(click事件類型綁定的處理函數(shù))蔗崎。這里就有一個問題沛简,無法允許團隊不同人員對同一元素監(jiān)聽同一事件但做出不用的響應(yīng)。

<body>
  <div id="event">這是事件機制學(xué)習</div>
  <script>
    var div=document.getElementById('event');
    // 甲程序猿
    div.onclick=function(){
        console.log('甲要紅背景');
        div.setAttribute('style', 'background: #ff0000');
    };
    // 乙程序猿
    div.onclick=function(){
        console.log('乙要黃背景');
        div.setAttribute('style', 'background: #ffff00');
    };
  </script>
</body>
DOM1點擊響應(yīng)效果

這里面出現(xiàn)的問題:無法給同一個元素綁定多個相同的事件馆衔,然而在web開發(fā)中井誉,這個是非常普遍的一個應(yīng)用蕉扮。

第三種方式:DOM2級,對主流瀏覽器來說颗圣,使用addEventListenerremoveListener方法喳钟,它們都接受3個參數(shù):要處理的事件名屁使、作為事件處理程序的函數(shù)和一個布爾值。最后的布爾值參數(shù)如果是true奔则,表示在捕獲階段調(diào)用事件處理程序蛮寂;如果是false,表示在冒泡階段調(diào)用事件處理程序应狱。若最后的布爾值不填寫,則和false效果一樣祠丝。這里它支持同一dom元素注冊多個同種事件疾呻,還有新增了捕獲冒泡的概念。

<body>
  <div id="event">這是事件機制學(xué)習</div>
  <script>
    var div=document.getElementById('event');
    div.addEventListener('click', function(){
        console.log('這是DOM2級写半,甲綁定事件的的響應(yīng)');
    });
    div.addEventListener('click', function(){
        console.log('這是DOM2級岸蜗,乙綁定事件的的響應(yīng)');
    });
  </script>
</body>
DOM2點擊響應(yīng)效果

它也有問題:兼容性,IE8對此自定義了兩個自己的方法attachEventdetachEvent方法叠蝇。同時璃岳,需要注意IE在這里是‘onclick’。

div.attachEvent('onclick', function(){
    console.log('這是DOM2級悔捶,IE綁定事件的的響應(yīng)');
});

那么為了保持瀏覽器兼容性問題铃慷,我們還需要自己封裝一個簡單的函數(shù)去實現(xiàn)事件的綁定。同時蜕该,由于attachEvent()方法中的this指向window(下面會有說明)犁柜,所以需要對this進行顯式修改。

<body>
<div id="event">這是事件機制學(xué)習</div>
<script>
    var div=document.getElementById('event');
    function reponseCode(){
        console.log('這是封裝的綁定事件');
    }
    // 事件的綁定機制
    function addHandle(obj, type, handle){
        if(obj.addEventListener){
            obj.addEventListener(type, handle堂淡,false);
        }else if(obj.attachEvent){
            obj.attachEvent('on'+type, function(event) {
                return handler.call(target, event);
            });
        }else{
            obj['on'+type]=handle;
        }
    }
    addHandle(div, 'click', reponseCode);
</script>
</body>

移除事件綁定:通過addEventListener()添加的事件處理程序只能使用removeEventListener()來移除馋缅,移除時傳入的參數(shù)與添加處理程序時使用的參數(shù)相同。這意味著绢淀,addEventListener()添加的匿名函數(shù)將無法移除萤悴。同理attachEvent()和detachEvent();

無效代碼:

<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
    box.addEventListener("click",function(){
        this.innerHTML += '1'
    },false);
    box.removeEventListener('click',function(){
        this.innerHTML += '1'
    },false);
</script>

有效代碼:

<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
    var handle = function(){
        this.innerHTML += '1'
    };
    box.addEventListener("click",handle,false);
    box.removeEventListener('click',handle,false);    
</script>

說完上面綁定事件的幾種方式,這里還要指出一點皆的,即事件處理程序中的this所指覆履。

<div id="box" style="height:100px;width:300px;background-color:pink;"
     onclick = "console.log(this)">//<div>
</div>

<div id="box" style="height:100px;width:300px;background-color:pink;"></div>
<script>
    box.onclick= function(){
        console.log(this);//<div>
    }
</script>

<div id="box" style="height:100px;width:300px;background-color:pink;"></div>
<script>
    box.addEventListener('click',function(){
        console.log(this);//<div>
    });
</script>

<div id="box" style="height:100px;width:300px;background-color:pink;"></div>
<script>
    box.attachEvent('onclick',function(){
        console.log(this);//window
    });
</script>

與其他三個事件處理程序不同,IE事件處理程序的this指向window费薄,而非被綁定事件的元素内狗。

3.2 事件流

javascript操作CSS稱為腳本化CSS,而javascript與HTML的交互是通過事件實現(xiàn)的义锥。事件就是文檔或瀏覽器窗口中發(fā)生的一些特定的交互瞬間柳沙,而事件流(又叫事件傳播)描述的是從頁面中接收事件的順序。

3.2.1 歷史淵源

當瀏覽器發(fā)展到第四代時(IE4及Netscape4)拌倍,瀏覽器開發(fā)團隊遇到了一個很有意思的問題:頁面的哪一部分會擁有某個特定的事件赂鲤?想象畫在一張紙上的一組同心圓噪径。如果把手指放在圓心上,那么手指指向的不是一個圓数初,而是紙上的所有圓找爱。

兩家公司的瀏覽器開發(fā)團隊在看待瀏覽器事件方面還是一致的。如果單擊了某個按鈕泡孩,他們都認為單擊事件不僅僅發(fā)生在按鈕上车摄,甚至也單擊了整個頁面。

但有意思的是仑鸥,IE和Netscape開發(fā)團隊居然提出了差不多是完全相反的事件流的概念吮播。IE的事件流是事件冒泡流,而Netscape的事件流是事件捕獲流眼俊。

一個普通的HTML文檔意狠,下面統(tǒng)一使用。

<!DOCTYPE HTML>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <body>
    <div></div>
  </body>    
</html>

3.2.2 'IE'的'事件冒泡流'

IE的事件流叫做事件冒泡(event bubbling)疮胖,即事件開始時由最具體的元素(文檔中嵌套層次最深的那個節(jié)點)接收环戈,然后逐級向上傳播到較為不具體的節(jié)點(文檔)。

如果單擊了頁面中的'div'元素澎灸,那么這個click事件沿DOM樹向上傳播院塞,在每一級節(jié)點上都會發(fā)生,按照如下順序傳播:

(1)    <div>
(2)    <body>
(3)    <html>
(4)    document

所有現(xiàn)代瀏覽器都支持事件冒泡性昭,但在具體實現(xiàn)在還是有一些差別迫悠。IE9、Firefox巩梢、Chrome创泄、Safari將事件一直冒泡到window對象,如下括蝠。

(1)    <div>
(2)    <body>
(3)    <html>
(4)    document
(5)    window

事件冒泡流測試代碼:

<body>
  <div id="box" style="height: 100px;width: 300px;background: pink;"></div>
  <button id="reset">還原</button>
  <script>
    var box=document.getElementById('box');
    var reset=document.getElementById('reset');
    reset.onclick=function(){
        history.go();
    };
    box.onclick=function(){
        box.innerHTML+='div鞠抑、';
    };
    document.documentElement.onclick=function(){
        box.innerHTML+='html、';
    };
    document.body.onclick=function(){
        box.innerHTML+='body忌警、';
    };
    document.onclick=function(){
        box.innerHTML+='document搁拙、';
    };
    window.onclick=function(){
        box.innerHTML+='window<br>';
    };
  </script>
</body>

效果如下:

事件冒泡流

3.2.3 'Netscape'的'事件捕獲流'

網(wǎng)景的事件捕獲流,思想是不太具體的節(jié)點應(yīng)該更早接收到事件法绵,而最具體的節(jié)點應(yīng)該最后接收到事件箕速。事件捕獲的用意在于在事件到達預(yù)定目標之前就捕獲它。

在事件捕獲過程中朋譬,document對象首先接收到click事件盐茎,然后事件沿DOM樹依次向下,一直傳播到事件的實際目標徙赢,即<div>元素字柠。

(1)    document
(2)    <html>
(3)    <body>
(4)    <div>

IE9探越、Firefox、Chrome窑业、Safari等現(xiàn)代瀏覽器都支持事件捕獲钦幔,但是從window對象開始捕獲。

(1)    window
(2)    document
(3)    <html>
(4)    <body>
(5)    <div>

事件捕獲流代碼:

<body>
  <div id="box" style="height: 100px;width: 300px;background: #ccc;overflow: auto;"></div>
  <button id="reset">還原</button>
  <script>
    //IE8-瀏覽器返回div body html document
    //其他瀏覽器返回div body html document window
    var box=document.getElementById('box');
    var reset=document.getElementById('reset');
    reset.onclick=function(){
        history.go();
    };
    box.addEventListener('click', function(){
        box.innerHTML+='div\n';
    }, true);
    document.documentElement.addEventListener('click', function(){
        box.innerHTML+='html\n';
    }, true);
    document.body.addEventListener('click', function(){
        box.innerHTML+='body\n';
    }, true);
    document.addEventListener('click', function(){
        box.innerHTML+='document\n';
    }, true);
    window.addEventListener('click', function(){
        box.innerHTML+='window\n';
    }, true);
  </script>
</body>

addEventListener()方法中的第三個參數(shù)設(shè)置為true時常柄,即為事件捕獲階段鲤氢。

3.2.4 'W3C'的'DOM2'

事件流又稱為事件傳播,DOM2級事件規(guī)定的事件流包括三個階段:事件捕獲階段(capture phase)西潘、處于目標階段(target phase)和事件冒泡階段(bubbling phase)卷玉。

首先發(fā)生的是事件捕獲,為截獲事件提供了機會秸架,然后是實際的目標接收到事件揍庄,最后一個階段是冒泡階段咆蒿,可以在這個階段對事件做出響應(yīng)东抹,如下圖。

DOM2事件流

即真正觸發(fā)事件的dom元素沃测,是捕獲事件的終點缭黔,是冒泡事件的起點,所以這里就不區(qū)分事件了蒂破,哪個先注冊馏谨,就先執(zhí)行哪個。

3.3 事件委托

事件委托就是利用事件冒泡附迷,只指定一個事件處理程序惧互,就可以管理某一類型的所有事件。它能讓你避免對特定的每個節(jié)點添加事件監(jiān)聽器喇伯;相反喊儡,事件監(jiān)聽器是被添加到它們的父元素上。事件監(jiān)聽器會分析從子元素冒泡上來的事件稻据,找到是哪個子元素的事件艾猜。

3.3.1 事件委托現(xiàn)象比擬

網(wǎng)摘的大牛基本都用取快遞去描述事件委托這個現(xiàn)象捻悯,大體是這么個內(nèi)容匆赃。

有三個同事預(yù)計會在周一收到快遞。為簽收快遞今缚,有兩種辦法:一是三個人在公司門口等快遞算柳;二是委托給前臺MM代為簽收。現(xiàn)實當中姓言,我們大都采用委托的方案(公司也不會容忍那么多員工站在門口就為了等快遞)埠居。前臺MM收到快遞后查牌,她會判斷收件人是誰,然后按照收件人的要求簽收滥壕,甚至代為付款纸颜。這種方案還有一個優(yōu)勢,那就是即使公司里來了新員工(不管多少)绎橘,前臺MM也會在收到寄給新員工的快遞后核實并代為簽收胁孙。

這里其實還有2層意思的:

第一,現(xiàn)在委托前臺的同事是可以代為簽收的称鳞,即程序中的現(xiàn)有的dom節(jié)點是有事件的涮较;

第二,新員工也是可以被前臺MM代為簽收的冈止,即程序中新添加的dom節(jié)點也是有事件的狂票。

3.3.2 事件委托實現(xiàn)

我們以下面的代碼舉例:

<body>
  <ul id="outul">
    <li id="innerli1">事件冒泡流</li>
    <li id="innerli2">事件捕捉流</li>
    <li id="innerli3">事件委托</li>
    <li id="innerli4">事件對象</li>
  </ul>
</body>

1、假設(shè)我們要實現(xiàn)熙暴,點擊每個li元素會在終端輸出標簽內(nèi)的文本內(nèi)容闺属,傳統(tǒng)的思想是這樣的:我們遍歷li標簽,然后為他們綁定點擊事件周霉。那么腳本是這樣的;

<script>
  var outul=document.getElementById('outul');
  var allli=document.getElementsByTagName('li');
  for(var i=0; i<allli.length; i++){
    allli[i].onclick=function(){
      console.log(this.innerHTML);
    }
  }
</script>

2掂器、那么假設(shè)我們使用事件委托的方式呢。即我們?yōu)閘i標簽的父級標簽ul綁定點擊事件俱箱,從事件冒泡流的原理來說国瓮,li的點擊事件會冒泡到ul上面,這時狞谱,因為之前ul已經(jīng)綁定過了點擊事件乃摹,那么這個點擊事件就會被觸發(fā)。這里面就存在了一個問題:我們想要對不同的li標簽在響應(yīng)各自的點擊事件時跟衅,即事件三要素之一的監(jiān)聽器(事件處理程序)是不一樣的孵睬,那我們?yōu)閡l綁定的點擊事件又有什么意義呢?

為了解決上面的問題与斤,即我們要知道點擊事件被觸發(fā)時肪康,如何找到相對應(yīng)的不同的li標簽。Event對象(下面的環(huán)節(jié)會單獨介紹)提供了一個了一個屬性叫做target撩穿,可以返回事件的目標節(jié)點磷支,即事件三要素之一的事件源,也就是說target可以表示為當前的事件操作的DOM食寡,但不是真正操作DOM雾狈。

Event對象存在兼容性問題,代碼大體如下抵皱,在下環(huán)節(jié)里面會具體解釋善榛。

  <script>
    var outul=document.getElementById('outul');
    outul.onclick=function(event){
        // event即Event對象
        var ent=event || window.event;
        var target=ent.target || ent.srcElement;
        console.log(target);
        if(target.nodeName.toLowerCase()=='li'){
            console.log(target.innerHTML);
        }
    }
  </script>

這時有人說辩蛋,上面的都是點擊li操作的是同樣的效果,要是每個li被點擊的效果都不一樣移盆,那么用事件委托還有用嗎悼院?繼續(xù)來。

這次換原代碼咒循。

<div id="box">
  <input type="button" id="add" value="添加" />
  <input type="button" id="remove" value="刪除" />
  <input type="button" id="move" value="移動" />
  <input type="button" id="select" value="選擇" />
</div>

假設(shè)我們對不用的按鈕實現(xiàn)相應(yīng)的功能据途,簡單輸出終端名稱來代替。

3叙甸、同父元素不同子元素事件監(jiān)聽器不同颖医,傳統(tǒng)代碼。

<script>
  var add=document.getElementById("add");
  var remove=document.getElementById("remove");
  var move=document.getElementById("move");
  var select=document.getElementById("select");
  add.onclick = function(){
      console.log('添加');
  };
  remove.onclick = function(){
      console.log('刪除');
  };
  move.onclick = function(){
      console.log('移動');
  };
  select.onclick = function(){
      console.log('選擇');
  }
</script>

4裆蒸、同父元素不同子元素事件監(jiān)聽器不同熔萧,事件委托代碼。

<script>
  var box=document.getElementById('box');
  box.onclick=function(event){
      var ent=event || window.event;
      var target=ent.target || ent.srcElement;
      if(target.nodeName.toLowerCase()=='input'){
          switch(target.id){
              case 'add':
                  console.log('添加');
                  break;
              case 'remove':
                  console.log('刪除');
                  break;
              case 'move':
                  console.log('移動');
                  break;
              case 'select':
                  console.log('選擇');
                  break;
              default:
                  console.log('測試');
          }
      }
  };
</script>

5僚祷、同父元素不同子元素+新添加子元素佛致,事件委托。

<body>
<input type="button" id="add" value="添加" />
<ul id="outul">
  <li id="innerli1">事件冒泡流</li>
  <li id="innerli2">事件捕捉流</li>
  <li id="innerli3">事件委托</li>
  <li id="innerli4">事件對象</li>
</ul>
<script>
  var add=document.getElementById('add');
  var outul=document.getElementById('outul');
  add.onclick=function(){
      var li=document.createElement('li');
      var text=document.createTextNode('新事件');
      li.appendChild(text);
      outul.appendChild(li);
  };
  outul.onclick=function(event){
      var ent=event || window.event;
      var target=ent.target || ent.srcElement;
      if(target.nodeName.toLowerCase()=='li'){
          console.log(target.innerHTML);
      }
  };
</script>
</body>

最適合使用事件委托技術(shù)的事件包括click久妆、mousedown晌杰、mouseup跷睦、keydown筷弦、keyup和keypress。

4抑诸、事件對象

Event 對象代表事件的狀態(tài)烂琴,比如事件在其中發(fā)生的元素、鍵盤按鍵的狀態(tài)蜕乡、鼠標的位置奸绷、鼠標按鈕的狀態(tài)。

在觸發(fā)DOM上的某個事件時层玲,會產(chǎn)生一個事件對象event号醉。這個對象中包含著所有與事件有關(guān)的信息。包括導(dǎo)致事件的元素辛块,事件的類型以及其他與特定事件相關(guān)的信息畔派。

例:當用戶單擊某個元素的時候,我們給這個元素注冊的事件就會觸發(fā),該事件的本質(zhì)就是一個函數(shù),而該函數(shù)的形參接收一個event對象。

有瀏覽器都支持event對象润绵,但支持方式不同线椰。

4.1 獲取事件對象

event對象是事件函數(shù)的第一個參數(shù),如文本內(nèi)容一樣尘盼,IE8不支持憨愉;再者如終端輸出烦绳,火狐不支持。

<body>
<b>IE8-瀏覽器輸出undefined配紫,其他瀏覽器則輸出事件對象[object MouseEvent]</b><br />
<div id="box" style="height: 30px; line-height: 30px; width: 200px; background: #ccc;"></div>
<script>
  var box=document.getElementById('box');
  box.onclick=function(ent){
      box.innerHTML=ent;
      // 直接訪問event變量径密、firefox瀏覽器不支持
      console.log(event);
  }
</script>
</body>

然后產(chǎn)生一個兼容所有瀏覽器的寫法:

<body>
<div id="box" style="height: 30px; line-height: 30px; width: 200px; background: #ccc;"></div>
<script>
  var box=document.getElementById('box');
  box.onclick=function(ent){
      ent=ent || event;
      box.innerHTML=ent;
      console.log(ent);
  }
</script>
</body>

這里在之前事件委托中已經(jīng)使用到。

4.2 事件的屬性和方法

事件對象包含與創(chuàng)建它的特定事件有關(guān)的屬性和方法躺孝。觸發(fā)的事件類型不一樣睹晒,可用的屬性和方法也不一樣。不過括细,所有事件都有些共有的屬性和方法伪很。

4.2.1 事件類型

事件有很多類型,事件對象中的type屬性表示被觸發(fā)的事件類型奋单。

<body>
<div id="box" style="height: 30px; line-height: 30px; width: 200px; background: #ccc;"></div>
<script>
  var box=document.getElementById('box');
  box.onclick=box.onmouseover=box.onmouseout=function(ent){
      ent=ent || event;
      box.innerHTML=ent.type;
  }
</script>
</body>

上述代碼分別在鼠標移入锉试、點擊、移出時顯示:mouseover览濒、click呆盖、mouseout。

4.2.2 事件目標

關(guān)于事件目標贷笛,共有currentTarget应又、target和srcElement這三個屬性。

1乏苦、currentTarget

currentTarget屬性返回事件當前所在的節(jié)點株扛,即正在執(zhí)行的監(jiān)聽函數(shù)所綁定的那個節(jié)點,但IE8-瀏覽器不支持汇荐。

一般地洞就,currentTarget與事件中的this指向相同。但在attachEvent()事件處理程序中掀淘,this指向window旬蟋。之前事件綁定中已提及。

<body>
<ul id="outul">
  <li>currentTarget</li>
  <li>target</li>
</ul>
<script>
    var outul=document.getElementById('outul');
    var allli=document.getElementsByTagName('li');
    outul.onclick=function(ent){
        ent=ent || event;
        allli[0].innerHTML=ent.currentTarget;
        allli[1].innerHTML=(this===ent.currentTarget);
    }
</script>
</body>

2革娄、target

currentTarget屬性返回事件正在執(zhí)行的監(jiān)聽函數(shù)所綁定的節(jié)點倾贰,而target屬性返回事件的實際目標節(jié)點,但IE8-瀏覽器不支持拦惋。

<body>
<ul id="outul" style="background: #ccc; width: 300px; height: 60px; line-height: 30px;">
  <li>currentTarget</li>
  <li>target</li>
</ul>
<script>
    var outul=document.getElementById('outul');
    outul.onmouseover=function(ent){
        ent=ent || event;
        ent.target.style.background='red';
        console.log(ent.target);
    };
    outul.onmouseout = function(ent){
        ent=ent || event;
        ent.target.style.background='#ccc';
        console.log(ent.target);
    };
</script>
</body>

上部分代碼分別有三部分可以移入匆浙、移出,分別是左部架忌、右側(cè)上部吞彤、右側(cè)下部。效果是移入變紅、移出回顯灰色饰恕,兩者終端都會輸出相應(yīng)節(jié)點挠羔。

3、srcElement

srcElement屬性與target的功能一致埋嵌,但火狐不兼容破加。

<body>
<ul id="outul" style="background: #ccc; width: 300px; height: 60px; line-height: 30px;">
  <li>currentTarget</li>
  <li>target</li>
</ul>
<script>
    var outul=document.getElementById('outul');
    outul.onmouseover=function(ent){
        ent=ent || event;
        ent.srcElement.style.background='red';
        console.log(ent.srcElement);
    };
    outul.onmouseout = function(ent){
        ent=ent || event;
        ent.srcElement.style.background='#ccc';
        console.log(ent.srcElement);
    };
</script>
</body>

故結(jié)合以上三點,一般兼容代碼如下:

<script>
    var handler = function(ent){
        ent=ent || event;
        var target=ent.target || ent.srcElement;
    };
</script>

4.2.3 事件冒泡

事件冒泡是事件流的第三個階段雹嗦,通過事件冒泡可以在這個階段對事件做出響應(yīng)范舀。

關(guān)于冒泡,事件對象中包含bubbles了罪、cancelBubble锭环、stopPropagation()和stopImmediatePropagation()這四個相關(guān)的屬性和方法。

1泊藕、屬性bubbles

bubbles屬性返回一個布爾值辅辩,表示當前事件是否會冒泡。該屬性為只讀屬性娃圆。

發(fā)生在文檔元素上的大部分事件都會冒泡玫锋,但focus、blur和scroll事件不會冒泡讼呢。所以撩鹿,除了這三個事件bubbles屬性返回false外,其他事件該屬性都為true悦屏。

<body>
<button id="btn">按鈕</button>
<input id="test">
<script>
    var btn=document.getElementById('btn');
    var test=document.getElementById('test');
    btn.onclick=function(ent){
        ent=ent || event;
        btn.innerHTML=ent.bubbles;
        console.log(ent.bubbles);
    };
    test.onfocus=function(ent){
        test.innerHTML=ent.bubbles;
        console.log(ent.bubbles);
    }
</script>
</body>

按鈕點擊后节沦,上面的值會變成true,輸入框聚焦后沒反應(yīng)窜管,但兩者終端都會輸出相應(yīng)的bubbles的值散劫。

2稚机、方法stopPropagation()

stopPropagation()方法表示取消事件的進一步捕獲或冒泡幕帆,無返回值,但IE8-瀏覽器不支持赖条。

<body>
<button id="btn" style="width: 100px;">按鈕</button>
<input id="test">
<script>
    var btn=document.getElementById('btn');
    var test=document.getElementById('test');
    btn.onclick=function(ent){
        ent=ent || event;
        test.value+='按鈕欄失乾、';
        // ent.stopPropagation();
    };
    document.body.onclick=function(ent){
        ent=ent || event;
        test.value+='文檔。';
    }
</script>
</body>

正常代碼這樣纬乍,從W3C事件流的說法碱茁,假設(shè)window->div這樣是從外到內(nèi),點擊事件被由外到內(nèi)分別捕捉仿贬,目標接收事件纽竣,再由內(nèi)到外冒泡響應(yīng)。

所以上面的代碼結(jié)果是在點擊按鈕之后,輸入框會顯示“按鈕欄蜓氨、文檔聋袋。”穴吹;那么如果把阻止冒泡的語句注釋刪去的話幽勒,響應(yīng)結(jié)果就會變成這樣“按鈕欄、”港令。

3啥容、方法stopImmediatePropagation()

stopImmediatePropagation()方法不僅可以取消事件的進一步捕獲或冒泡,而且可以阻止同一個事件的其他監(jiān)聽函數(shù)被調(diào)用顷霹,無返回值咪惠,但IE8-瀏覽器不支持。

<body>
<button id="btn" style="width: 100px;">按鈕</button>
<input id="test">
<script>
    var btn=document.getElementById('btn');
    var test=document.getElementById('test');
    btn.addEventListener('click', function(ent){
        ent=ent || event;
        test.value+='按鈕欄淋淀、';
        // ent.stopImmediatePropagation()
    }, false);
    btn.addEventListener('click', function(ent){
        ent=ent || event;
        btn.style.background='#ff0000';
    }, false);
    document.body.addEventListener('click', function(ent){
        ent=ent || event;
        test.value+='文檔硝逢。';
    }, false);
</script>
</body>

上面的代碼結(jié)果是在點擊按鈕之后,輸入框會顯示“按鈕欄绅喉、文檔渠鸽。”,且按鈕底色會變紅柴罐;那么如果把阻止冒泡的語句注釋刪去的話徽缚,響應(yīng)結(jié)果就會變成這樣“按鈕欄、”革屠,它既阻止了點擊事件向body層冒泡凿试,還阻止了同層監(jiān)聽點擊事件底色變化。

這里面我們可以知道似芝,事件是先注冊那婉,先調(diào)用的原則。

4党瓮、cancelBubble

cancelBubble屬性只能用于阻止冒泡详炬,無法阻止捕獲階段。該值可讀寫寞奸,默認值是false呛谜。當設(shè)置為true時,cancelBubble可以取消事件冒泡枪萄。該屬性全瀏覽器支持隐岛,但并不是標準寫法

<body>
<button id="btn" style="width: 100px;">按鈕</button>
<input id="test">
<script>
    var btn=document.getElementById('btn');
    var test=document.getElementById('test');
    btn.addEventListener('click', function(ent){
        ent=ent || event;
        test.value+='按鈕欄、';
        ent.cancelBubble=true;
    }, false);
    document.body.addEventListener('click', function(ent){
        ent=ent || event;
        test.value+='文檔瓷翻。';
    }, false);
</script>
</body>

當使用stopIPropagation()方法或stopImmediatePropagation()方法時聚凹,關(guān)于cancelBubble值的變化割坠,各瀏覽器表現(xiàn)不同。

  • chrome/safari/opera中妒牙,cancelBubble的值為false韭脊。
  • IE9+/firefox中,cancelBubble的值為true单旁。

兼容處理沪羔,阻止冒泡:

var handler = function(ent){
    ent=ent || event;
    if(ent.stopPropagation){
        ent.stopPropagation();
    }else{
        ent.cancelBubble = true;
    }
}

4.2.4 事件流:eventPhase屬性

eventPhase屬性返回一個整數(shù)值,表示事件目前所處的事件流階段象浑,但IE8-瀏覽器不支持蔫饰。

0表示事件沒有發(fā)生,1表示捕獲階段愉豺,2表示目標階段篓吁,3表示冒泡階段。

<body>
<button id="btn" style="width: 100px;">按鈕</button>
<script>
  document.body.addEventListener('click', function(ent){
      ent=ent || event;
      btn.innerHTML=ent.eventPhase;
  }, true);
</script>
</body>

效果為“按鈕”變成“1”蚪拦。
換腳本:

<script>
  var btn=document.getElementById('btn');
  btn.addEventListener('click', function(ent){
      ent=ent || event;
      btn.innerHTML=ent.eventPhase;
  }, false);
</script>

效果為“按鈕”變成“2”杖剪。

換腳本:

<script>
  document.body.addEventListener('click', function(ent){
      ent=ent || event;
      btn.innerHTML=ent.eventPhase;
  }, false);
</script>

效果為“按鈕”變成“3”。

這里大致可以看出W3C對事件流定義的三個階段驰贷。

4.2.5 取消默認行為

常見的瀏覽器默認行為有點擊鏈接后盛嘿,瀏覽器跳轉(zhuǎn)到指定頁面;或者按一下空格鍵括袒,頁面向下滾動一段距離次兆。

關(guān)于取消默認行為的屬性包括cancelable、defaultPrevented锹锰、preventDefault()和returnValue芥炭。

使用:

  • 在DOM0級事件處理程序中取消默認行為,使用returnValue恃慧、preventDefault()return false都有效

  • 在DOM2級事件處理程序中取消默認行為园蝠,使用return false無效

  • IE事件處理程序中取消默認行為,使用preventDefault()無效

1痢士、cancelable屬性

cancelable屬性返回一個布爾值彪薛,表示事件是否可以取消。該屬性為只讀屬性良瞧。返回true時陪汽,表示可以取消。否則褥蚯,表示不可取消。IE8-瀏覽器不支持况增。

<a id="test" href="#">鏈接</a>
<script>
var test=document.getElementById('test');
test.onclick= function(ent){
    ent=ent || event;
    test.innerHTML=e.cancelable;
}
</script>

效果:點擊“鏈接”變成“true”赞庶。

2、preventDefault()方法

preventDefault()方法取消瀏覽器對當前事件的默認行為澜薄,無返回值,IE8-瀏覽器不支持。

<a id="test" >鏈接</a>
<script>
  var test=document.getElementById('test');
  test.onclick= function(ent){
      ent=ent||event;
      ent.preventDefault();
  }
</script>

效果:不轉(zhuǎn)跳妒峦。

3、returnValue屬性

returnValue屬性可讀寫,默認值是true若债,但將其設(shè)置為false就可以取消事件的默認行為,與preventDefault()方法的作用相同,firefox和IE9+瀏覽器不支持泰讽。

做兼容處理:

var handler = function(ent){
  ent=ent || event;
  if(ent.preventDefault){
      ent.preventDefault();
  }else{
      ent.returnValue=false;
  }
}

4、return false

<script>
  var test=document.getElementById('test');
  test.onclick= function(ent){
    return false;
  }
</script>

效果:不轉(zhuǎn)跳。

5奥吩、defaultPrevented屬性

defaultPrevented屬性表示默認行為是否被阻止,返回true時表示被阻止端衰,返回false時楼誓,表示未被阻止主守。

<a id="test" >鏈接</a>
<script>
  var test=document.getElementById('test');
  test.onclick= function(ent){
    ent=ent || event;
    if(ent.preventDefault){
        ent.preventDefault();
    }else{
        ent.returnValue=false;
    }
    test.innerHTML=ent.defaultPrevented;
  }
</script>

效果:點擊“鏈接”變?yōu)椤皌rue”。

5、完整的簡單事件相關(guān)代碼

<script>
  var EventUtil={
    // 事件對象
    getEvent: function(event){
      return event||window.event;
    },
    // 事件目標節(jié)點
    getTarget: function(event){
      return event.target||event.srcElement;
    },
    // 阻止事件的默認行為
    preventDefault: function(){
      if(event.preventDefault){
        event.preventDefault();
      }else{
        event.returnValue=false;
      }
    },
    // 阻止向上冒泡
    stopPropagation: function(){
      if(event.stopPropagation){
          event.stopPropagation();
      }else{
          event.cancelBubble=true;
      }
    },
    // DOM2級添加事件  
    addHandler: function(element, type, handler){
      if(element.addEventListener){
          element.addEventListener(type, handler, false);
      }else if(element.attachEvent){
        element["e"+type]=function(){
          handler.call(element)
        };
        element.attachEvent("on"+type, element["e"+type]);
      }else{
        element["on"+type]=handler;
      }
    },
    // DOM2級移除事件    
    removeHandler: function(element, type, handler){
      if(element.removeEventListener){
        element.removeEventListener(type, handler, false);
      }else if(element.detachEvent){
        element.detachEvent("on"+type, element["e"+type]);
        element["e"+type]=null;
      }else{
        element["on"+type]=null;
      }
    }
  };
</script>

6、內(nèi)存泄漏

程序的運行需要內(nèi)存。只要程序提出要求,操作系統(tǒng)或者運行時(runtime)就必須供給內(nèi)存茎芋。
對于持續(xù)運行的服務(wù)進程(daemon)掏缎,必須及時釋放不再用到的內(nèi)存蒿秦。否則勺疼,內(nèi)存占用越來越高,輕則影響系統(tǒng)性能,重則導(dǎo)致進程崩潰勋功。

不再用到的內(nèi)存,沒有及時釋放,就叫做內(nèi)存泄漏(memory leak)。

6.1 垃圾回收機制

大多數(shù)語言提供自動內(nèi)存管理抽活,減輕程序員的負擔歇由,這被稱為"垃圾回收機制"(garbage collector)糊昙。

垃圾回收機制最常使用的方法叫做"引用計數(shù)"(reference counting):語言引擎有一張"引用表",保存了內(nèi)存里面所有的資源(通常是各種值)的引用次數(shù)。如果一個值的引用次數(shù)是0,就表示這個值不再用到了,因此可以將這塊內(nèi)存釋放。

let arr = [1, 2, 3, 4];
console.log('hello world');
arr = null;

數(shù)組[1, 2, 3, 4]是一個值,會占用內(nèi)存。變量arr是僅有的對這個值的引用充岛,因此引用次數(shù)為1。

如果增加最下面那行代碼,解除arr對[1, 2, 3, 4]引用伞鲫,這塊內(nèi)存就可以被垃圾回收機制釋放了儒搭。

JavaScript中最常用的垃圾收集方式是標記清除(mark-and-sweep)默穴。當變量進入環(huán)境(例如乙埃,在函數(shù)中聲明一個變量)時遇伞,就將這個變量標記為“進入環(huán)境”。從邏輯上講渐排,永遠不能釋放進入環(huán)境的變量所占的內(nèi)存炬太,因為只要執(zhí)行流進入相應(yīng)的環(huán)境,就可能用到它們驯耻。而當變量離開環(huán)境時亲族,這將其 標記為“離開環(huán)境”。雖然JavaScript 會自動垃圾收集可缚,但是如果我們的代碼寫法不當霎迫,會讓變量一直處于“進入環(huán)境”的狀態(tài),無法被回收城看。

6.2 意外的全局變量

JavaScript 處理未定義變量的方式比較寬松:未定義的變量會在全局對象創(chuàng)建一個新變量女气。在瀏覽器中,全局對象是 window 测柠。

可能一般寫了這段代碼:

function foo(arg) { 
  bar = "this is a hidden global variable"; 
}

然而這段代碼的執(zhí)行是這樣:

function foo(arg) { 
  window.bar = "this is a hidden global variable"; 
}

另一種意外導(dǎo)致定了了全局變量:

function foo() { 
    this.variable = "potential accidental global"; 
} 
// foo 調(diào)用自己炼鞠,this 指向了全局對象(window)缘滥,而不是 undefined 
foo();

在JavaScript文件頭部加上 'use strict',可以避免此類錯誤發(fā)生谒主。啟用嚴格模式解析 JavaScript 朝扼,避免意外的全局變量。

6.3 被遺忘的計時器或回調(diào)函數(shù)

var someResource=getData();
setInterval(function() {
  var node=document.getElementById('Node');
  if(node) {
    // 處理 node 和 someResource 
    node.innerHTML = JSON.stringify(someResource);
  }
}, 1000);

如果idNode的元素從 DOM 中移除,該定時器仍會存在,同時,因為回調(diào)函數(shù)中包含對 someResource的引用,定時器外面的someResource也不會被釋放霎肯。

6.4 脫離DOM的引用

保存DOM節(jié)點內(nèi)部數(shù)據(jù)結(jié)構(gòu)很有用擎颖。假如你想快速更新表格的幾行內(nèi)容,把每一行 DOM 存成字典(JSON鍵值對)或者數(shù)組很有意義观游。此時搂捧,同樣的DOM元素存在兩個引用:一個在 DOM 樹中,另一個在字典中懂缕。將來你決定刪除這些行時允跑,需要把兩個引用都清除。

  var elements = {
    button: document.getElementById('button'),
    image: document.getElementById('image'),
    text: document.getElementById('text')
  };

  function doStuff() {
    image.src = 'http://some.url/image';
    button.click();
    console.log(text.innerHTML);
    // 更多邏輯 
  }

  function removeButton() {
    // 按鈕是 body 的后代元素 
    document.body.removeChild(document.getElementById('button'));
    // 此時搪柑,仍舊存在一個全局的 #button 的引用 
    // elements 字典聋丝。button 元素仍舊在內(nèi)存中,不能被 GC 回收工碾。 
  }

然后還有'DOM樹'內(nèi)部或子節(jié)點的引用問題弱睦。假如你的JavaScript代碼中保存了表格某一個'td'的引用。將來決定刪除整個表格的時候渊额,直覺認為'GC'會回收除了已保存的'td'以外的其它節(jié)點况木。實際情況并非如此:此'td'是表格的子節(jié)點,子元素與父元素是引用關(guān)系端圈。由于代碼保留了'td'的引用焦读,導(dǎo)致整個表格仍待在內(nèi)存中。保存'DOM'元素引用的時候舱权,要小心謹慎矗晃。

6.5 閉包

閉包是 JavaScript 開發(fā)的一個關(guān)鍵方面:匿名函數(shù)可以訪問父級作用域的變量。

簡單來說是這個樣子的:

<script>
  var leaks=(function(){
    var leak='closure';
    return function(){
      console.log(leak);
    }
  })();
</script>

參考博文:一個意想不到的js內(nèi)存泄漏宴倍。

參考博文:JS常見4種內(nèi)存泄漏和chrome開發(fā)工具監(jiān)測介紹张症。

全文來源博文:javascript學(xué)習目錄

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鸵贬,一起剝皮案震驚了整個濱河市俗他,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌阔逼,老刑警劉巖兆衅,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡羡亩,警方通過查閱死者的電腦和手機摩疑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來畏铆,“玉大人雷袋,你說我怎么就攤上這事〈蔷樱” “怎么了楷怒?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長瓦灶。 經(jīng)常有香客問我鸠删,道長,這世上最難降的妖魔是什么倚搬? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任冶共,我火速辦了婚禮,結(jié)果婚禮上每界,老公的妹妹穿的比我還像新娘。我一直安慰自己家卖,他們只是感情好眨层,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著上荡,像睡著了一般趴樱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上酪捡,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天叁征,我揣著相機與錄音,去河邊找鬼逛薇。 笑死捺疼,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的永罚。 我是一名探鬼主播啤呼,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼呢袱!你這毒婦竟也來了官扣?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤羞福,失蹤者是張志新(化名)和其女友劉穎惕蹄,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡卖陵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年恋昼,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赶促。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡液肌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鸥滨,到底是詐尸還是另有隱情嗦哆,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布婿滓,位于F島的核電站老速,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏凸主。R本人自食惡果不足惜橘券,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望卿吐。 院中可真熱鬧旁舰,春花似錦、人聲如沸嗡官。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽衍腥。三九已至磺樱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間婆咸,已是汗流浹背竹捉。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留尚骄,地道東北人块差。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像乖仇,于是被迫代替她去往敵國和親憾儒。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容