1. 程式人生 > >js視窗間通訊--postMessage

js視窗間通訊--postMessage

why

我們知道:瀏覽器限制不同視窗之間的通訊,除非同一個域名下的網頁。為了解決這一問題,HTML5新出了一個API: window.postMessage, 實現了不同域名的視窗通訊。

what

MDN上webAPI語法:
otherWindow.postMessage(message, targetOrigin)

  • otherWindow
    其他視窗的一個引用,比如iframe的contentWindow屬性、執行window.open返回的視窗物件、或者是命名過或數值索引的window.frames。

  • message
    將要傳送到其他 window的資料。將會被結構化克隆演算法序列化。這意味著你可不受什麼限制的安全傳送資料物件給目標視窗而無需自己序列化。

  • targetOrigin
    Specifies what the origin of otherWindow must be for the event to be dispatched, either as the literal string “*”;
    (這個引數不是很懂,大概意思是指定目標視窗的網址,我的例子用的是“*”)

message 事件的回撥函式為 receiveMessage,一旦收到其他視窗發來的資訊,receiveMessage 函式就會被呼叫。receiveMessage 函式接受一個 event 事件物件作為引數,該物件的 origin 屬性表示資訊的來源網址,如果該網址不符合要求,就立刻返回,不再進行下一步處理。
event.data屬性則包含了實際傳送過來的資訊; event 物件的屬性除了 origin 和 data,還有一個 source 屬性,指向向當前網頁傳送資訊的視窗物件。

how

下面是一個完整的例子,實現了兩個視窗間的通訊,做的有點像網頁版微信聊天的視窗—。—
效果展示:
視窗間通訊

原始碼, HTML部分:

//頁面a

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="keyword" content="post, message">
    <title>post message a</title>
    <link rel="stylesheet" type="text/css"
href="CSS/style.css"/>
<!-- // <script type="text/javascript" src="JS/utils.js" defer></script> --> <!-- // <script type="text/javascript" src="JS/ClassPM.js" defer></script> --> <script type="text/javascript" src="JS/IIFEPM.js" defer></script> <script type="text/javascript" src="JS/postMessage.js" defer></script> <script type="text/javascript" src="JS/moveWin.js" defer></script> </head> <body> <div id="win_div"> <div id="title_div"> <label id="name_lbl">Bob</label> <img src="photos/detail_icon.jpg" alt="detail infor icon" title="detail infor"/> </div> <div id="content_div"> <textarea id="text_content" readonly="true"></textarea> </div> <div id="input_div"> <textarea rows="1" id="sendtext_content"></textarea> <button id="send_btn" onclick="sendBtnClick()">傳送</button> </div> </div> </body> </html>

//頁面b

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="keyword" content="post, message">
    <title>post message b</title>
    <link rel="stylesheet" type="text/css" href="CSS/style.css"/>
    <!-- // <script type="text/javascript" src="JS/ClassPM.js" defer></script> -->
    <!-- // <script type="text/javascript" src="JS/utils.js" defer></script>     -->
    <script type="text/javascript" src="JS/IIFEPM.js" defer></script>
    <script type="text/javascript" src="JS/postMb.js" defer></script>
    <script type="text/javascript" src="JS/moveWin.js" defer></script>
</head>
<body>
    <div id="win_div">
        <div id="title_div">
            <label id="name_lbl">Amelia</label>
            <img src="photos/detail_icon.jpg" alt="detail infor icon" title="detail infor"/>
        </div>
        <div id="content_div">
            <textarea id="text_content" readonly="true"></textarea>         
        </div>
        <div id="input_div">
            <textarea rows="1" id="sendtext_content"></textarea>
            <button id="send_btn" onclick="sendBtnClick()">傳送</button>
        </div>
    </div>
</body>
</html>

//a 頁面js

// 引用IIFEPM.js
window.myname = "Bob: ";

var parentWin = window.opener;

function sendBtnClick(){
    parentWin.postMessage(window.myname + module1.sendtxtContent.value, '*');
    module1.displayMessage();
};  

// b頁面js


// 引用 IIFEPM.js
// 描述:視窗間通訊中通用的屬性和方法
    window.myname = "Amelia: "
    var newWindow = window.open('postMessage_b.html');
    function sendBtnClick(){
        newWindow.postMessage(window.myname + module1.sendtxtContent.value, '*');
        module1.displayMessage();
    };

//公用模組,實現視窗間通訊

// ************************************************************************
// * File Name: utils.js
// * Description: 視窗間通訊公用屬性和函式
// * Note:
// ************************************************************************

var module1 = (function(win){

    'use strict';

    var DEBUG = 1;
    if (DEBUG)
      var debug = function(s) { 
        console.log('<postMessage> ------: [utils.js] : ' + s + '\n'); 
      };
    else
      var debug = function(s) {};

    // ID選擇器
    var $ = function(selector){
        return document.getElementById(''+selector);
    }
    //-------------------------------------------------------------------------
    // 傳送訊息
    var sendtxtContent = $('sendtext_content');
    // 訊息內容
    var txtContent = $('text_content');

    // 接收訊息的回撥函式
    function receiveMessage(event){
        txtContent.value += event.data + "\n\n";            
        console.log(event.data);

        // 顯示在最後一行
        txtContent.scrollTop = txtContent.scrollHeight; 
    }
    // 新增監聽事件
    win.addEventListener("message", receiveMessage, false);


    function displayMessage(){
        txtContent.value += "我: "+sendtext_content.value + "\n\n";      
        sendtext_content.value = "";
        // 顯示在最後一行
        txtContent.scrollTop = txtContent.scrollHeight; 
    }

    // 按鍵Ctrl標誌
    var flag_ctrl = false;
    // 新增按鍵監聽事件
    win.addEventListener("keydown", handleKeyup, false);
    // 按鍵事件處理
    function handleKeyup(event){
        console.log(event);
        if(event.keyIdentifier === 'Control' || event.key === 'Control')
        {
            flag_ctrl = true;
        }
        else if(event.keyIdentifier === 'Enter' || event.key === 'Enter' && flag_ctrl){
            win.sendBtnClick();
        }
        else
        {
            flag_ctrl = false;
        }

    };

    return{
        sendtxtContent: sendtxtContent,
        txtContent: txtContent,
        $: $,
        debug: debug,
        displayMessage: displayMessage,
    };
})(window || {});
// 放大模式是擴充套件window的屬性;寬放大模式是引數可以為空


// 寬放大模式,基本可以實現模組化封裝;但是如果實現的功能較複雜,需要考慮使用標準的模組化框架,如requirejs,seajs



// 新增監聽事件
// window.addEventListener("message", PM.prototype.receiveMessage, false);

// 新增按鍵監聽事件
// window.addEventListener("keydown", PM.prototype.handleKeyup, false);  //此處繫結物件無效

//實現視窗拖拽功能

// 視窗移動

(function(){
    var winDiv_x0;  // 視窗左上角x座標
    var winDiv_y0;  // 視窗左上角y座標
    var down_x;     // 滑鼠點選初始x座標
    var down_y;     // 滑鼠點選初始y座標
    var cur_x;      // 滑鼠當前x座標
    var cur_y;      // 滑鼠當前y座標
    var down_flag = false;

    var titleDiv = module1.$('title_div'); //滑鼠點選移動的頭部
    var winDiv = module1.$('win_div');  //需要移動的視窗



    function mouseDown(event){
        down_x = event.screenX;
        down_y = event.screenY + document.documentElement.scrollTop;
        winDiv_x0 = winDiv.offsetLeft;
        winDiv_y0 = winDiv.offsetTop;       
        down_flag = true;
    };

    function mouseMove(event){
        if(down_flag){
            cur_x = event.screenX;
            cur_y = event.screenY + document.documentElement.scrollTop;
            moveDiv(cur_x - down_x, cur_y - down_y);            
        }
    };

    function mouseUp(event){
        if(down_flag){
            cur_x = event.screenX;
            cur_y = event.screenY + document.documentElement.scrollTop;
            moveDiv(cur_x - down_x, cur_y - down_y);    
            down_flag = false;      
        }



    };

    function mouseOut(){
        down_flag = false;
    };

    function moveDiv(dx, dy){
        winDiv.style.marginLeft = winDiv_x0 + dx + 'px';
        winDiv.style.marginTop = winDiv_y0 + dy + 'px';
    }

    titleDiv.addEventListener('mousedown', mouseDown, false); //事件控制代碼在冒泡階段執行
    titleDiv.addEventListener('mousemove', mouseMove, false);
    titleDiv.addEventListener('mouseup', mouseUp, false);
    titleDiv.addEventListener('mouseout', mouseOut, false);


// 雙引號會搜尋引號內的內容是不是有變數,有則輸出其值,沒有則輸出原有內容。
// 所以輸出純字串的時候用單引號比雙引號效率高,因為省去檢索的過程。        
})();

//樣式表

*{
    margin:0px;
    padding: 0px;
}

#win_div{
    /*background: rgb(223,223,223);*/
    border-color: gray;
    border-width: 1px;
    border-style: solid;
    width: 300px;
    height: 540px;
    margin-top: 100px;
    margin-left: 100px;

}

#title_div{
    background-color: black;
    width:100%;
    height:50px;
    color: white;
    line-height: 50px;
    cursor: move;
}

#title_div label{
    float: left;
    margin-left: 20px;
}


#title_div img{
    width: 30px;
    height: 30px;
    float: right;
    margin-top: 10px;
    margin-right: 10px;
    cursor: pointer;
}


#content_div{
    background-color: rgb(239, 239, 239);
    width: 100%;
    height: 450px;
}

#content_div textarea{
    width:100%;
    height: 450px;
    resize: none;
    font-size: 15px;
    font-family: '微軟雅黑';
    border: none;
    border-bottom: 1px solid gray;
    background-color: rgb(239, 239, 239);   
    overflow-x: hidden; 
}

#input_div{
    width: 100%;
    height: 40px;
}

#input_div textarea{
    float: left;
    width: 230px;
    height: 39px;
    resize: none;
    font-size: 20px;
    font-family: "Times New Roman",Georgia,Serif;
    border: none;
}

#input_div button{
    float: right;
    margin-right: 10px;
    color: white;
    background-color: rgb(45, 172, 67);
    width: 50px;
    height: 30px;
    margin-top: 5px;
    border-color: white;
}

結語

參考了多位大神的分析後,自己動手實現了,感覺還是不錯的。繼續加油,加油!