JavaScript本地存储(2) : IE Only的userData

上次我们提到了本地存储的一个方式,那就是Cookie,不过遗憾的是Cookie保存的数据量非常小,更详细的可以参考 《在 Internet Explorer 中的 cookie 的数字和大小限制》 ,而且我们还要冒着用户禁用Cookie的风险,那么有没有变通的方法呢,微软为我们提供了一个类似的功能userData来帮助我们实现本地存储。

浏览器支持 : IE5.0 或以上

  • 基本语法 :
    XML: <Prefix: CustomTag id=sID style="behavior:url('#default#userData')" />
    HTML: <ELEMENT style="behavior:url('#default#userData')" id=sID>
  • Script:
    object.style.behavior = "url('#default#userData')"
    object.addBehavior ("#default#userData")
  • 属性:
    expires 设置或者获取 userData behavior 保存数据的失效日期。
    XMLDocument 获取 XML 的引用。
  • 方法:
    getAttribute() 获取指定的属性值。
    load(object) 从 userData 存储区载入存储的对象数据。
    removeAttribute() 移除对象的指定属性。
    save(object) 将对象数据存储到一个 userData 存储区。
    setAttribute() 设置指定的属性值。

要使用userData存储功能,必须先建立一个HTML标签,然后将behavior:url('#default#userData')样式属性加上去,等于说userData是寄存于HTML标签的,当然不是所有标签都是可以的,仅限于部分标签。要了解更多的信息可以访问MSDN的 《userData Behavior》

下面我们基于先前讲解的IStorage接口,实现UserData这个类。

var UserData = function() {
  this.userData = null;
  this.name = location.hostname;
  //this.name = "wangye.org";
  
  if (!this.userData) {
    try {
      this.userData = document.createElement('INPUT');
      this.userData.type = "hidden";
      this.userData.style.display = "none";
      this.userData.addBehavior ("#default#userData");
      document.body.appendChild(this.userData);
      var expires = new Date();
      expires.setDate(expires.getDate()+365);
      this.userData.expires = expires.toUTCString();
    } catch(e) {
    }
  }
  
  this.setItem = function(key, value) {
    this.userData.load(this.name);
    this.userData.setAttribute(key, value);
    this.userData.save(this.name);
  }
  
  this.getItem = function(key) {
    this.userData.load(this.name);
    return this.userData.getAttribute(key);
  }
  
  this.remove = function(key) {
    this.userData.load(this.name);
    this.userData.removeAttribute(key);
    this.userData.save(this.name);
  }
}

在这里我有必要说明一下,this.name这里指定了userData存储文件的文件名,这里我们指定为location.hostname,不过大家在本地测试时可能会遇到location.hostname为空的情况,这样会导致下面的脚本执行出错,因为不能指定一个空文件名,这时我们可以硬编码个文件名供我们测试。接下来我们通过createElement动态创建一个input元素作为我们userData的宿主,然后设置过期时间,这点类似于Cookie的过期时间,我们设置为当前时间+365天。接下来就可以通过load加载,然后对键值进行相关操作,然后再save了。

关于UserData这个类的具体操作和先前讲的Cookie一样,这里就不举例了。

Posted in:
  • 前端开发与用户体验
  • Web开发及相关
Tagged
  • javascript
  • 本地存储

使用DOMContentLoaded取代部分window.onload

使用DOMContentLoaded的好处是不言而喻的,其可以在DOM加载解析完毕后立即执行,而不是像window.onload那样还要继续等待所有外部图片什么的都加载完成才执行,所以在这点上DOMContentLoaded的效率功能要大于window.onload,一般适合于绑定事件到指定元素上时使用。

if(document.addEventListener){
  document.addEventListener("DOMContentLoaded",
    function() {
        alert('DOMContentLoaded is execute')
    }, false);
}

大家看到这段代码肯定非常熟悉,是的,想必大家都用过下面这段辅助函数,主要用于给元素绑定事件的,注意是绑定事件,多个绑定事件将依次触发。

function addListener(element, e, fn) {
    if (element.addEventListener) {
        element.addEventListener(e, fn, false);
    } else {
        element.attachEvent("on" + e, fn);
    }
}

// 比如绑定window.onload事件
addListener(window, "load", 
    function() {
        alert('window.onload is execute');
    }
);

说到这里大家就有疑问了,IE系列的浏览器不支持.addEventListener方法,这也就是在addListener函数里面要做个判断的原因,如果不支持就使用.attachEvent方法,那回到一开始的DOMContentLoaded的代码,那么这段代码很明显就不能支持IE系列浏览器了,我们能不能使用addListener函数呢?很遗憾,问题不在于这儿,而是IE系列浏览器不能很好的支持DOMContentLoaded,所以即使使用了addListener这样的辅助函数依然无济于事。

注意 : 微软最新发布的Internet Explorer 9(IE9)已经能够支持.addEventListener和DOMContentLoaded。

真的没有办法了吗?其实办法也不是没有,网上就有人总结出了两种方法:

  1. 一种是创建空script标签,属性拥有defer,然后待onreadystatechange为complete时激发DOMContentLoaded。
  2. 一种是通过调用doScroll('left')的原理去判断DOMContentLoaded。

如果大家对代码实现比较感兴趣的话可以参考 《IE里模拟DOMContentLoaded事件》 这篇文章。

说到这里,大家也许在想有没有更好的封装函数供我们使用呢?幸运的是 Jesse Skinner 已经在文章 《addDOMLoadEvent》 给出了其设计的函数脚本 adddomloadevent.js ,大家可以参考下。

Posted in:
  • 前端开发与用户体验
  • Web开发及相关
Tagged
  • javascript
  • htmldom

JavaScript利用Cookie保存所设置网页的字体大小

最近日本地震灾害比较严重,有些朋友可能会关注日本的网络媒体多一些,大家发现日本的网站很多都有字体大小的设置,分为“大”、“中”、“小”3个等级,这个对于视力不佳者可谓福音,也免去了调整浏览器字体大小之苦,但是如何让网站记住用户设置的字体大小呢,这时我们就需要本地存储了,在这里我们就用到前一篇文章 《JavaScript本地存储(1) : 使用Cookie》 讲解的Cookie类。

<html>
<head>
<style type="text/css">
body {
  font-size:75%;
}
p {
  font-size:1.2em;
}
.fontset a{
  font-size:12px;
}
</style>
</head>
<body>
<script type="text/javascript">
// 这里放《JavaScript本地存储(1) : 使用Cookie》的Cookie类代码

var myCookie = new Cookie();

function loadFontSize() {
  document.body.style.fontSize=(myCookie.getItem("fontsize"));
}

function setFontSize(value) {
  myCookie.setItem("fontsize", value);
  loadFontSize();
}

window.onload = function(){
  loadFontSize();
}
</script>
<div class="fontset">
<a href="javascript:setFontSize('120%')">大</a>
<a href="javascript:setFontSize('100%')">中</a>
<a href="javascript:setFontSize('75%')">小</a>
</div>
<p>我是内容</p>
</body>
</html>

大家可以看到,设置字体的办法是通过设置style的fontSize属性,当然这里我们通过CSS将body的字体默认为75%这样的百分比大小,然后通过设置内容的font-size字体大小为指定em为单位的数值大小就可以了,然后我们可以通过调整body的font-size的百分比来控制整体字体大小,当然对于字体固定为px像素大小的只能调整相应的像素值了,因为百分比对像素值的字体大小不起作用,这也就是为什么有些网站无法通过浏览器上的调整字体大小来调整的原因,当然这个像素大小字体的作用可以用来固定我们的“大”、“中”、“小”三个链接的字体大小,避免它们也随着整体百分比的改变而字体变化。

Posted in:
  • 前端开发与用户体验
  • Web开发及相关
Tagged
  • javascript
  • cookie
  • 网页字体

JavaScript本地存储(1) : 使用Cookie

在网页开发中,为了某些特殊的效果比如像购物车、页面换肤、记住密码等等就可能需要用到JavaScript的本地存储功能,说到底JavaScript的本地存储就是让网站能够“记住”你,等到下次再访问的时候能够识别出来,当然记住的方式无外乎就是通过在浏览者的电脑上保存一个记录的文件。本系列文章主要讲解这些技术,当然我们实现约定一个类似于下面的接口:

var IStorage = function() {
  this.setItem = function(key, value) {}
  this.getItem = function(key) {}
  this.remove = function(key) {}
}

下面的编程实现将围绕在这个接口展开,关于为什么定义这个接口,我将在下面的文章中阐述。

说到Cookie(也可以叫做Cookies)大家可能比较熟悉,这可以是最早实现本地存储的一个方法之一了,实现也非常简单。基于IStorage我们实现了下述关于Cookie类的代码:

var Cookie = function() {
  this.setItem = function(key, value) {
    var expires = new Date();
    expires.setTime(expires.getTime() + 
            1000 * 60 * 
            60 * 24 * 365);
            
    document.cookie = key + "=" + 
            escape(value) + 
            ";path=/" + 
            "; expires=" + 
            expires.toGMTString();
  }
  
  this.getItem = function(key) {
    var key_equal = key + "=";
    if (document.cookie.length <= 0)
      return "";
    var pos_start = document.cookie.
            indexOf(key_equal);
    if (pos_start == -1)
      return "";
    pos_start += key_equal.length;
    pos_end = document.cookie.
          indexOf(";", pos_start);
    if (pos_end == -1)
      pos_end = document.cookie.length;
      
    return unescape(document.cookie.
            substring(pos_start, pos_end));
  }
  
  this.remove = function(key) {
    this.setItem(key, "");
  }
}

可以看出每个Cookie由key和value这样的键值对组成,而且在这之间以分号相隔,形如key1=value1;key2=value2;等等,当然操作cookie可以通过document.cookie进行,然后我们所做的其实是文本分析,比如如何根据key获得等于号后面的value。

好了,下面的一篇文章我将会给大家展示一个例子,讲解Cookie的使用方法。

Posted in:
  • 前端开发与用户体验
  • Web开发及相关
Tagged
  • javascript
  • cookie
  • 本地存储

近况@2011.03.17

最近很累,长时间的面对电脑,眼睛明显不好使了,估计近视会有加深的迹象,尤其是处理那种“不能错”的Excel表格,仿宋的小字看得眼睛都酸,希望这样的日子快点结束吧。

电脑上的Eset Smart Security试用期满,不想满世界的去找激活码,360和金山的暂时不会考虑,avast有时感觉会很卡,AVG也是性能问题,不晓得现在有没有改观,小红伞误报率较高,不想在安全上耗费太多的时间,只要安全软件能够安安静静的就可以了,这也就是当初为什么选择ESS的原因,几番对比,选择了个比较安静的杀毒软件,那就是微软自家的Microsoft Security Essentials,用了段时间感觉良好,比较安静,资源占用不大,至于杀毒能力,这个我不介意,大部分病毒还是比较明显的,平常注意些就能很好躲过。

最近工作上经常需要处理一些定时的任务,但是由于任务繁忙,经常容易忘记,于是便想找个日程任务定时提醒的软件,后来搜索发现 Atnotes 这个还是比较好用的,在这里分享下。

目前,由于工作需要,目前主要的精力放在Web设计模式上,准备研究好PHP+MySQL的网站架构,PHP刚开始给我的感觉就是函数多,而且有好些函数看到名字想不到用途,需要常查手册,估计是比较生疏的缘故吧。Windows程序方面有个小工具程序的编写在进行中,不过目前要让步于Web应用的编写,但是等Web项目结束后,我将尽快推出这个小工具的。

Posted in:
  • 我的生活点滴
Tagged
  • 生活
  • 工作

使用Scripting.Dictionary字典对象

Scripting.Dictionary是个很有用的组件,其创建了类似于Key索引对应Value值的字典对象,并且在其内部提供了快速索引访问的机制,可以让我们通过Key直接索引到指定的Value,比遍历二维数组有效得多。

其在VBScript中是这样访问的。

Dim objDict
Set objDict = WSH.CreateObject("Scripting.Dictionary")
  ' .Add(key, value)    
  objDict.Add "a", "value1"
  objDict.Add "b", "value2"
  objDict.Add "c", "value3"
  '直接通过key=b索引到value2
  WSH.Echo objDict.Item("b")
  objDict.Remove "b" ' 删除索引b及其对应的值
    
  ' 以下是遍历字典
  Dim objKeys, objItems, i
  objKeys = objDict.Keys
  objItems = objDict.Items
  For i = 0 To objDict.Count -1
    WSH.Echo "Key=" & objKeys(i) &_
             " AND Value=" & objItems(i)
  Next
  ' 判断指定的key是否存在
  If objDict.Exists("b") Then
    WSH.Echo "Found it"
  Else
    WSH.Echo "Not Exists!"
  End If
  objDict.RemoveAll  ' 清空字典内所有的key及其对应value
Set objDict = Nothing

当然在JScript访问的方式一样,但是在遍历这里需要一点点变动。

var dict = WSH.CreateObject("Scripting.Dictionary");
  dict.Add("a", "value1");
  dict.Add("b", "value2");
  dict.Add("c", "value3");
  WSH.Echo(dict.Item("b"));
  dict.Remove("b");

  // 注意这里的遍历
  var keys = new VBArray(dict.Keys());
  var items = new VBArray(dict.Items());

  for (var i=0; i < dict.Count; i++) {
    WSH.Echo("Key=" + keys.getItem(i) +
             " AND Value=" + items.getItem(i));	
  }

  if (dict.Exists("b")) {
    WSH.Echo("Found it");
  } else {
    WSH.Echo("Not Exists!");
  }

  dict.RemoveAll();

由于Scripting.Dictionary的Keys和Items的集合返回的是VB安全数组,也就是说JScript访问需要多个转换的步骤,方法就是采用new VBArray()对象,相关信息可以参考MSDN的 《VBArray Objects》 ,当获得VBArray对象后就可以通过getItem方法获取数组元素了,值得注意的是这个VBArray对象自己不创建数组,其只起到一个转换的作用,也可以将其看成是一个操作接口吧。当然其toArray()方法可以将其转换为真正的JScript数组,届时可以直接像操作JScript数组一样操作VBArray转换的对象了。

Posted in:
  • VBScript/JavaScript/Python
  • 系统应用程序开发
Tagged
  • dictionary字典

利用CSS的box-sizing属性控制input输入框的高度

不久前记述过一篇关于侧边栏搜索框奇怪的高度问题,见 《Webkit内核浏览器下搜索框大小及文字下沉问题》 ,后来简单处理了下,但是在各个浏览器下还是有几个像素的差异,特别是Webkit内核和其他浏览器内核的差异。

一日在网上瞎逛,偶然看见百度UFO的 《如何更好地控制input输入框的高度》 这篇文章,颇受启发,原来在标准模式下盒模型的变化导致了input输入框的差异,无论通过设置height还是padding,总是会有些差异,这篇文章给出了很详细的叙述。最终比较妥当的解决方案就是使用box-sizing属性,这样高度就能完美控制了。

input {
    -moz-box-sizing: border-box;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
    height: 28px;
    *height: 22px;
}

由于IE6/7不支持这个属性, 所以需要写hack,由于IE下的默认border值是2, padding是1, 所以height需要减6像素。

后来该文作者提到了CSS Reset,也就是说一般会习惯性将input的padding设置为0,这时算下来的就少个padding-top和padding-bottom的2像素,也就是说IE6/7下需要减去2个像素,所以原先的CSS变成下面这样:

input {
    padding-top: 0;
    padding-bottom: 0;
    -moz-box-sizing: border-box;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
    height: 28px;
    *height: 24px;
}

当然为了稳妥,我们先主动将padding-top和padding-bottom设置为0。

好了,问题目前算是较好解决了,如果大家还有什么好的办法记得分享哦。

Posted in:
  • 前端开发与用户体验
  • Web开发及相关
Tagged
  • 浏览器兼容

防止自己的网页被iframe框架引用

对于一些安全要求较高的网站,往往不希望自己的网页被另外非授权网站框架包含,因为这往往是危险的,因为不法分子总是想尽办法以“钓鱼”的方式牟利。

对于禁止网页被frame或者iframe框架,我总结了下面三种方法供大家参考。

1.使用meta元标签

<html>
<head>
    <meta http-equiv="Windows-Target" contect="_top">
</head>
<body></body>
</html>

这个方法的具体效果不是很清楚的,貌似之前试过一次,但是没有效果,可能浏览器太旧了吧。

2.使用JavaScript脚本

function location_top(){
    if(top.location!=self.location){
        top.location=self.location;
        return false;		
    }
    return true;
}
location_top(); // 调用

这个方法用得比较多,但是网上的高手也想到了破解的办法,那就是在父框架中加入脚本var location=document.location或者var location="",所以这个方法也就不推荐了,当然唬唬不懂的还是可以的。

3.使用HTTP响应头

这里介绍的响应头是 X-Frame-Options ,这个属性可以解决使用js判断会被var location;破解的问题,IE8、Firefox3.6、Chrome4以上的版本均能很好的支持,关于这个响应头属性详细的介绍可以看 《The X-Frame-Options response header》 ,这篇文章给出了这个属性两个可能的值:

DENY The page cannot be displayed in a frame, regardless of the site attempting to do so.
SAMEORIGIN The page can only be displayed in a frame on the same origin as the page itself.

其中我们用到DENY,这样就禁止该页在上述支持的浏览器被框架引用了。当然设置header响应头的办法有很多,比如PHP的header函数和.htaccess的Header set等。

Posted in:
  • 前端开发与用户体验
  • Web开发及相关
Tagged
  • frame框架
  • Web安全

Excel中VBA简单的编程技巧

最近在单位经常要整理Excel表格,大量的数据有时候用公式函数处理也略显麻烦,这时我们可以使用Excel的VBA特性,打开VBA编辑器。

我们需要给要处理的表指派一个任务,这时我们可以建立子过程,建立的方式只要输入:

Sub Sample()
   ' TODO : 这里写执行代码
End Sub

这样我们就建立起名称为Sample的子过程,下面我们可以单击运行箭头,然后在宏列表中选择我们的子过程执行,不过现在没有代码,所以将不会有什么结果。

将上面的代码改成下面这样,再次运行,就可以看到消息框“Hello, World”。

Sub Sample()
   MsgBox "Hello, World"
End Sub

好了,下面需要给子过程指派指定的工作表,比如工作表名为Sheet1,下面几步建立起工作表的关联。

Sub Sample()
   Dim ws As Worksheet
   ' 设定ws引用Sheet1对象
   Set ws = Worksheets("Sheet1") 
   ws.Activate ' 激活指定的表
   ' TODO : 这里放对表操作的代码
   Set ws = Nothing ' 销毁对象引用
End Sub

两个常见的操作是设置行高(RowHeight)和列宽(ColumnWidth)。我们可以通过ActiveSheet.Rows( 行数 )获得指定行的对象,或者通过ActiveSheet.Columns( 列数 )获得列对象,那么行高和列宽的设置可以像下面这样:

Sub Sample()
   Dim ws As Worksheet
   ' 设定ws引用Sheet1对象
   Set ws = Worksheets("Sheet1")
   ws.Activate ' 激活指定的表

   ActiveSheet.Rows(1).RowHeight = 30
   ActiveSheet.Columns(1).ColumnWidth = 10

   Set ws = Nothing ' 销毁对象引用
End Sub

需要注意的是,行和列都是从1开始数起。

接下来就是对每个单元格的操作了,刚才我们设定ws引用Sheet1对象,那么单元格对象就是ws.Cells( 行数 , 列数 )。

Sub Sample()
   Dim ws As Worksheet
   Set ws = Worksheets("Sheet1") ' 设定ws引用Sheet1对象
   ws.Activate ' 激活指定的表
   
   Dim i As Integer
   For i = 1 To 50
      ' 设置第3列1~50行的单元格值
      ws.Cells(i, 3).Value = 0
      ' 设置第4列1~50行的单元格文本
      ws.Cells(i, 4).Text = "hi"
   Next i

   Set ws = Nothing ' 销毁对象引用
End Sub

好的先介绍这么多,如果大家想深入研究,不妨参阅 《Microsoft Excel Visual Basic参考》

Posted in:
  • VBScript/JavaScript/Python
  • 系统应用程序开发
Tagged
  • excel
  • vba

CSS设置样式时区分不同版本IE的办法

刚才看到Web Designer Wall的这篇文章 《CSS Specific for Internet Explorer》 感觉蛮实用的,介绍了3种为不同版本IE设置样式的办法,在这里我总结下:

1.条件注释

<!--[if lt IE 7]>版本小于IE7将看到这行<![endif]-->
<!--[if lte IE 7]>版本小于或等于IE7将看到这行<![endif]-->
<!--[if IE 7]>版本如果是IE7将看到这行<![endif]-->
<!--[if gt IE 7]>版本大于IE7将看到这行<![endif]-->

很明显具体格式是if 运算符 IE 版本号,运算符如果省略将意味着等于,否则请取lt(小于)、gt(大于)、lte(小于或等于)、gte(大于或等于),另外还可以进行感叹号(!)逻辑取非和 | 逻辑或等运算,这个将在第三种方法介绍中给出实例。 具体使用在html中这样写:

<!--[if IE 6]>
<style type="text/css">
 /* 针对IE6定义的样式 */
</style>
<![endif]-->

当然除了定义style样式外我们还可以针对不同IE浏览器定义不同的内容,可以是script脚本或者其他显示或隐藏的内容等。

2.CSS Hack

这种方法可能大家已经很熟悉了,具体如下:

.box {	
    background: gray; /* 基本的 */
    background: pink\9; /* IE 8 及低于IE8版本 */
    *background: green; /* IE 7 及低于IE7版本 */
    _background: blue; /* IE 6 */
}

另外网上找到的下面这张表可以很清楚的像大家展示IE各版本的css hack。

Hack Example IE6(S) IE6(Q) IE7(S) IE7(Q) IE8(S) IE8(Q)
* *color Yes Yes Yes Yes No Yes
+ +color Yes Yes Yes Yes No Yes
- -color Yes Yes No No No No
_ _color Yes Yes No Yes No Yes
# #color Yes Yes Yes Yes No Yes
\0 color\0 No No No No Yes No
\9 color\9 Yes Yes Yes Yes Yes Yes
!important color:blue !important;
color:green;
No No Yes No Yes No

这里S代表standard标准模式,Q代表Quirks怪异模式 更详细的可以看 牛人总结的兼容一览表

3.根据条件注释为html设置不同的class

这个方法也是我最近研究HTML5时遇到的,特别是这里的一份 HTML5开发模板 ,具体做法如下:

<!DOCTYPE html>
<!--[if lt IE 7 ]> <html class="ie6"> <![endif]-->
<!--[if IE 7 ]> <html class="ie7"> <![endif]-->
<!--[if IE 8 ]> <html class="ie8"> <![endif]-->
<!--[if IE 9 ]> <html class="ie9"> <![endif]-->
<!--[if (gt IE 9)|!(IE)]><!--> <html> <!--<![endif]-->
<head>
<style type="text/css">
.box {
	color: #fff;
	padding: 5px 20px;
	background: gray; 
}
.ie8 .box {
	background: pink;
}
.ie7 .box {
	background: green;
}
.ie6 .box {
	background: blue;
}
</style>
</head>
<body>
<div class="box">
Content here
</div>
</body>
</html>

可以看到这里巧妙的用到了第一种条件注释的方法,其中if (gt IE 9)|!(IE)就是在第一种方法中谈到的逻辑或和非的一个实例,意思就是,如果IE版本大于9或者非IE则if成立。

总结一下,说到底还是第三种方法个人感觉比较好,而且也是符合标准能够通过验证(HTML+CSS)的办法之一。具体这个办法的详细介绍可以参考Paul Irish的 《Conditional stylesheets vs CSS hacks? Answer: Neither!》

Posted in:
  • 前端开发与用户体验
  • Web开发及相关
Tagged
  • 浏览器兼容

© Wang Ye / 王 晔. All rights reserved.