引子
昨天晚上做一个项目,遇到的一个问题,这个项目是一个在线考试系统,新建一份试卷的页面,要添加一些试题策略。点击添加试题策略,弹出添加策略的页面,策略编辑好之后提交,添加策略页关闭,当前添加试题页面策略列表刷新。那么就遇到一个问题,在“添加试卷页”中点击“添加策略”按钮弹出添加策略页,添加策略后,父页面只能局部刷新(整体刷新会丢掉页面输入框未保存的数据)。
综上总结,浓缩成一句话啊,就是“父页面打开子页面,子页面完成操作后触发父页面的事件。”
过程
我在百度和必应里面搜一下,很多解决方案是用window.showModalDialog来实现的,因为它有返回值,可以根据返回值来实现。但是chrome37以后就不支持window.showModalDialog,考虑兼容性,就不能使用window.showModalDialog。所以说,只能用window.open打开,但是window.open没有返回值,所以只能在子页面中想办法触发父页面的事件。
当时第一反应想到的就是,在父页面写一个公用事件,让子页面调用,很傻的一个想法,这想法实在太不靠谱了(但是我觉得程序员还是要敢于想象),因为每个页面都是一个对象实例,你调用公用方法,都不知道是在作用那个页面,其次,被直接调用的必须是静态方法,静态方法又是不能对页面控件做直接操作的,如果不是静态方法,就需要new一个页面,完全没有意义。
接着想着从C#中来获取父页面,从而触发父页面的事件,找了一个发现,C#并没有好的方法能获取到父页面。那么就只能从js入手,我对js不是太了解,菜鸟都算不上的菜鸟,我的想法就是用js找到父页面(js在找父页面还是很方便的)。搜了一下,有下面几种方法:
window.opener.document在页面运行结果如下:
window.parent.document在页面运行结果如下:
发现:
window.opener.document获取的是父级页面。
window.parent.document获得的是本身,很奇怪了。
之后查阅了一些资料得出结论:
window.parent能获取一个框架的父窗口或父框架。顶层窗口的parent引用的是它本身。
window.opener引用的是window.open打开的页面的父页面。
opener即谁打开我的,比如A页面利用window.open弹出了B页面窗口,那么A页面所在窗口就是B页面的opener,在B页面通过opener对象可以访问A页面。
parent表示父窗口,比如一个A页面利用iframe或frame调用B页面,那么A页面所在窗口就是B页面的parent。
之后,就很顺利了的使用“window.opener.document.getElementById(‘Button1‘).click(); ”触发页面事件(我实现的是通过一个按钮来实现这个事件)。我们可以把这个按钮隐藏起来。
经过以上种种实验和思考,终于实现了通过子页面js触发父页面某个按钮的单击事件,实现子页面刷新父页面局部数据的方法。
结果
截图如下:
打开父页面,在文本框中随便输入一些内容(便于测试是不是仅仅刷新了局部)。
点击打开子页面:
点击刷新父级页局部
可以看出,父页面仅仅局部被刷新了。
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>父级页</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<label runat="server" id="label1" style="color:blue">
原始标记</label>
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<input type="button" value="打开子页面" onclick="window.open(‘WebForm2.aspx‘)" />
<div style="display: none">
<asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Button" />
</div>
</div>
</form>
</body>
</html>
using System;
namespace WebDemo
{
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
label1.InnerText = "被局部刷新了";
label1.Style.Add("color", "red");
}
}
}
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>子级页</title>
</head>
<body>
<form id="form1" runat="server">
<asp:Button ID="Button1" runat="server" Text="刷新父级页局部" OnClick="Button1_Click" />
</form>
</body>
</html>
using System;
namespace WebDemo
{
public partial class WebForm2 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
Response.Write("<script language=‘javascript‘>window.opener.document.getElementById(‘Button1‘).click(); </script>");
}
}
}
WebForm2.aspx.cs
以上都是转载部分。。。。
我的项目的实际情况和引子描述基本一致,原作者用的是window.open来弹出窗口。我用的是div包着iframe情况略有不同但是大同小异。
子页面触发父页面局部刷新按钮事件
protected void Button1_Click(object sender, System.EventArgs e)
{
ScriptManager.RegisterStartupScript(Page, this.GetType(), "msg", "parent.window.document.getElementById('txt_tx').value='" + Request.Form["xz"] + "';parent.window.document.getElementById('btnTixi').click();parent.layer.close(parent.layer.getFrameIndex(window.name))", true);
}
这里我用了layer框架的弹窗,并且给隐藏的textbox赋值,达到页面之间传值的效果。而父页面也是有一个隐藏的button等待子页面来触发,做局部刷新用
<div style="display: none;">
<asp:Button ID="btnTixi" runat="server" Text="Button" CausesValidation="false" OnClick="btnTixi_Click" />
</div>
protected void btnTixi_Click(object sender, EventArgs e) { xs_fw(this.txt_tx.Text.Trim()); }
这样就完成了,点击子页面上的某个按钮来触发父页面局部刷新