关于URL重写

没有评论

2011 年 05 月 30 日 at 上午 11:19分类:PHP

网站结构如下:
index.php
source/src_{模块名}.php
template/tpl_{模块名}.php
通过首页统一入口来访问各个模块文件,
比如,最简单的统一入口访问代码:
define(‘ROOT’, dirname(__FILE__));
$mod = isset($_GET['mod']) ? $_GET['mod'] : ‘index’;
$src = ROOT . ‘source/src_’ . $mod . ‘.php’ ;
$tpl = ROOT . ‘template/src_’ . $mod . ‘.php’ ;
file_exists($src) && include($src) ;
file_exists($tpl) && include($tpl) ;
现在访问一个地址:index.php?mod=news ;则会访问source/src_news.php 文件。
现在要让url重写,访问 /news 这个url能实现一样的效果,
Linux下的Apache和Nginx,
windows下的ISAPI_Rewrite模块重写,
重写要求,如果我访问的是不存在的文件,重定向到index.php,通过获取实际访问的url,来判断该调用哪个文件,如果我访问 /news?page=1 这个URL,重定向后:
三个服务器访问后的结果:
Apache 关于开启rewrite模块后,会产生三个头部:
$_SERVER['REDIRECT_STATUS'] => 200 (证明重写成功)
$_SERVER['REDIRECT_QUERY_STRING'] => page=1
$_SERVER['REDIRECT_URL'] => /news
而IIS的rewrite模块重写后,会有
$_SERVER['REDIRECT_STATUS'] => 200
$_SERVER['HTTP_X_REWRITE_URL'] => /news
Nginx重写后有:
$_SERVER['REDIRECT_STATUS'] => 200
所以,如果采用 $_SERVER['REDIRECT_URL'] 或 $_SERVER['HTTP_X_REWRITE_URL']都是不可取的,因为Nginx重写后,不产生重写URL,三个服务器并没有交集,如果还考虑其他服务器比如 lighttpd,cheroke等,可能产生的头部更加不一致。
通常需要考虑的就是PHP的 $_SERVER['REQUEST_URI']
现在看看这几个服务器产生的 REQUEST_URI是否一致:
apache下:
$_SERVER['REQUEST_URI'] => /news
Nginx下:
$_SERVER['REQUEST_URI'] => /news
IIS下:
$_SERVER['REQUEST_URI'] => /index.php/news
这他妈是怎么回事?干嘛加个/index.php这个玩意呢?我没有搞懂,不过IIS的重写不完全支持.htaccess语法,所以,不知道是否是我配置的问题,但是我绞尽脑汁也弄不掉这个/index.php
所以,干脆用IIS专用的 $_SERVER['HTTP_X_REWRITE_URL'] 来替换 $_SERVER['REQUEST_URI']
暂时世界清静了。
要提取完整的重写后的URL地址,还没这么简单,需要考虑子目录下重写的情况,下回放出完整的无bug的提取重写url的函数,依靠此函数,将函数分割,然后形成$controller, $action .. 等参数

$_SERVER[PHP_SELF],[SCRIPT_NAME], ['REQUEST_URI']

没有评论

2011 年 05 月 25 日 at 下午 2:50分类:PHP

$_SERVER[PHP_SELF], $_SERVER[SCRIPT_NAME], $_SERVER['REQUEST_URI'] 在用法上是非常相似的,他们返回的都是与当前正在使用的页面地址有关的信息,这里列出一些相关的例子,帮助确定哪些是在你的脚本最适合的。

$_SERVER[’PHP_SELF’]http://www.fenge8.cn/example/ — – — /example/index.php
http://www.fenge8.cn/example/index.php — – — /example/index.php
http://www.fenge8.cn/example/index.php?a=test — – — /example/index.php
http://www.fenge8.cn/example/index.php/dir/test — – — /dir/test
当我们使用$_SERVER['PHP_SELF']的时候,无论访问的URL地址是否有index.php,它都会自动的返回 index.php.但是如果在文件名后面再加斜线的话,就会把后面所有的内容都返回在$_SERVER['PHP_SELF']。

$_SERVER['REQUEST_URI']

http://www.fenge8.cn/example/ — – — /
http://www.fenge8.cn/example/index.php — – — /example/index.php
http://www.fenge8.cn/example/index.php?a=test — – — /example/index.php?a=test
http://www.fenge8.cn/example/index.php/dir/test — – — /example/index.php/dir/test
$_SERVER['REQUEST_URI']返回的是我们在URL里写的精确的地址,如果URL只写到”/”,就返回 “/”

$_SERVER['SCRIPT_NAME']

http://www.fenge8.cn/example/ — – — /example/index.php
http://www.fenge8.cn/example/index.php — – — /example/index.php
http://www.fenge8.cn/example/index.php — – — /example/index.php
http://www.fenge8.cn/example/index.php/dir/test — – — /example/index.php
在所有的返回中都是当前的文件名/example/index.php

关于zencart的index.php的分析

没有评论

2011 年 05 月 17 日 at 下午 9:09分类:PHP


有的程序一看就明白,然而真要进行修改和扩展,还真不容易,ICommerce就是这样的程序;然而有的程序,看起来很复杂,以至于一时间连某个页面都找不到在哪儿,这种程序架构比较复杂,初级网站编程人员很难看明白,但是当你真正的掌握到了它的程序结构后,你一定会为架构师的思维拍案叫绝,因为你会发现,若要开发一些新功能会变得如此的方便,zen-cart就是如此.
zen-cart是国外比较优秀的开源电子商务网点源码,zen-cart程序源于os-commerce,借鉴了os-commerce的很多成果,而在扩展性方面,zen-cart显得又比前辈做得更好.

可以看到, zen-cart前台所有的页面地址是如下格式:

1.http://127.0.0.1/zencart/index.php?main_page=index&cPath=1_4

2.http://127.0.0.1/zencart/index.php?main_page=product_info&cPath=1_4&products_id=1

3.http://127.0.0.1/zencart/index.php?main_page=site_map

也许你会觉得很困惑, 怎么所有的页面都是index.php啊?这么大的一个网店系统, 难道一个index.php页面就可以搞定了吗? 这时, 你可能就需要耐心的去分析一下index.php页面了.
经过分析, 你就会发现, 其实index.php页面并不想你所想象的那么神奇, 那么深不可测. 相对于其他文件, Index.php更像是一个管理者, 一个司令员, 当它接到指令后, 并不是自己来处理, 而是把任务分配给他的下属. 至于要完成什么任务, 那么就要看接收到的指令main_page的内容了.

在index.php页面的开头, 作者介绍了该页面的处理过程, 如表格 2‑1所示.

* index.php represents the hub of the Zen Cart MVC system
 *
 * Overview of flow
 * <ul>
 * <li>Load application_top.php - see {@tutorial initsystem}</li>
 * <li>Set main language directory based on $_SESSION['language']</li>
 * <li>Load all *header_php.php files from includes/modules/pages/PAGE_NAME/</li>
 * <li>Load html_header.php (this is a common template file)</li>
 * <li>Load main_template_vars.php (this is a common template file)</li>
 * <li>Load on_load scripts (page based and site wide)</li>
 * <li>Load tpl_main_page.php (this is a common template file)</li>
 * <li>Load application_bottom.php</li>
 * </ul>
*

表格 1.加载application_top.php文件
Application_top.php文件中将进行许多常量的定义, 加载配置文件等工作

2.设置语言目录这里定义了一个变量$language_page_directory, 代表当前的语言目录, 该变量的值和$_SESSION['language']值息息相关.

3.加载” includes/modules/pages/PAGE_NAME/”目录中的所有header_php.php文件.可以看到, 在每个网页内容的文件夹中, 都有header_php.php文件, 加载页面时, 首先会加载该文件.

4.加载html_header.php文件html_header.php文件包含了在标记中出现的内容, 默认放在includes/templates/common目录下. 当然在特定的情况下, 加载的可能不是这个文件, 你可以再模板级或者页面级来重写这个文件, 从而定义特殊的html_header.php文件.

通过金字塔视图, 可以看到html_header.php文件的优先级别, 越靠近金字塔顶端, 优先级别越高, 如果优先级别高的文件已经存在,则不会加载优先级别比它低的文件了.

main_template_vars.php文件根据$_GET['main_page']的值实现了页面的跳转逻辑. 该文件的加载过程和html_header.php文件的加载过程异曲同工, html_header.php加载的是网页头部中的内容, 而文件main_template_vars.php却决定了要加载的网页的主体内容.

通过观察template_default中common下面的main_template_vars.php中的代码, 可以知道, 若在存储页面文件的”includes/modules/pages/$_GET['main_page']”中增加一个名为main_template_vars.php的文件, 则将直接加载这个文件. 否则, 将加载当前模板中的”tpl_$_GET['main_page']_ default.php”文件. (注意, 这里实际上没有真正加载文件, 而只是将要加载的文件的路径$body_code进行设置)

如果您对这两个文件的加载过程还不胜了解, 那么请试着做下面的实验.

在地址栏中访问”http://127.0.0.1/zencart/index.php?main_page=news_list”, 将会出现网页未找到的提示信息. 这是可以理解的, 因为news_list是我自己想象的, zen-cart没有提供这个页面, 我自己也没有安装类似的插件.

然后在目录”/includes/modules/pages”中新建一个文件夹, 命名为news_list, 再访问该链接, 就不会提示找不到链接了, 至此一个新的页面就建好了, 接下来的工作就是要丰富该页面的内容了. 这将在后面的章节中予以描述.

6.加载on_load脚本
on_load_*.js文件中包含了要在标记的onload属性中出现的内容, 即在页面加载完成后要执行的脚本. 可以在同一个页面中定义多个on_load_*.js文件, 这些内容将会同时被执行.
on_load_*.js文件可能是页面级和站点级的. 页面级的on_load_*.js文件只在当前页面中有效, 而站点级on_load_*.js文件将对站点中的每个页面有效. 有关如何定义页面级和站点级的onload事件, 将在后面的章节予以描述.
在这里, 页面级和站点级的on_load_*.js文件内容都将读入$za_onload_array[]数组中, 然后组合到变量$zv_onload中, 为后面做好准备. $zv_onload最终将作为到的onload属性值.

7.加载tpl_main_page.php文件

这里定义将选择影响页面布局的模板, 可以在具体的页面中选择, 或者使用zen-cart默认的模板(一个标准的三列板式), 模板中会对main_template_vars.php中定义的变量$body_code所指文件予以加载.

8.加载application_bottom.php文件
application_bottom.php文件中将进行一些清理操作.
原文:http://blog.sina.com.cn/s/blog_6dfe94d60100my39.html

jquery的queue和dequeue的使用

没有评论

2011 年 05 月 17 日 at 下午 9:08分类:jQuery

之前对jquery的这两个功能不能理解,也不知道是怎么回事,今天有时间来仔细的搜索了一些资料研究了下。
queue(name,[]):

queue(name,[callback]): 当只传入一个参数时, 它返回并指向第一个匹配元素的队列(将是一个函数数组,队列名默认是fx); 当有两个参数传入时, 第一个参数还是默认为fx的的队列名, 第二个参数又分两种情况, 当第二个参数是一个函数时, 它将在匹配的元素的队列最后添加一个函数. 当第二个参数是一个函数数组时,它将匹配元素的队列用新的一个队列来代替(函数数组).可能, 这个理解起来有点晕, 稍后, 后面会有点此查看DEMO.

详细出处参考:http://www.jb51.net/article/25481.htm

PHP soap拓展的使用

没有评论

2011 年 05 月 09 日 at 下午 2:40分类:PHP

SOAP
1. Simple Object Access Protocol 简单对象访问协议
主要包括下面四部分:
a) SOAP 封装: 用于将传输数据中的内容, 发送端消息, 接收端信息和处理方式等信息封装起来以准备数据传输.
b) SOAP 编码规则: 用于表示传输数据中各项的数据类型等信息
c) SOAP 远程过程调用协定: 用于进行远程过程调用及应答的协议
d) SOAP 绑定协议: 用于表示信息交换的底层协议
Ø PHP5 中SOAP 应用及配置
=========================================================================
1. PHP 5 中新增了内置的 SOAP 扩展,我们称之为 ext/soap 。它是作为 PHP 的一部分提供的,因此不需要下载、安装和管理单独的包。
2. ext/soap 可能已经编译但没有加载,因此需要更新 PHP 配置,以便加载 ext/soap 。编辑 php.ini 并找到 Dynamic Extensions 部分,在这里增加一行代码来自动加载该扩展。 在 Windows 上,这一代码行是:extension=php_soap.dll
如果以前没有加载过任何可选的扩展,可能还要设置 extension_dir 指令,让它指向包含扩展库(其中包括 php_soap )的目录:
extension_dir=”C:/php/ext/” (在 Windows 上使用正斜杠)
3. 完成配置后ext/soap 显示为:
[soap]
; Enables or disables WSDL caching feature.
soap.wsdl_cache_enabled=1
; Sets the directory name where SOAP extension will put cache files.
soap.wsdl_cache_dir=”/tmp”
; (time to live) Sets the number of second while cached file will be used
; instead of original one.
soap.wsdl_cache_ttl=86400
这段配置控制了 SOAP 扩展的 WSDL 缓存特性。默认情况下,WSDL 描述文件在 24 小时(86400 秒)内都缓存在 /tmp 目录下。现在要设置 soap.wsdl_cache_enabled=0 ,否则,在开发代码时,您会遇到一些莫名其妙的行为。完成开发之后,要记得打开 WSDL 缓存 ,使代码运行得更快。
========================================================================
SOAP 扩展库( 主要包括三种对象)
1. SoapServer : 用于在创建PHP 服务端页面时定义可被调用的函数及返回响应数据.
格式为: $soap = new SoapServer($wsdl, $array);
说明:$wsdl 为SOAP 使用的WSDL 文件, 是描述WebService 的一种标准格式. 设为NULL 表示不使用WSDL 模式. 如果想用的话可以用ZED 生成.
$array 是SoapServer 的属性信息, 是一个数组.
注意 :$array 中包括一个uri, 和encoding, 如果是客户端程序中还要包含一个location.
其中’uri ‘ 选项是必须的 ,但其值可以随便填 ,可以为空(”), 但不可以为null .
‘encoding ‘ 对于中文的处理,是必须的,一般填’gb2312′ ( 否则会出错, 因为默认为’utf-8′ ).
该对象的addFunction 方法用来声明哪个函数可以被客户端调用,
语法格式为:$soap -> addFunction($function_name);
该对象的handle 方法用来处理用户的输入并调用相应的函数, 最后返回给客户端处理后的结果,
语法格式为:$soap -> handle([$soap_request]);
说明:$soap_request 是一个可选参数, 用来表示用户的请求信息, 如果不指定, 则表示服务器将接收用户的全部请求 .
2. SoapClient : 用于调用远程服务器上的SoapServer 页面, 并实现了对相应函数的调用.
语法格式为:$soap = new SoapClient($wsdl, $array);
注意 : 对于soap client,’location ‘ 和’uri’ 选项都是必须的 ; 其中’location ‘ 必须为soap server 的URL , 必须准确, 但uri 似乎可以随便填,不过最好与soap server 中的uri 一致. 而’encoding ‘ 对于中文用户是必须的
创建该对象后, 调用服务器页面中的函数相当于调用SoapClient 的方法
语法格式为:$soap -> user_function($params);
说明:user_function 是服务器端定义的可以被调用中的函数中的一个或几个函数.
3. SoapFault : 用于生成在SOAP 访问过程中可能出现的错误.
语法格式为:$fault = new SoapFault($faultcode, $faultstring);
说明:$faultcode 是用户自定义的错误代码, 如果没有定义的话应该是HTTP( 印象中),$faultstring 是用户自定义的错误信息.
该对象会在服务器端页面出现错误时自动生成, 或者通过用户自行创建SoatFault 对象来获得相应的错误信息.
在客户端捕获SoapFault 对象后, 可以通过下面的代码获得错误代码的错误信息
$fault -> faultcode; // 错误代码
$fault -> faultstring; // 错误信息

下面的例子:
soapserver.php

<?php

function reserve($arr){
	return http_build_query($arr);
}

$soap = new SoapServer(NULL,array('uri'=>'http://www.fbbin.com'));

//为对象添加一个用户自定义的函数
$soap->addFunction('reserve');

$soap->handle();
?>

soapclient.php

<?php

try{

	$client = new SoapClient(NULL,array('location'=>'http://localhost/exercise/soap/soapserver.php','uri'=>'http://www.fbbin.com'));

	$arr = array('id'=>3,"tag"=>"PHP MYSQL","search"=>"soap");

	echo $string = $client->reserve($arr);
	
}catch(SoapFault $fault){
	
	echo "FAULT!Code:".$fault->faultcode."String:".$fault->faultstring;
	
}


?>

输出的结果为:id=3&tag=PHP+MYSQL&search=soap.
成功。。。

JS获取style属性

没有评论

2011 年 05 月 05 日 at 下午 5:36分类:CSS | HTML | JavaScript

<html >
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>currentStyle</title>
    <!-- <style type="text/css">
    </style>-->
    <link rel="Stylesheet" href="StyleSheet.css" type="text/css" />
</head>
<body>
    <div id="qq" class="divtitle">
        测试样式
    </div>
    <script type="text/javascript"> 
//document.defaultView.getComputedStyle 这是w3c标准方法,取得元素的样式信息,因为有些样式是在外部css文件定义的,所以用element.style是取不到的 如果是IE,可以用 element.currentStyle["name"]
   function GetCurrentStyle (obj, prop) 
   { 
      if (obj.currentStyle) //IE
      { 
          return obj.currentStyle[prop]; 
      }
      else if (window.getComputedStyle) //非IE
      { 
          propprop = prop.replace (/([A-Z])/g, "-$1"); 
          propprop = prop.toLowerCase (); 
          return document.defaultView.getComputedStyle(obj,null)[propprop]; 
      } 
     return null;
   } 
   var dd=document.getElementById("qq"); 
   alert(GetCurrentStyle(dd,"color")); 
    </script>
</body>
</html>
外部样式:StyleSheet
.divtitle
        {
            background: blue;
            color: #cdcdcd;
            width: 200px;
        }

JS函数

var getCss = function(o,key){
    return o.currentStyle ? o.currentStyle[key] : document.defaultView.getComputedStyle(o,false)[key]; 
};

用oncontextmenu事件单禁用右键菜单

没有评论

2011 年 05 月 05 日 at 下午 4:13分类:JavaScript

onconTextmenu=window.event.returnValue=false;右键菜单禁用,用这个可以禁止复制。
在中加入属性代码:
oncontextmenu=”return false”
onselectstart=”return false” 禁止选中网页上的内容
oncopy=”return false” 防复制用户在网页上选中的内容
防止用户另存网页:
利用

标签,能防止网页的直接另存,但不能防止网页被人使用工具下载
*为通配符。

<html> 
<head> 
<title>OnContextMenu事件</title>
<script language="JavaScript"> 
<!--
function uFunction()

{     document.all.infoDiv.innerHTML='你按下了鼠标右键,但是右键菜单不能 显示!';}

function uFunction2()

{    document.all.infoDiv.innerHTML='你按下了Ctrl+鼠标右键,可以 显示右键菜单。';}

//-->
</script> 
</head>
<body oncontextmenu="if(!event.ctrlKey){uFunction();return false}else{uFunction2()}">
<div id="infoDiv">你按下了鼠标右键,但是右键菜单不能 显示!<br>你按下了Ctrl+鼠标右键,可以显示右键菜单。 
</div></body> 
</html>

************************************************************
附:
JS中判断鼠标按键的问题。
IE
左键是 window.event.button = 1
右键是 window.event.button = 2
中键是 window.event.button = 4
没有按键动作window.event.button = 0

Firefox
左键是 event.button = 0
右键是 event.button = 2
中键是 event.button = 1
没有按键动作 event.button = 0

Opera 7.23/7.54
鼠标左键是 window.event.button = 1
没有按键动作 window.event.button = 1
右键和中键无法获取

Opera 7.60/8.0
鼠标左键是 window.event.button = 0
没有按键动作 window.event.button = 0
右键和中键无法获取

另外:屏蔽右键的是window.event.button = 3

************************************************************
Window.event对象代表事件的状态,例如触发event对象的元素、鼠标的位置及状态、按下的键等等。
Window.event对象只在事件发生的过程中才有效。
Window.event的某些属性只对特定的事件有意义。比如,fromElement 和 toElement 属性只对 onmouseover 和 onmouseout 事件有意义。
如果事件触发后,鼠标移出窗口外,则返回的值为 -1 ,这是个只读属性。这意味着,你只能通过它来得到鼠标的当前位置,却不能用它来更改鼠标的位置。

Event对象的属性有:
altKey, button, cancelBubble, clientX, clientY, ctrlKey, fromElement, keyCode, offsetX, offsetY, propertyName, returnValue, screenX, screenY, shiftKey, srcElement, srcFilter, toElement, type, x, y

详情:点击

js中正则对象的方法说明

没有评论

2011 年 05 月 05 日 at 下午 1:36分类:JavaScript

exec 方法
用正则表达式模式在字符串中运行查找,并返回包含该查找结果的一个数组。
rgExp.exec(str)
参数 rgExp : 必选项。包含正则表达式模式和可用标志的正则表达式对象。
str : 必选项。要在其中执行查找的 String 对象或字符串文字。
说明
如果 exec 方法没有找到匹配,则它返回 null。如果它找到匹配,则 exec 方法返回一个数组,并且更新全局 RegExp 对象的属性,以反映匹配结果。数组的0元素包含了完整的匹配,而第1到n元素中包含的是匹配中出现的任意一个子匹配。这相当于没有设置全局标志 (g) 的 match 方法。
如果为正则表达式设置了全局标志,exec 从以 lastIndex 的值指示的位置开始查找。如果没有设置全局标志,exec 忽略 lastIndex 的值,从字符串的起始位置开始搜索。
exec 方法返回的数组有三个属性,分别是 input、index 和 lastIndex。Input 属性包含了整个被查找的字符串。Index 属性中包含了整个被查找字符串中被匹配的子字符串的位置。LastIndex 属性中包含了匹配中最后一个字符的下一个位置。
示例
下面的例子举例说明了 exec 方法的用法:

function RegExpTest(){
  var ver = Number(ScriptEngineMajorVersion() + "." + ScriptEngineMinorVersion())
  if (ver >= 5.5){                 // 测试 JScript 的版本。
    var src = "The rain in Spain falls mainly in the plain.";
    var re = /\w+/g;               // 创建正则表达式模式。
    var arr;
    while ((arr = re.exec(src)) != null)
       document.write(arr.index + "-" + arr.lastIndex + "\t" + arr);
  }
  else{
    alert("请使用 JScript 的更新版本");
  }
} 

match 方法
使用正则表达式模式对字符串执行查找,并将包含查找的结果作为数组返回。
stringObj.match(rgExp)

function MatchDemo(){
   var r, re;         // 声明变量。
   var s = "The rain in Spain falls mainly in the plain";
   re = /ain/ig;      // 创建正则表达式模式。
   r = s.match(re);   // 尝试去匹配搜索字符串。
   return(r);         // 返回的数组包含了所有 "ain" 
                      // 出现的四个匹配。
}

search 方法
返回与正则表达式查找内容匹配的第一个子字符串的位置。
stringObj.search(rgExp)
参数 stringObj 必选项。要在其上进行查找的 String 对象或字符串文字。
rgExp 必选项。包含正则表达式模式和可用标志的正则表达式对象。
说明
search 方法指明是否存在相应的匹配。如果找到一个匹配,search 方法将返回一个整数值,指明这个匹配距离字符串开始的偏移位置。如果没有找到匹配,则返回 -1。
示例

function SearchDemo(){  
 var r, re;                   // 声明变量。  
 var s = "The rain in Spain falls mainly in the plain.";
 var re = /falls/i;            
// 创建正则表达式模式。 
  r = s.search(re);            
// 查找字符串。  
   return(r);                 
  // 返回 Boolean 结果。
}

test 方法
返回一个 Boolean 值,它指出在被查找的字符串中是否存在模式。
rgexp.test(str)
参数 rgexp 必选项。包含正则表达式模式或可用标志的正则表达式对象。
str 必选项。要在其上测试查找的字符串。
说明
test 方法检查在字符串中是否存在一个模式,如果存在则返回 true,否则就返回 false。
全局 RegExp 对象的属性不由 test 方法来修改。

function TestDemo(re, s){  
 var s1; 
// 检查字符串是否存在正则表达式。
   if (re.test(s))                 
         s1 = " contains ";           // s 包含模式。   
  else      s1 = " does not contain ";   // s 不包含模式。   
  return("'" + s + "'" + s1 + "'"+ re.source + "'"); // 返回字符串。
}

split 方法
将一个字符串分割为子字符串,然后将结果作为字符串数组返回。
stringObj.split([separator[, limit]])
参数 stringObj 必选项。要被分解的 String 对象或文字。该对象不会被 split 方法修改。
separator 可选项。字符串或 正则表达式 对象,它标识了分隔字符串时使用的是一个还是多个字符。如果忽略该选项,返回包含整个字符串的单一元素数组。
limit 可选项。该值用来限制返回数组中的元素个数。
说明
split 方法的结果是一个字符串数组,在 stingObj 中每个出现 separator 的位置都要进行分解。separator 不作为任何数组元素的部分返回。

function SplitDemo(){   var s, ss;  
 var s = "The rain in Spain falls mainly in the plain.";   // 在每个空格字符处进行分解。 
  ss = s.split(" ");   return(ss);
}

JS判断浏览器类型

没有评论

2011 年 05 月 05 日 at 上午 11:11分类:JavaScript

你知道世界上有多少种浏览器吗?除了我们熟知的IE, Firefox, Opera, Safari四大浏览器之外,世界上还有近百种浏览器。
几天前,浏览器家族有刚诞生了一位小王子,就是Google推出的Chrome浏览器。由于Chrome出生名门,尽管他还是个小家伙,没有人敢小看他。以后,咱们常说浏览器的“四大才子”就得改称为“五朵金花”了。
在网站前端开发中,浏览器兼容性问题本已让我们手忙脚乱,Chrome的出世不知道又要给我们添多少乱子。虽然说现在有一些WEB技术(AJAX、ExtJS等)已经帮我们处理不同浏览器的兼容问题,但是有时候自己来解决岂不是更好,浏览器兼容性是前端开发框架要解决的第一个问题,要解决兼容性问题就得首先准确判断出浏览器的类型及其版本。
JavaScript是前端开发的主要语言,我们可以通过编写JavaScript程序来判断浏览器的类型及版本。JavaScript判断浏览器类型一般有两种办法,一种是根据各种浏览器独有的属性来分辨,另一种是通过分析浏览器的userAgent属性来判断的。在许多情况下,值判断出浏览器类型之后,还需判断浏览器版本才能处理兼容性问题,而判断浏览器的版本一般只能通过分析浏览器的userAgent才能知道。
我们先来分析一下各种浏览器的特征及其userAgent。
IE
只有IE支持创建ActiveX控件,因此她有一个其他浏览器没有的东西,就是ActiveXObject函数。只要判断window对象存在 ActiveXObject函数,就可以明确判断出当前浏览器是IE。而IE各个版本典型的userAgent如下:
Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0)
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2)
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Mozilla/4.0 (compatible; MSIE 5.0; Windows NT)
其中,版本号是MSIE之后的数字。
Firefox
Firefox中的DOM元素都有一个getBoxObjectFor函数,用来获取该DOM元素的位置和大小(IE对应的中是 getBoundingClientRect函数)。这是Firefox独有的,判断它即可知道是当前浏览器是Firefox。Firefox几个版本的 userAgent大致如下:
Mozilla/5.0 (Windows; U; Windows NT 5.2) Gecko/2008070208 Firefox/3.0.1
Mozilla/5.0 (Windows; U; Windows NT 5.1) Gecko/20070309 Firefox/2.0.0.3
Mozilla/5.0 (Windows; U; Windows NT 5.1) Gecko/20070803 Firefox/1.5.0.12
其中,版本号是Firefox之后的数字。
Opera
Opera提供了专门的浏览器标志,就是window.opera属性。Opera典型的userAgent如下:
Opera/9.27 (Windows NT 5.2; U; zh-cn)
Opera/8.0 (Macintosh; PPC Mac OS X; U; en)
Mozilla/5.0 (Macintosh; PPC Mac OS X; U; en) Opera 8.0
其中,版本号是靠近Opera的数字。
Safari
Safari浏览器中有一个其他浏览器没有的openDatabase函数,可做为判断Safari的标志。Safari典型的userAgent如下:
Mozilla/5.0 (Windows; U; Windows NT 5.2) AppleWebKit/525.13 (KHTML, like Gecko) Version/3.1 Safari/525.13
Mozilla/5.0 (iPhone; U; CPU like Mac OS X) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/4A93 Safari/419.3
其版本号是Version之后的数字。
Chrome
Chrome有一个MessageEvent函数,但Firefox也有。不过,好在Chrome并没有Firefox的getBoxObjectFor 函数,根据这个条件还是可以准确判断出Chrome浏览器的。目前,Chrome的userAgent是:
Mozilla/5.0 (Windows; U; Windows NT 5.2) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.27 Safari/525.13
其中,版本号在Chrome只后的数字。
有趣的是,Chrome的userAgent还包含了Safari的特征,也许这就是Chrome可以运行所有Apple浏览器应用的基础吧。
只要了解了以上信息,我们就可以根基这些特征来判断浏览器类型及其版本了。我们会将判断的结果保存在Sys名字空间中,成为前端框架的基本标志信息,供今后的程序来读取。如果判断出谋种浏览器,Sys名字空间将有一个该浏览器名称的属性,其值为该浏览器的版本号。例如,如果判断出IE 7.0,则Sys.ie的值为7.0;如果判断出Firefox 3.0,则Sys.firefox的值为3.0。下面是判断浏览器的代码:

    <script type="text/javascript">
        var Sys = {};
        var ua = navigator.userAgent.toLowerCase();
        if (window.ActiveXObject)
            Sys.ie = ua.match(/msie ([\d.]+)/)[1]
        else if (document.getBoxObjectFor)
            Sys.firefox = ua.match(/firefox\/([\d.]+)/)[1]
        else if (window.MessageEvent && !document.getBoxObjectFor)
            Sys.chrome = ua.match(/chrome\/([\d.]+)/)[1]
        else if (window.opera)
            Sys.opera = ua.match(/opera.([\d.]+)/)[1]
        else if (window.openDatabase)
            Sys.safari = ua.match(/version\/([\d.]+)/)[1];
        
        //以下进行测试
        if(Sys.ie) document.write('IE: '+Sys.ie);
        if(Sys.firefox) document.write('Firefox: '+Sys.firefox);
        if(Sys.chrome) document.write('Chrome: '+Sys.chrome);
        if(Sys.opera) document.write('Opera: '+Sys.opera);
        if(Sys.safari) document.write('Safari: '+Sys.safari);
    </script>

我们把对IE的判断放在第一,因为IE的用户最多,其次是判断Firefox。按使用者多少的顺序来判断浏览器类型,可以提高判断效率,少做无用功。之所以将Chrome放在第三判断,是因为我们预测Chrome很快会成为市场占有率第三的浏览器。其中,在分析浏览器版本时,用到了正则表达式来析取其中的版本信息。
如果你的JavaScript玩得很高,你还可以将前面的判断代码写成这样:

 <script type="text/javascript">
        var Sys = {};
        var ua = navigator.userAgent.toLowerCase();
        window.ActiveXObject ? Sys.ie = ua.match(/msie ([\d.]+)/)[1] :
        document.getBoxObjectFor ? Sys.firefox = ua.match(/firefox\/([\d.]+)/)[1] :
        window.MessageEvent && !document.getBoxObjectFor ? Sys.chrome = ua.match(/chrome\/([\d.]+)/)[1] :
        window.opera ? Sys.opera = ua.match(/opera.([\d.]+)/)[1] :
        window.openDatabase ? Sys.safari = ua.match(/version\/([\d.]+)/)[1] : 0;
        
        //以下进行测试
        if(Sys.ie) document.write('IE: '+Sys.ie);
        if(Sys.firefox) document.write('Firefox: '+Sys.firefox);
        if(Sys.chrome) document.write('Chrome: '+Sys.chrome);
        if(Sys.opera) document.write('Opera: '+Sys.opera);
        if(Sys.safari) document.write('Safari: '+Sys.safari);
    </script>

这样可以使JavaScript代码更精简些。当然,可读性稍差一些,就看你是重视效率还是重视可维护性了。
使用不同特征来判断浏览器的方法,虽然在速度上比用正则表达式分析userAgent要来的快,不过这些特征可能会随浏览器版本而变化。比如,一种浏览器本来独有的特性取得了市场上的成功,其他浏览器也就可能跟着加入该特性,从而使该浏览器的独有特征消失,导致我们的判断失败。因此,相对比较保险的做法是通过解析userAgent中的特征来判断浏览器类型。何况,反正判断版本信息也需要解析浏览器的userAgent的。
通过分析各类浏览器的userAgent信息,不难得出分辨各类浏览器及其版本的正则表达式。而且,对浏览器类型的判断和版本的判断完全可以合为一体地进行。于是,我们可以写出下面的代码:

<script type="text/javascript">
        var Sys = {};
        var ua = navigator.userAgent.toLowerCase();
        var s;
        (s = ua.match(/msie ([\d.]+)/)) ? Sys.ie = s[1] :
        (s = ua.match(/firefox\/([\d.]+)/)) ? Sys.firefox = s[1] :
        (s = ua.match(/chrome\/([\d.]+)/)) ? Sys.chrome = s[1] :
        (s = ua.match(/opera.([\d.]+)/)) ? Sys.opera = s[1] :
        (s = ua.match(/version\/([\d.]+).*safari/)) ? Sys.safari = s[1] : 0;

        //以下进行测试
        if (Sys.ie) document.write('IE: ' + Sys.ie);
        if (Sys.firefox) document.write('Firefox: ' + Sys.firefox);
        if (Sys.chrome) document.write('Chrome: ' + Sys.chrome);
        if (Sys.opera) document.write('Opera: ' + Sys.opera);
        if (Sys.safari) document.write('Safari: ' + Sys.safari);
    </script>

其中,采用了“… ? … : …”这样的判断表达式来精简代码。判断条件是一条赋值语句,既完成正则表达式的匹配及结果复制,又直接作为条件判断。而随后的版本信息只需从前面的匹配结果中提取即可,这是非常高效的代码。
以上的代码都是为了打造前端框架所做的预研,并在五大浏览器上测试通过。今后,判断某种浏览器只需用if(Sys.ie)或 if(Sys.firefox)等形式,而判断浏览器版本只需用if(Sys.ie == ’8.0′)或if(Sys.firefox == ’3.0′)等形式,表达起来还是非常优雅的。
前端框架项目已经启动,一切就看过程和结果了…