对于完全基于 Ajax 的网站 —— RIA 的另一种形式,情况也是如此。允许用户与一个页面进行多次交互的网站很容易受到后退按钮的困扰,或者受到任何历史记录按钮的困扰(就此而言)。前进和重载按钮的问题与后退按钮的问题一样。
Web 浏览器内置的内部历史记录机制是一个不可逃避的问题。出于安全的原因,开发人员不能篡改浏览器历史记录或者任何相关按钮。还有可用性的问题。设想一下,如果后退按钮突然弹出一个神秘的警告提示或者用户被打发到一个新的网站上去,用户该是多么困惑。
构建历史堆栈
虽然不能改变浏览器历史记录,但是可以自己构建一个在 RIA 中使用的历史记录。显然,它在某种程度上应该与浏览器的标准导航工具分开,但正如前面所说的,富应用程序在一定程度上背离了 Web 的页面到页面的标准模式。
我们将建立一个堆栈来管理应用程序的历史事件记录,也就是说存储一个列表,在表的最后添加元素。堆栈用于按照后进先出(LIFO)的顺序存储数据。虽然回退的时候并没有删除堆栈顶部的数据,但这个模型跟我们的需要非常接近。在 JavaScript 中,堆栈可以用数组来管理。
与堆栈在一起的还有一个指针,指示我们在堆栈中的当前位置。当我们在应用程序中单击的时候,新的事件将被压入堆栈顶部,指针指向最后添加的元素。单击应用程序的后退和前进按钮时,不会在堆栈中添加新的事件,而是移动堆栈的指针。
想一想使用后退按钮时历史堆栈中会发生什么:浏览器返回上一次查看的页面,原来不能用的前进按钮突然之间变得可用了。浏览新的页面时,前进按钮再次变成灰色。浏览器历史记录中较晚保存的元素将被弹出堆栈,新的事件被压入堆栈顶部。我们将在自己创建的历史堆栈中再现这种行为。
我们的目标是创建一组可用的历史记录按钮:后退、前进和刷新,如图 1 所示。
图 1. 后退、前进和刷新的历史记录按钮显示在左侧,不可用状态显示在右侧
可重用的设计
JavaScript 使用非常宽松的方法创建对象和类,但仍然能够建立可重用的代码。首先列出历史堆栈需要的功能,然后用 JavaScript 建立堆栈模型。在把历史堆栈集成到相册应用程序之前,首先要建立一个简单的页面来测试其功能。这样做有两方面的好处:测试页有助于将精力集中到开发和测试类的核心功能上,建立单独的测试页可以避免混淆历史堆栈和相册的功能,从而确保可重用性。
用 cookie 缓冲
我们需要应用程序的历史记录在整个浏览器会话中都存在。只要用户仍在查看相册页面,历史堆栈对象就会一直存在。每当发生更改的时候,这个类就会将整个历史记录复制到浏览器 cookie 中。如果用户在同一个浏览器会话中离开该页之后又返回,那么将返回他离开该应用程序时所在的同一个位置。
编写类
我们来看看历史堆栈中需要存储的数据或属性。前面已经讨论了堆栈(数组)和指针。stack_limit 属性可以防止因为数据过多而造成的 cookie 溢出(参见清单 1)。在实践中,我们希望在删除最老的记录之前能够存储 40-50 个事件。出于测试的目的,我们将该值设置为 15。
清单 1. 历史堆栈的构造,包括类的属性
function HistoryStack () { this.stack = new Array(); this.current = -1; this.stack_limit = 15; } |
除了这三个属性外,该类还需要一些方法来添加元素、检索堆栈数据以及将堆栈数据保存到浏览器 cookie 中。首先看一看 addResource() 方法,它用于将记录压入历史堆栈的堆栈顶部(参见清单 2)。注意,如果堆栈的长度超过了 stack_limit,那么最老的记录将从堆栈中移走。
清单 2. addResource() 方法,向历史堆栈的堆栈顶部添加记录
HistoryStack.prototype.addResource = function(resource) { if (this.stack.length > 0) { this.stack = this.stack.slice(0, this.current + 1); } this.stack.push(resource); while (this.stack.length > this.stack_limit) { this.stack.shift(); } this.current = this.stack.length - 1; this.save(); }; |
给历史堆栈添加的以下三个方法用于从该类中获取信息(参见清单 3)。getCurrent() 返回堆栈指针指向的当前记录,这在堆栈中导航的时候非常有用。hasPrev() 和 hasNext() 方法返回 Boolean 值,告诉我们当前记录之前或之后是否还有记录,或者指示我们到达了堆栈顶部或堆栈尾部。这些方法很简单,但是确定后退和前进按钮的状态时很有用。
清单 3. 历史堆栈定义的方法
HistoryStack.prototype.addResource = function(resource) HistoryStack.prototype.getCurrent = function () { return this.stack[this.current]; };
HistoryStack.prototype.hasPrev = function() { return (this.current > 0); };
HistoryStack.prototype.hasNext = function() { return (this.current < this.stack.length - 1 && this.current > -1); };
|
回首页
现在就可以向历史堆栈中添加记录并确定所在的位置了。但还是无法在堆栈中导航。清单 4 中定义的 go() 方法允许我们在堆栈中来回移动。通过传递正或负的增量就可以在堆栈中向前或向后移动。这与 JavaScript 内置的 location.go() 方法类似。既然模仿内置功能,为何不根据这些已有的方法建立模型呢?
此外,我们还可用该方法实现刷新功能。可以通过传递正或负的参数在堆栈中导航。传递零时则会刷新当前页面。
清单 4. 历史堆栈的 go() 方法
HistoryStack.prototype.go = function(increment) { // Go back... if (increment < 0) { this.current = Math.max(0, this.current + increment);
// Go forward... } else if (increment > 0) { this.current = Math.min(this.stack.length - 1, this.current + increment);
// Reload... } else { location.reload(); }
this.save(); };
|
到目前为止,只要 HistoryStack 对象存在于当前文档中,这个新建的类就能正常工作。我们已经讨论了刷新页面会造成数据丢失的问题,现在来解决它。清单 5 中添加了在浏览器 cookie 中设置和访问数据的方法。所要做的只是设置每个 cookie 的名称值对。因为只需要在浏览器会话中保存 cookie,而不需要设置有效期。为了简化示例,我们不考虑其他参数,如 secure、domain 和 path。
注意:如果该类需要对 cookie 做复杂处理,更明智的办法是使用完全独立的 cookie 管理类。建立和读取 cookie 有点偏离历史堆栈的正题。如果 JavaScript 允许指定方法和属性访问的作用域,也可以将这些方法设成私有的。
清单 5. 建立和访问浏览器 cookie 的方法
HistoryStack.prototype.setCookie = function(name, value) { var cookie_str = name + "=" + escape(value); document.cookie = cookie_str; };
HistoryStack.prototype.getCookie = function(name) { if (!name) return '';
var raw_cookies, tmp, i; var cookies = new Array();
raw_cookies = document.cookie.split('; '); for (i=0; i < raw_cookies.length; i++) { tmp = raw_cookies[i].split('='); cookies[tmp[0]] = unescape(tmp[1]); }
if (cookies[name] != null) { return cookies[name]; } else { return ''; } };
|
定义了管理任何 cookie 的方法之后,可以编写另外两个类专门处理历史堆栈的类。save() 方法将堆栈转化成字符串并保存到 cookie 中,load() 重新将字符串解析成用于管理历史堆栈的数组(参见清单 6)。
清单 6. save() 和 load() 方法
HistoryStack.prototype.save = function() { this.setCookie('CHStack', this.stack.toString()); this.setCookie('CHCurrent', this.current); };
HistoryStack.prototype.load = function() { var tmp_stack = this.getCookie('CHStack'); if (tmp_stack != '') { this.stack = tmp_stack.split(','); }
var tmp_current = parseInt(this.getCookie('CHCurrent')); if (tmp_current >= -1) { this.current = tmp_current; } };
|
回首页
测试类
可以用简单的 HTML 页面和一些 JavaScript 来测试完成的类。测试页面将在上方显示历史记录按钮,只有活动的按钮是突出显示并且可以单击的。我们没有建立复杂的测试应用程序,该页面在每次单击链接时仅仅生成随机数。这些数字就是记录到历史堆栈中的事件。堆栈也在页面上显示,指针标记的当前记录用粗体显示。
清单 7. 测试历史堆栈的简单 HTML 页面
<html> <head> <title></title> </head>
<body>
<div id="historybuttons"></div> <div> <a href="#" onclick="do_add(); return false;">Add Random Resource</a> </div> <div id="output" style="margin-top:40px;"></div>
</body> </html>
|
在该 HTML 页面的头部需要添加清单 8 所示的 JavaScript 代码。这段代码首先实例化一个新的历史堆栈对象,并载入可能已经保存到浏览器 cookie 中的所有数据。
我们定义了四个 do_*() 函数,这些事件处理程序将添加到后退、前进和刷新按钮的链接中,此外还有 Add Random Resource 链接,如清单 7 所示。
display() 函数检查历史记录对象的当前状态,并为历史记录按钮生成 HTML。它还生成历史记录中存储的项目列表。
清单 8. 集成历史记录类和测试页面的 JavaScript
<script type="text/javascript" src="history.js"></script> <script type="text/javascript">
var myHistory = new HistoryStack(); myHistory.load();
function do_add() { var num = Math.round(Math.random() * 1000); myHistory.addResource(num); display(); return false; }
function do_back() { myHistory.go(-1); display(); }
function do_forward() { myHistory.go(1); display(); }
function do_reload() { myHistory.go(0); }
function display() { // Display history buttons var str = ''; if (myHistory.hasPrev()) { str += '<a href="#" onclick="do_back(); return false;">' + '<img src="icons/back_on.gif" alt="Back" /></a> '; } else { str += '<img src="icons/back_off.gif" alt="" /> '; } if (myHistory.hasNext()) { str += '<a href="#" onclick="do_forward(); return false;">' + '<img src="icons/forward_on.gif" alt="Forward" />' + '</a> '; } else { str += '<img src="icons/forward_off.gif" alt="" /> '; } str += '<a href="#" onclick="do_reload(); return false;">' + '<img src="icons/reload.gif" alt="Reload" /></a>'; document.getElementById("historybuttons").innerHTML = str;
// Display the current history stack, highlighting the current // position. var str = '<div>History:</div>'; for (i=0; i < myHistory.stack.length; i++) { if (i == myHistory.current) { str += '<div><b>' + myHistory.stack[i] + '</b></div>'; } else { str += '<div>' + myHistory.stack[i] + '</div>'; } } document.getElementById("output").innerHTML = str; }
window.onload = function () { display(); }; </script>
|
运行该测试页面,可以看到历史记录按钮反映了历史堆栈的状态(见图 2)。比如,第一次加载页面时,按钮都是灰色的。向堆栈中添加一些记录后,后退按钮就变成活动的了。如果在堆栈中回退,前进按钮就变亮了。还要注意的是,如果单击几次后退然后再单击 Add,那么堆栈会被截掉一部分,新的事件 被压入缩短的堆栈顶部。
图 2. 历史堆栈的测试页面
测试完该类后,就可以进入最激动人心的阶段了。
回首页
集成历史记录对象和相册
我们将从第 1 部分留下的问题开始,直接从相册页面调用历史堆栈。不需要修改任何 PHP 文件。
首先需要添加一个 div 标记来存放历史记录按钮。如清单 7 所示。
<div id="historybuttons"></div> |
历史堆栈代码被保存到一个 .js 文件中,该文件将链接到相册页面。
<script type="text/javascript" src="history.js"></script> |
需要实例化历史堆栈对象并从缓冲区加载它们。这些操作可以添加到相册页面上已有脚本的前面。
var myHistory = new HistoryStack(); myHistory.load(); |
在针对历史堆栈的测试应用程序中,只存储随机数作为事件。我们可以在历史记录中存储需要的任何信息,但是要记住,当用户单击应用程序的后退按钮时,还要确定历史堆栈中的内容是什么。应用程序只有两个动作与 x_get_table() 和 x_get_image() 函数有关。因此对于每个表链接,可以存储名称 table 再加上 start 和 step 值作为事件标识符,比如 table-10-5。类似地,可以存储名称 image 和将被查看图像的 index,如 image-20。
在第 1 部分中,相册中的每个链接都是由 get_table_link() 和 get_image_link() 两个函数生成的。通过编辑这些函数,可以在调用 Sajax 函数之前让该函数先调用历史堆栈。清单 9 以粗体显示了这些变化。
清单 9. get_table_link() 和 get_image_link() 函数的更新版本
function get_table_link ( $title, $start, $step ) { $link = "myHistory.addResource('table-$start-$step'); " ."x_get_table($start, $step, to_window); " ."return false;"; return '<a href="#" onclick="' . $link . '">' . $title .'</a>'; } function get_image_link ( $title, $index ) { $link = "myHistory.addResource('image-$index'); " ."x_get_image($index, to_window); " ."return false;"; return '<a href="#" onclick="' . $link . '">' . $title . '</a>'; } |
当应用程序进行 Sajax 调用时,to_window() 作为回调函数在页面上重新生成 HTML。在测试应用程序中,我们用函数 display()(清单 8)完成了两项任务:更新页面输出和更新历史记录按钮的状态。现在将在已有的 to_window() 函数体中添加下列函数调用:
display_history_buttons(); |
该函数的定义如清单 10 所示。
清单 10. display_history_buttons() 函数
function display_history_buttons() { var str = ''; if (myHistory.hasPrev()) { str += '<a href="#" onclick="do_back(); return false;"> <img src="icons/back_on.gif" alt="Back" /></a>'; } else { str += '<img src="icons/back_off.gif" alt="" />'; } if (myHistory.hasNext()) { str += '<a href="#" onclick="do_forward(); return false;"> <img src="icons/forward_on.gif" alt="Forward" /></a>'; } else { str += '<img src="icons/forward_off.gif" alt="" />'; } str += '<a href="#" onclick="do_reload(); return false;"> <img src="icons/reload.gif" alt="Reload" /></a>'; document.getElementById("historybuttons").innerHTML = str; }
|
在开始跟踪相册应用程序的历史记录之前,只需要在页面加载过程中调用 x_get_table() 函数即可。这样就可以调用通过 Sajax 显示的初始表。
现在已经有了历史堆栈,但是我们不希望每次打开该应用程序时都要从头开始。相反,我们希望从离开的地方开始。因此需要添加 load_current() 函数以扩展应用程序,加载页面时会调用该函数。添加后退和前进按钮处理程序时,还将调用该函数,根据保存到历史堆栈中的事件 ID 来更新页面。
清单 11. load_current() 函数
function load_current() { // No existing history. if (myHistory.stack.length == 0) { x_get_table(to_window); myHistory.addResource('table-0-5');
// Load from history. } else { var current = myHistory.getCurrent(); var params = current.split('-'); if (params[0] == 'table') { x_get_table(params[1], params[2], to_window); } else if (params[0] == 'image') { x_get_image(params[1], to_window); } } } |
onload 处理程序需要进行相应的修改:
window.onload = function () { load_current(); };
|
最后,添加清单 12 中的历史记录按钮处理例程。注意处理程序和测试应用程序的相似性。
清单 12. 历史记录按钮事件处理程序
function do_back() { myHistory.go(-1); load_current(); }
function do_forward() { myHistory.go(1); load_current(); }
function do_reload() { myHistory.go(0); }
|
至此就完成了历史堆栈到相册应用程序的集成。完成后的产品如图 3 所示。
图 3. 与相册应用程序结合的历史记录按钮
打开应用程序并单击链接,就会看到存储在浏览器 cookie 中的历史堆栈和指针。
CHCurrent = 4 CHStack = table-0-5%2Cimage-1%2Cimage-2%2Cimage-3%2Ctable-3-5 |
如果正在运行 Mozilla Firefox 并下载了 Web Developer Toolbar 扩展,那么这些操作就很容易实现。
分享到:
相关推荐
【作品名称】:基于servlet+jsp+mysql实现的影视管理系统【课程设计】 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【项目介绍】: 基于servlet+jsp+mysql实现的影视管理系统【课程设计】 基于servlet+jsp+mysql实现的影视管理系统【课程设计】 Java Web课程设计,基于servlet+jsp+ajax+mysql做的影视管理系统 运行环境: Tomcat 9.0 JDK 1.8 MySQL 8.0 后台管理账号密码均为:root,项目依赖:lib 目录 【资源声明】:本资源作为“参考资料”而不是“定制需求”,代码只能作为参考,不能完全复制照搬。需要有一定的基础看懂代码,自行调试代码并解决报错,能自行添加功能修改代码。
kernel-5.15-ky10-x86.tar.gz
【作品名称】:基于AT89C51 单片机为核心器件,程序设计采用C 语言,Keil 软件编译程序,配以相关外围接口电路,实现了方波、锯齿波、正弦波、三角波、梯形波五种特定波形的产生【论文+源码】 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【项目介绍】:本设计中的波形发生器系统要求基于51单片机,因此选用以AT89C51单片机作为整个系统的控制核心,应用其强大的接口功能,构成整个波形发生器的硬件系统。使用C 语言对单片机编程可产生相应的正弦波,方波,三角波,锯齿波梯形波波形信号。在程序运行时,当接收到按键信息后,需要输出某种波形时,调用相应的中断服务子程序和波形发生程序,经电路的数/模转换器和运算放大器处理后,从信号发生器的输出端口输出即可得到要求的波形。 当需要改变频率时只需要改变单片机的波形发生程序中的递增或者递减变量即可。 【资源声明】:本资源作为“参考资料”而不是“定制需求”,代码只能作为参考,不能完全复制照搬。需要有一定的基础看懂代码,自行调试代码并解决报错,能自行添加功能修改代码。
基于java的法律咨询系统设计与实现.docx
适用于元营销 API 的 Python SDK适用于 Python 的 Facebook Business SDK 介绍Facebook Business SDK是一站式服务,可帮助我们的合作伙伴更好地服务于他们的业务。合作伙伴正在使用多个 Facebook API 来满足其客户的需求。采用所有这些 API 并在各个平台上保持最新状态可能非常耗时,而且最终会造成高昂的成本。为此,Facebook 开发了 Business SDK,将其许多 API 捆绑到一个 SDK 中,以简化实施和维护。Business SDK 是 Marketing API SDK 的升级版,其中包括 Marketing API 以及来自不同平台(如 Pages、Business Manager、Instagram 等)的许多 Facebook API。快速入门商业SDK入门指南Python 目前是我们第三方开发人员最常用的语言。是一个 Python 包,它提供了您的 Python 应用程序与Business SDK 内的 Facebook APIfacebook_business之间的
数学建模培训资料 数学建模实战题目真题答案解析解题过程&论文报告 公交车调度的运作数学模型 共12页.pdf
smart-http 是一款可编程的 Http 应用微内核,方便用户根据自身需求进行 Server 或 Client 的应用开发。支持GET、POST的 HTTP 请求。提供了 URL 路由组件,可以快速搭建一套静态服务器。支持部分 RFC2612 规范,后续会逐渐完善。支持 Https 协议,由 smart-socket 为其赋能。具备文件上传的能力。支持 websocket、Cookie支持 Server、Client 开发
新闻资讯系统 微信小程序+SpringBoot毕业设计 源码+数据库+论文+启动教程 项目启动教程:https://www.bilibili.com/video/BV1oiBpYcEBp
高校师生工作室-JAVA-基于微信小程序的高校师生工作室管理系统的设计与实现
基于java的常见小儿疾病中医护理系统设计与实现.docx
本教程播放列表涵盖了 Python 中的数据结构和算法。每个教程都有数据结构或算法背后的理论、BIG O 复杂性分析和可供练习的练习。使用 Python 的数据结构和算法本教程涵盖了 Python 中的数据结构和算法。每个教程都包含数据结构或算法背后的理论、BIG O 复杂度分析以及可供练习的练习。要观看视频,您可以访问播放列表https://www.youtube.com/playlist?list=PLeo1K3hjS3uu_n_a__MI_KktGTLYopZ12订阅 codebasics youtube 频道https://www.youtube.com/c/codebasics
数学建模学习资料 蒙特卡罗方法课件教程 第2章.随机数 共29页.pptx
python实现基于CNN网络的新闻数据集文本分类源码+数据集(Python期末大作业),个人大三学期的期末大作业、经导师指导并认可通过的高分大作业设计项目,评审分98分。主要针对计算机相关专业的正在做大作业的学生和需要项目实战练习的学习者,可作为课程设计、期末大作业。 python实现基于CNN网络的新闻数据集文本分类源码+数据集(Python期末大作业)python实现基于CNN网络的新闻数据集文本分类源码+数据集(Python期末大作业),个人大三学期的期末大作业、经导师指导并认可通过的高分大作业设计项目,评审分98分。主要针对计算机相关专业的正在做大作业的学生和需要项目实战练习的学习者,可作为课程设计、期末大作业。python实现基于CNN网络的新闻数据集文本分类源码+数据集(Python期末大作业),个人大三学期的期末大作业、经导师指导并认可通过的高分大作业设计项目,评审分98分。主要针对计算机相关专业的正在做大作业的学生和需要项目实战练习的学习者,可作为课程设计、期末大作业。python实现基于CNN网络的新闻数据集文本分类源码+数据集(Python期末大作业),个人大
中小学知识产权教育试点学校申报表.doc
基于django的音乐推荐系统.zip
在建工程涉及专项行动情况检查表.docx
本项目是一个基于Python技术的学生管理系统,采用Django框架进行开发,旨在为计算机相关专业的学生提供一个实践性强、功能全面的管理系统,以帮助他们完成毕业设计或进行项目实战练习。 系统实现了对学生信息、课程信息、成绩、考勤等多方面的管理功能。学生信息管理包括学生基本信息的增删改查;课程信息管理允许管理员设置课程信息,包括课程名称、授课老师、学分等;成绩管理功能使学生和教师能够录入、查看和修改成绩;考勤管理则方便教师记录学生的出勤情况。 该项目采用B/S架构,前端使用HTML、CSS、JavaScript等技术,后端使用Python语言和Django框架,数据库采用MySQL。Django框架提供了强大的后台管理功能,使得系统管理更加便捷。 通过开发这个项目,学生不仅能提升自己的编程能力,还能学习到如何构建一个实际应用的系统,对于即将步入职场的学生来说,具有很高的实用价值。
适用于 Python 的 Splunk 软件开发工具包参考文档适用于 Python 的 Splunk Enterprise 软件开发工具包版本 2.1.0适用于 Python 的 Splunk Enterprise 软件开发套件 (SDK) 包含库代码,旨在使开发人员能够使用 Splunk 平台构建应用程序。Splunk 平台是一个搜索引擎和分析环境,它使用分布式 map-reduce 架构来有效地索引、搜索和处理大型时变数据集。Splunk 平台深受系统管理员的欢迎,用于聚合和监控 IT 机器数据、安全性、合规性以及各种其他场景,这些场景都需要有效地从大量时间序列数据中索引、搜索、分析和生成实时通知。Splunk 开发者平台使开发人员能够利用 Splunk 平台所使用的相同技术来构建令人兴奋的新应用程序。开始使用 Python 版 Splunk SDK开始使用 Python 版 Splunk Enterprise SDKSplunk Enterprise SDK for Python 包含库代码,其示例位于splunk-app-examples存储库
分布式事务练习
家庭财务管理系统 微信小程序+SSM毕业设计 源码+数据库+论文+启动教程 项目启动教程:https://www.bilibili.com/video/BV1BfB2YYEnS