编程技术文章分享与教程

网站首页 > 技术文章 正文

第36节 window对象的窗口-JavaScript

hmc789 2024-11-12 11:41:33 技术文章 4 ℃

本内容是《Web前端开发之Javascript视频》的课件,请配合大师哥《Javascript》视频课程学习。

多窗口和窗体:

可以打开多个浏览器窗口,每个窗口都是独立的;

一个浏览器窗口可能包含多个标签页;每个标签页都是独立的上下文,都是独立的window对象,而且相互之间互不干扰;

但是窗口也并不总是和其他窗口完全没有关系;一个窗口或标签页中的脚本可以打开新的窗口或标签页,如此,这些多窗口或标签页就可以互相操作;

打开窗口:

window.open()方法,打开一个新的浏览器窗口、标签页,导航到一个指定的URL;

语法:window.open(url,name,features,replace);url为打开新窗口的url,name为窗口目标,features设置窗口特性参数列表,replace为Boolean值,指定是否用新窗口替换当前页面;

一般使用第一个参数,如果也省略该参数或使用空字符串,则会打开一个空页面的URL about:blank;

如果使用第二个参数,而且该参数是已有窗口或框架的名称,就会在该窗口或框架中加载指定的URL;否则弹出新窗口,并将新窗口的name命名为该参数,如果省略此参数,则会使用指定的“_blank”打开一个新的、未命名的窗口;

第二个参数也可以是:_self、_parent、_top、_blank;

窗口的名字也可作为<a>和<form>元素上target属性的值,用来表示引用的文档或提交处理的页面;

<script>
window.open("https://www.zeronetwork.cn/","myWindow");
</script>
<a href="https://www.google.cn/" target="myWindow">baidu.com</a>

窗口特性属性:

第三个参数是一个逗号分隔的设置字符串,表示在新窗口中都显示哪些特性;如果省略,则以默认的形式呈现;

  • fullscreen:yes / no,是否最大化;仅限IE;
  • height:数值,新窗口的高,不能小于100;
  • left:数值,新窗口的左坐标,不能是负值;
  • location:yes / no,是否显示地址栏;不同浏览器的默认值不同;设置为no,地址栏有可能会隐藏,也有可能会被禁用;
  • menubar:yes / no,是否显示菜单栏,默认为no;
  • resizable:yes / no,是否能改变大小,默认为no ;
  • scrollbars:yes / no,如果内容区域大于视口,是否允许滚动,默认为no;
  • status:yes / no,是否显示状态栏,默认为no;
  • toolbar:yes / no,是否显示工具栏,默认为no;
  • top:数值,新窗口的上坐标,不能是负值;
  • width:数值,新窗口的宽,不能小于100;

字符串格式为:设置使用键值对,且全部使用逗号分隔,而且不能有空格;

window.open("https://www.zeronetwork.cn/","myWindow",
            "width=400,height=400,top=50,left=50,resizable=yes");

显式指定这些特性,打开的应该是新窗口,而不是新标签页;这个参数是非标准的,而且HTML5也主张浏览器应该忽略它;

另外,出于安全考虑,浏览器包含对可能指定的功能的限制,如,通常不允许指定一个太小的或者位于屏幕之外的窗口,并且一些浏览器不允许创建一个没有状态栏的窗口;

第四个参数只有在设置了第二个参数命名的是一个已存在的窗口时才有用;它是一个布尔值,声明了由第一个参数指定的URL是替换掉窗口浏览历史的当前记录(true)还是应该在窗口浏览历史中创建一个新的记录(false),默认为false;

window.open()会返回一个window对象,用于操作新创建的窗口;该引用可以操作新窗口的内容,从而就可以在一个窗口中控制另一个窗口的内容,例如向一个新开的浏览器窗口中输出内容;

var w = window.open();  // 打开一个新的空白窗口
w.document.write("<h2>零点网络</h2>");
w.alert("详情请进入https://www.zeronetwork.cn/");
w.location = "https://www.zeronetwork.cn/";

某些浏览器在默认情况下可能不允许针对主浏览器窗口调整大小或移动,但不允许针对通过open()创建的窗口调整大小或移动,如:

var newWindow = window.open("https://www.zeronetwork.cn/","myWindow",
            "width=400,height=400,top=50,left=50,resizable=yes");
newWindow.resizeTo(600,600);
newWindow.moveTo(200,200);

注:有些浏览器默认不允许这样操作;

窗口opener属性:

opener属性是新窗口对打开它的原始窗口的引用;即指向调用window.open()的窗口或框架;但只在弹出窗口的最外层window对象(top)中有定义;

var newWindow = window.open("https://www.zeronetwork.cn/","myWindow",
            "width=400,height=400,top=50,left=50,resizable=yes");
console.log(newWindow.opener === window); // true
 
<!-- 主窗口 -->
<input type="text" id="selectCity" placeholder="选择" />
<script>
var selectCity = document.getElementById("selectCity");
selectCity.onclick = function(){
    var newWin = window.open("select.html","newWin","width=400,height=400");
}
</script>
 
<!-- 新窗口 -->
<select id="city">
    <option value="beijing">北京</option>
    <option value="nanjing">南京</option>
    <option value="anhui">安徽</option>
</select>
<script>
var city = document.getElementById("city");
city.onchange = function(){
    window.opener.document.getElementById("selectCity").value = city.options[city.selectedIndex].value;
    window.close();
}
</script>

有些浏览器(如IE8和Chrome)会在独立的进程中运行每个标签页,当一个标签页打开另一个标签页时,如果两个window对象之间需要彼此通信,那么新标签页就不能运行在独立的进程中。在Chrome中,将新创建的标签页的opener属性设置为null,即表示在单独的进程中运行新标签页。

newWindow.opener = null;

标签页之间的联系一旦切断,将没有办法恢复。

window.close()关闭窗口:

对于主窗口,如果没有得到用户的允许是不能关闭它的;但弹出窗口可以不经用户允许可以关闭自己;

窗口关闭后,窗口的引用仍然存在,可以使用window.closed属性检测,但在实际场景中没有多大用处;

function openWin(){
    var newWindow = window.open("https://www.zeronetwork.cn/","myWindow",
            "width=400,height=400,top=50,left=50,resizable=yes");
    newWindow.document.write("<h2>零点网络</h2>");
    function closeWin(){
        newWindow.close();
        //alert(newWindow.closed);
        if(newWindow.close)
            alert("已关闭");
    }
    setTimeout(closeWin,3000);
}
openWin();

注:document对象也有close()方法,为了避免混淆,所以调用close()方法时,要显式调用,即使用window.close();

安全限制:

大多数浏览器针对弹出窗口实施了多方面的安全限制,如:不允许在屏幕之外创建弹出窗口,不允许将弹出窗口移动到屏幕之外,不允许关闭状态栏等;不允许关闭地址栏,默认情况下不允许移动弹出窗口或调整其大小;或者部分浏览器不支持修改状态栏,始终显示地址栏等;

通常,对于open()方法只有当用户手动单击按钮或超链接时才会调用;如果尝试在浏览器初始载入时开启一个弹出窗口时,通常会被屏蔽;

弹出窗口屏蔽程序:

大多数浏览器都内置有弹出窗口屏蔽程序,如果没有内置,可以安装第三方实用工具;内置屏蔽会使window.open可能返回null,第三方会返回错误;通过需要检测其返回值;

// 内置的屏蔽程序
var newWin = window.open("https://www.zeronetwork.cn/","_blank");
if(newWin == null){
    alert("弹窗被阻止!");
}
 
// 第三方
var blocked = false;
try{
    var newWin = window.open("https://www.zeronetwork.cn/","_blank");
    if(newWin == null)
        blocked = true;
}catch(e){
    blocked = true;
}
if(blocked){
    alert("弹窗被屏蔽");
}

弹出窗口通信:

主窗口向新窗口传值,直接为新窗口window对象添加成员,如:

// 主窗口
var newWin = window.open("select.html","_blank");
var person = {
    name:"wangwei",
    sex: true,
    age: 18
}
newWin.person = person;
 
// 新窗口
document.write("姓名:",person.name);
document.write("性别:",person.sex);
document.write("年龄:",person.age);

新窗口向主窗口传值,通过window.opener获取原始窗口的属性或者html元素;

<!-- 主窗口 -->
<div id="mydiv"></div>
<script>
var newWin = window.open("select.html","_blank");
function showDiv(str){
    var mydiv = document.getElementById("mydiv");
    mydiv.innerHTML = str;
}
</script>
 
<script>
// 新窗口
var str = "零点网络";
window.opener.showDiv(str);
window.close();
</script>

通过普通的get传值;

// 主窗口
function putId(id){
    window.open("select.html?id=" + encodeURIComponent(id),"putWin");
}
putId("1002");
 
// 新窗口
var id = location.search;
id = id.split("=");
console.log(id[1]);
document.write(decodeURIComponent(id[1]));

框架窗口:

如果页面使用了框架集合(包含frameset和iframe),则每个框架都由它自己的window对象表示,并存放在frames集合中;

与相互独立的标签页或窗口不同,框架窗口之间并不是相互独立的;

每个window对象都有一个name属性,其中包含框架的名称;

<frameset rows="100,*">
    <frame src="top.html" name="topFrame" />
    <frameset cols="50%,50%">
        <frame src="left.html" name="leftFrame" />
        <frame src="right.html" name="rightFrame" />
    </frameset>
</frameset>

在frames集合中,可用数字(下标从0开始,从左到右,从上到下)访问;即可以使用frames[0]表示第1个子窗口、frames[1]表示第2个子窗口;

可使用名称对框架进行访问,该名称就是该框架的name属性,如 window.frames[“topFrame”]引用;

也可以window.topFrame 使用架框的名字访问;

window.onload = function(){
    console.log(frames);
var topFrame = frames[0];
    console.log(topFrame);
    console.log(topFrame.name);
    var leftFrame = window.frames["leftFrame"];
    console.log(leftFrame.name);
    console.log(window.rightFrame.name);
}

可使用frames.length 取得框架集合长度;

如果是内联框架,那就更简单了,如:

<iframe src="right.html" name="myFrame" width="200" height="200"></iframe>
<script>
    console.log(frames);
    console.log(frames[0].name);
</script>

对于顶级窗口的window对象来说,可以看作为由若干个子窗口组成的窗口数组,也就是说可以把window对象当作为窗口的集合,如:

    console.log(window.length);
    console.log(window[0]);

但在实际场景中,最好还是使用frames来代替window,因为frames显得更清晰些;

对于<iframe>元素,如果设置了id,也可以通过document.getElementById()方法获取,如:

var myframe = document.getElementById("myframe");
console.log(myframe);
myframe.src = "one.html";

<iframe>元素有contentWindow属性,引用该窗体的window对象,所以此窗体的window对象就是:

var mywin = document.getElementById("myframe").contentWindow;
console.log(mywin);
mywin.document.write("是内联框架window对象");

可以进行反向操作,通过window对象的frameElement属性,来获取该窗体的<iframe>元素;表示顶级窗口的window对象的frameElement属性为null,窗体中的window对象的frameElement属性不是null;

var myframe = document.getElementById("myframe");
var mywin = myframe.contentWindow;
console.log(mywin.frameElement);
console.log(mywin.frameElement === myframe);
console.log(window.frameElement);
 
// 在one.html会返回<iframe id="myframe" src="one.html"></iframe>
console.log(window.frameElement);

尽管可以通过document.getElementById()和contentWindow属性来获取窗口中的子窗体的引用,但在实际场景中,用的还是比较少,主要还是使用frames属性来访问;

top对象:

可以使用top引用最顶层(外层)框架,就是浏览器窗口;使用它可以确保在一个框架中正确的访问另一个框架;

// 框架集页面
window.onload = function(){
    console.log(frames);
    console.log(top);
console.log(top === frames);  // true
console.log(top === window);  // true
    console.log(frames === window);  // true
}
// 框架页面
console.log("topFrame:",frames);
console.log("topFrame:",top);
console.log("topFrame:",top === frames);  // false

因此,使用top访问框架时,也可以如下:

    console.log(top[0].name);
    console.log(top["topFrame"].name);
    console.log(top.topFrame.name);
 
    console.log(top.frames[0].name);
    console.log(top.frames["topFrame"].name);
console.log(top.frames === frames);  // true

parent对象:

与top相对的另一个window对象是parent,其指的当前框架的直接上层框架,即父框架;

parent在不同的位置指不同的对象;在某些情况下,parent有可能等于top,但在没有框架的情况下,parent一定等于top(此时,它们都等于window),如:

<!-- frameset.html -->
<frameset rows="100,*">
    <frame src="top.html" name="topFrame" />
    <frameset cols="50%,50%">
        <frame src="left.html" name="leftFrame" />
        <frame src="right.html" name="rightFrame" />
    </frameset>
</frameset>
<!-- right.html -->
<frameset cols="50%,50%">
    <frame src="one.html" name="oneFrame" />
    <frame src="two.html" name="twoFrame" />
</frameset>

// one.html中的代码
var parentFrame = window.parent;
console.log("从one.html中访问:",parentFrame.name);  // rightFrame
parentFrame.frames[1].document.write("<h2>框架访问</h2>");
 
// top.html中的代码
var parentFrame = window.parent;
console.log("top:", parentFrame.name);
console.log("top:", parentFrame.length);
console.log("top:", parentFrame.frames.length);
console.log("top:", parentFrame === top);  // true
console.log("top:", parentFrame.frames[1].name);
parentFrame.frames[1].document.write("<h2>是left页面吗?</h2>");

注:除非最顶层的窗口是通过window.open()打开的,否则其window对象的name属性不会包含任何值;

self对象:

指向当前window自身,即self和window可以互换使用;

引入self的目的只是为了与top和parent对象对应起来;

对于顶级窗口,parent == self; // true

注:所有的这些对象都是window对象的属性,可以通过window.parent、window.top等形式访问;同时,这也意味着可以将不同层次的window对象连接起来,如:window.parent.parent.frames[0];

窗口交互:

对于一个复杂的框架,窗口之间可以相互访问,它主要包括对框架自身的引用、父窗口对子窗口的引用、子窗口对父窗口及其他窗口的引用、对顶级窗口的引用;

在使用框架的情况下,浏览器中会存在多个Global对象,即每个窗口都会有自己的执行上下文,在每个框架中定义的全局变量会自动成为框架中window对象的属性;

<!-- right.html -->
<div id="myDiv"></div>
<div id="imgDiv"></div>
<script>
function showImg(){
    var imgDiv = document.getElementById("imgDiv");
    var img = document.createElement("img");
    img.src = "images/1.jpg";
    imgDiv.appendChild(img);
}
</script>
 
// 框架集页面
window.onload = showDiv;
function showDiv(){
    var rightFrame = parent[2];
    var myDiv = rightFrame.document.getElementById("myDiv");
    myDiv.innerHTML = "<h2>零点网络</h2>";
}
 
<!-- top.html -->
<h2>Top页面</h2>
<div><button id="showBtn">显示图片</button>
    <button id="closeBtn">关闭图片</button></div>
<script>
window.onload = function(){
    var showBtn = document.getElementById("showBtn");
    var closeBtn = document.getElementById("closeBtn");
    var rightFrame = top[2];
    showBtn.onclick = rightFrame.showImg;
    closeBtn.onclick = closeImg;
}
function closeImg(){
    var rightFrame = top.rightFrame;
    var imgDiv = rightFrame.document.getElementById("imgDiv");
    imgDiv.innerHTML = "";
}
</script>

对于构造函数,它也是函数,所以当用构造函数和相关的原型对象定义一个自定义类时,这个类只在一个单独的窗口中定义;子窗口也可以引用这个类;

对于内置类来说,和自定义类就有所不同了;内置的类都会在所有的窗口中自动预定义,即每个窗口的window对象都包含原生类型的构造函数,因此每个框架一套自己的构造函数,这些构造函数一一对应,但并不相等;如:top.Object并不等于top.frames[0].Object,这个问题会影响到对跨框架传递的对象使用instanceof操作符;

WindowProxy对象:

window对象是客户端Javascript的全局变量;但是从技术上来看,并不是这样的;Web浏览器每次向窗口或窗体中载入新的内容,它都会开始一个新的JavaScript执行上下文,包含一个新创建的全局对象;但是当多个窗口或窗体在使用时,有一个重要的概念,尽管窗体或窗口载入了新的文档,但是引用窗体或窗口的window对象还仍然是一个有效的引用;

所以客户端Javascript有两个重要的对象;客户端全局对象处于作用域链的顶级,并且是全局变量和函数所定义的地方;事实上,全局对象会在窗口或窗体载入新内容时被替换;

而我们称之为“window对象”的对象实际上不是全局对象,而是全局对象的一个代理;每次查询或设置window对象的属性时,就会在窗口或窗体的当前全局对象上查询或设置相同的属性;

HTML5规范称这个代理对象为WindowProxy;由于它的代理行为,除了有更长的生命周期之外,代理对象表现得像真正的全局对象;如果可以比较两个对象,那么区分它们会很困难;但是事实上,没有办法可以引用到真正的客户端全局对象;全局对象处于作用域链的顶端,但是window、self、top、parent以及窗体的属性全部返回代理对象;window.open()也返回代理对象;甚至顶级函数里this的值也是代理对象,而不是真正的全局对象;


Tags:

标签列表
最新留言