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;
}
结语
参考了多位大神的分析后,自己动手实现了,感觉还是不错的。继续加油,加油!