blog | 业余项目 | 关于Karry | RSS订阅 | 我在阅读 | 管理

Archive for the ‘Javascript’ Category

javascript设定文本框中的指定文字为选中状态

星期六, 六月 5th, 2010

前段时间写了一个获取网页中鼠标选中的文字,这个是逆向操作,通过javascript设置文本框中的指定文字为选定状态。

var PG={
    isIE:!-[1,],  //判断浏览器是否为IE,为IE时返回true
    /*
    * 设置文本框中的文字为选中状态,包括开始索引,不包括结束索引
    * param domObj 文本框的dom对象,start为开始索引,不填则从0开始, end为结束索引,不填则选中到文本结束
    * 若domObj参数不填,则直接返回为false.
    */
    setSelectText:function(domObj,start,end){
        if(typeof domObj !=="object"){
            return false;
        }
        start = start ||0;
        end = end || domObj.value.length
        domObj.focus();
        if(this.isIE){
            var range = domObj.createTextRange();
            range.move("character",start);
            range.moveEnd("character",end-start);
            range.select();
        }else{
            domObj.setSelectionRange(start,end)
        }
    }
}

【测试页面】

获取网页中鼠标选中的文字

星期天, 五月 16th, 2010

最近在做一个chrome插件,需要用到捕获鼠标选中的文字,发现各浏览器兼容性解决起来挺麻烦,总结了一下,方便以后使用。

直接看代码:

 var PG={
            isIE:!-[1,],  //判断浏览器是否为IE,为IE时返回true
            getText:function(){
                 if(this.isIE){
                     return this._getText();
                }else{
                    var element = document.activeElement;
                    if (element && (element.tagName.toLowerCase() == 'input' || element.tagName.toLowerCase() == 'textarea')) {
                        return this._getTextInput()
                    } else {
                        return this._getText()
                    }
                }
            },
            _getText:function(){
                if (window.getSelection) {
                    return window.getSelection().toString();
                } else if (document.selection && document.selection.createRange) {
                    return document.selection.createRange().text;
                }
                return "";
            },
            //获取非IE浏览器中input框和textarea框中的文字
            _getTextInput:function(){
                var element = document.activeElement;
                if (element.selectionStart != undefined && element.selectionEnd != undefined) {
                    return element.value.substring(element.selectionStart, element.selectionEnd);
                }
                return "";
            }
        }

使用方法:

 document.onmouseup = function(){
            alert("您选中的文字是:"+PG.getText());
        }

点击查看:[测试示例]

参考:
PPK :Introduction to Range
getSelection method (window)
document.selection.createRange方法

DOMContentLoaded事件在IE中的实现机制

星期一, 五月 3rd, 2010

在所有非IE浏览器中都有DOMContentLoaded事件,也就是DOM结构加载完毕之后执行的事件,有别于onload事件必须页面上的所有元素(比如图片和flash)加载完毕才执行,通常该事件用于绑定dom事件,操作dom等。而IE中缺乏对该事件的支持,几乎所有的javascript框架都为IE实现了该事件,但实现方式各不相同。comojs有Como.onloadHandler,jQuery里有$(document).ready,YUI里有onDOMReady。

加载script标签

comojs当前采用的这种方式,Dojo和比较老的jQuery版本也采用的这种方式。


var src = 'javascript: void(0)';
if(window.location.protocol == 'https:'){
    src = '//:';
}
document.write('<script onreadystatechange="if
(this.readyState==\'complete\') {this.parentNode.removeChild(this);
Como.Hook._onloadHook();}" defer="defer" ' + 'src="' + src + '">
<\/script\>');

这个原理比较简单,在页面中插入一个script标签。利用IE支持defer属性的特点,使插入的这个script标签在文档加载完毕之后才执行,然后利用script标签的onreadystatechange函数实现ready事件。

利用doScroll()

这是目前最主流的解决方案jQuery、Ext、YUI、Mootool都用这种方式处理。原理是利用IE浏览器支持的doScroll,当DOM未加载完毕时,执行doScroll时会抛出异常。利用这个特定定时执行doScroll,直到不抛异常,说明DOM加载完成。

// If IE and not an iframe
// continually check to see if the document is ready
if ( document.documentElement.doScroll && typeof window.frameElement === "undefined" )
(function(){
	if ( jQuery.isReady ) return;
	try {
		// If IE is used, use the trick by Diego Perini
		// http://javascript.nwbox.com/IEContentLoaded/
		document.documentElement.doScroll("left");
	} catch( error ) {
		setTimeout( arguments.callee, 0 );
		return;
	}
	// and execute any waiting functions
	jQuery.ready();
})();

监测document, document.getElementByTagName及document.body

设置定时器不断监测document的相关属性和方法是否已存在,如果存在,说明dom加载完毕。

domReady.timer = setInterval(isDomReady, 13);
function isDomReady() {
    if (domReady.done) return false;
    if (document && document.getElementsByTagName && document.getElementById && document.body) {
        clearInterval(domReady,timer);
        domReady.timer = null;
        //执行ready函数。
    }
}
好消息一则:IE9支持DOMContentLoaded事件。

参考文档

http://developer.yahoo.com/yui/docs/YAHOO.util.Event.html#onContentReady

http://peter.michaux.ca/articles/the-window-onload-problem-still

javascript单元测试工具QUnit介绍

星期六, 四月 24th, 2010

[点击查看示例]

QUnit 介绍:

QUnit是jQuery团队开发的JavaScript单元测试工,使用起来非常方便。有漂亮的外观和完整的测试功能(包括异步测试)。

由于存在浏览器解析环境、用户操作习惯等差异,前端程序的许多问题是无法捕捉或重现的,现在前端程序的测试多是黑盒测试,靠模仿用户不停的点击来寻找程序bug。这种方式既费时费力,又无法保证测试的覆盖面。

随着前端逻辑和交互越来越复杂,和其他编程语言一样,一个函数,一个模块,在修改bug或添加新功能的过程中,很容易就产生新的bug,或使老的bug复活。这种情况下,反复进行黑盒测试效率很低,如果测试工作交给QA去做,更是怨声载道。此外,浏览器兼容性测试是前端程序测试的重要一环,在多个浏览器之间测试前端程序,黑盒测试的工作量就会成倍增加。这时候单元测试就体现出了优势,你只需要写一份单元测试代码,到各给浏览器跑一遍,就可以轻松看到不同的浏览器导致的BUG。有新功能的增加或修改时,只需要跑一下单元测试,问题就一目了然。同时,单元测试本身就是一份良好的说明文档,而且总是能跟着程序保持同步。

使用方法:

QUnit虽然是jQuery团队开发,但是并不依赖jQuery,使用QUnit也很方便,只需要引入一个css和一个js文件即可。

<link rel="stylesheet" href="http://github.com/jquery/qunit/raw/master/qunit/qunit.css" type="text/css" media="screen" />
<script type="text/javascript" src="http://github.com/jquery/qunit/raw/master/qunit/qunit.js"></script>

你当然可以把这两个文件下载到本地直接使用。

测试方法介绍:

module(string,lifecycle)声明一个测试模块。第一个个参数为必填,第二参数可选。如:

module("doAdd方法测试模块");

test( name, expected, testfuction )开始一个测试。第一个参数为必填,测试名字,字符串。第二个参数为选填,预期的测试个数,数字。第三个参数为测试的方法。如:

test('doAdd(a,b)方法测试',function() {
    expect(1);
    equals(doAdd(2,5), 7, '2+5等于7');
});

ok( state, message ) 布尔断言,第一个参数是true时,表示测试通过,第二个参数选填,附加信息。

equals( actual, expected, message )等价判断,第一个参数为实际的执行结果,必填。第二个参数为预期结果,必填。当两个结果相等时,测试通过,否则测试不通过。第三个参数为附加信息。但要注意的是,equals适用于判断字符串、数字等是否相等。对于数组、object、或自定义类别的对比,应该使用same()函数测试。
same( actual, expected, message ) 与equal用法一样,适合判断数组、object等的等价。
expect( number ) 测试个数预期,与test函数的第二个参数等价。如果实际测试次数和预期值不符合,则测试不通过。

asyncTest( name, expected, test ) 异步测试。该测试模块并不立即执行,等到start()函数出现时才开始执行,适合事件、ajax等异步函数的测试。
start()开始执行这个测试模块。
stop()暂停执行这个测试模块,直到start()函数重新开启。如:

module("异步测试模块");
asyncTest("事件测试", function() {
    expect(1);
    document.body.onclick = function(event){
        start();
        equals(event,"[object MouseEvent]","event值");
    }
});
相关文档:http://docs.jquery.com/QUnit

关于“跨域”问题的总结

星期一, 四月 19th, 2010

什么是同源策略?

同源策略阻止从一个域上加载的脚本获取或操作另一个域上的文档属性。也就是说,受到请求的 URL 的域必须与当前 Web 页面的域相同。这意味着浏览器隔离来自不同源的内容,以防止它们之间的操作。这个浏览器策略很旧,从 Netscape Navigator 2.0 版本开始就存在。

URL 说明 是否允许通信
http://www.playgoogle.com/lab/a.js

http://www.playgoogle.com/script/b.js

同一域名下不同文件夹 允许
http://www.playgoogle.com/a.js

http://www.playgoogle.com/b.js

同一域名下 允许
http://www.playgoogle.com:8000/a.js

http://www.playgoogle.com/b.js

同一域名,不同端口 不允许
http://www.playgoogle.com/a.js

https://www.playgoogle.com/b.js

同一域名,不同协议 不允许
http://www.playgoogle.com/a.js

http://61.129.251.35/b.js

域名和域名对应ip 不允许
http://www.playgoogle.com/a.js

http://script.playgoogle.com/b.js

主域相同,子域不同 不允许
http://www.test.com/a.js

http://www.playgoogle.com/b.js

不同域名 不允许

常用的跨域解决方案

jsonp(JSON with Padding): 简单点说,异步请求跨域的服务器端时,不是直接返回数据,而是返回一个js方法,把数据作为参数传过来。这需要服务器端稍作修改,如果只是跨域传递数据(而不是DOM操作),个人认为这种方式是最好的。参考内容:使用 JSONP 实现跨域通信

服务器端代理: 由js请求同源的服务器端,再由服务器端做代理来实现跨域的请求。参考内容:ajax跨域之服务端代理

动态创建script:虽然浏览器默认禁止了跨域访问,但并不禁止在页面中引用其他域的JS文件,并可以自由执行引入的JS文件中的function,根据这一点,可以方便地通 过创建script节点的方法来实现完全跨域的通信有点XSS跨站脚本攻击的意思,所以安全性方面需要仔细评估。这也是所有GreaseMonkey的实现方式,比如sogou的云输入法

document.domain:仅适用于不同的二级域名之间的跨域访问。

window.name:name 在浏览器环境中是一个全局/window对象的属性,且当在 frame 中加载新页面时,name 的属性值依旧保持不变。通过在 iframe 中加载一个资源,该目标页面将设置 frame 的 name 属性。此 name 属性值可被获取到,以访问 Web 服务发送的信息。参考使用 window.name 解决跨域问题

window.location.hash:hash可以实现跨域传值从而达到跨域通讯的目标。为了及时捕获Hash值的修改,需要轮询实现。如果是iframe和主页面不在同一个域下而要进行通讯,这是不错的方式。不过会导致一个问题,在chrome和firefox这类浏览器中,hash值的修改会在浏览器中保存记录,扰乱正常的前进和后退功能。

iframe 代理:iframe和主页面不在同一个域,但iframe 需要操作主页面的DOM结构,这个时候用iframe代理可实现,比如iframe高度自适应,只要在iframe中再插入一个与主页面同域的iframe,把参数传递过去即可。

介绍javascript 中Object的几个方法

星期二, 三月 23rd, 2010

hasOwnProperty(property)

判断对象是否有某个特定的属性。必须用字符串指定该属性。(例如,o.hasOwnProperty(“name”)),返回布尔值。此方法无法检查该对象的原型链中是否具有该属性;该属性必须是对象本身的一个成员。

如下代码:

var s =”";

alert(s.hasOwnProperty(“split”)); èreturn false

alert(String.prototype.hasOwnProperty(“split”));èreturn true

isPrototypeOf(object)

判断该对象是否为另一个对象的原型。

obj1.isPrototypeOf(obj2);

obj1是一个对象的实例;obj2是另一个将要检查其原型链的对象。原型链可以用来在同一个对象类型的不同实例之间共享功能。如果obj2的原型链中包含obj1,那么isPrototypeOf 方法返回 true。如果obj2不是一个对象或者obj1没有出现在obj2中的原型链中,isPrototypeOf 方法将返回 false

propertyIsEnumerable()

判断给定的属性是否可以用 for…in 语句进行枚举,返回布尔值。

toString()

返回对象的原始字符串表示。对于 Object 对象,ECMA-262 没有定义这个值,所以不同的 ECMAScript 实现具有不同的值。

toLocaleString()

Object中和toString()方法的实现方式一样,但在其他类中有特定的实现,如在DatetoLocaleString() 方法可根据本地时间把 Date 对象转换为字符串,并返回结果。

valueOf()

返回最适合该对象的原始值。对于许多对象,该方法返回的值都与 toString() 的返回值相同。

以上6个方法都是Object.prototype上定义的,ECMAScript 中的所有对象都由Object继承而来,所以在ECMAScript上的所有对象都具有以几个方法。

javascript 小测试

星期一, 三月 1st, 2010

javascript小测试,弄清楚这些题目可以帮助你更好的理解javascript语言的一些特性,比如运算符的优先级、作用域链、强制数据转换、函数表达式等。

点击这里有这些题的运算结果。

先看题:

example 1

    var num1 = 5,
    num2 = 10,
    result = num1+++num2;
    rusult 的值是多少,num1和num2 的值分别是多少?

example 2

    var x = 5,
        o = {
            x: 10,
            doIt: function doIt(){
                var x = 20;
                setTimeout(function(){
                    alert(this.x);
                }, 10);
            }
        };
    o.doIt();

example 3

   var num1 = "10",
    num2 = "9";
    num1 < num2 的值是?
    +num1 < num2 的值是?
    num1 + num2 的值是?
    +num1 + num2 的值是?

example 4

  var message = "Hello world!";
    message.substring(1, 4)值是?
    message.substr(1,4)值是?

example 5

    var o = {
        x: 8,

        valueOf: function(){
            return this.x + 2;
        },
        toString: function(){
            return this.x.toString();
        }
    },
    result = o < "9";
    alert(o);
    result 的结果是?
    alert 打印的结果是?

example 6

      (function(){
          return typeof arguments;
        })();

example 7

        var f = function g(){ return 23; };
        typeof g();

example 8

  (function(x){
        delete x;
        return x;
    })(1);

example 9

    var y = 1, x = y = typeof x;
    x的值是?

example 10

    (function f(f){
        return typeof f();
    })(function(){ return 1; });
====================================分割线====================================
解答:

example 1

result 的值是15,num1为6,num2为10
下表按从最高到最低的优先级列出JavaScript运算符。具有相同优先级的运算符按从左至右的顺序求值。

运算符 描述
. [] () 字段访问、数组下标、函数调用以及表达式分组
++ — – ~ ! delete new typeof void 一元运算符、返回数据类型、对象创建、未定义值
* / % 乘法、除法、取模
+ – + 加法、减法、字符串连接
<< >> >>> 移位
< <= > >= instanceof 小于、小于等于、大于、大于等于、instanceof
== != === !== 等于、不等于、严格相等、非严格相等
& 按位与
^ 按位异或
| 按位或
&& 逻辑与
|| 逻辑或
?: 条件
= oP= 赋值、运算赋值
, 多重求值

很明显,”++”为一元运算符,优先级要高于”+”加法运算符。所以,result=num1+++num2 等价于 result = (num1++)+num2,又因为++运算符后置,后缀式运算符是在计算过包含它们的表达式后才进行增量或减量运算的。所以result 的值是15。

example 2

该题的结果是5.涉及到了作用域链(JavaScript scope)的知识,setTimeout 中的函数的的scope会放入global scope, “this”指向window,所以会找到全局变量x。

example 3

字符串比较大小,会把字符转换为ASCII 码来对比,是逐个字符比较,如果相等,再比较下一个字符,否则返回比较结果。“10”的第一个字符‘1’比‘9’的ASCII要小。所以,”10″<”9″会返回 true.

字符串前面的”+”运算符会把字符串转换为数字。当一个是字符串变量,一个是数字比较大小时,这里还包括字符串变量是数字还是字母。当字符串是数字时,ECMAscript规定自动转换为数字类型,当是字母时,转换为数字时会转换为NaN,当NaN和数字比较时不论大小都返回false。

example 4

substring(index1,index2)的第二个参数是截取结束字符的index值。

substr(index1,length) 的第二个参数是截取的字符串的长度。

注:substr() 在ECMA中已建议弃用

example 5

在javascript中,object的prototype中已有两个方法valueOf和toString,前者在获取对象的值时自动调用,后者在转换为字符串的时候自动调用。该题中对象o重写了这两个方法,所以result=o<”9″ 调用了o的valueOf方法,转换为result =10<”9″,根据第二题,9自动会转换为数字,所以返回为false.而在执行alert(o)时,会自动调用toString方法,打印出来是8

example 6

标识符arguments本质上是个局部变量,在每个函数中都会被自动声明并被初始化,是一个特殊的数组,继承自object,typeof 打印出来为“object”.

example 7

在IE下返回为number,在FF下报“ReferenceError: g is not defined”的异常。具体情况还未知,有知道的告诉我。

example 8

通过var声明的变量和通过function声明的函数拥有DontDelete特性,无法被删除.详细可参考这篇文章

example 9

x值为undefine(而不是报异常),JavaScript引擎会优先解析var变量和function定义。在预解析完成后,才会执行代码。 详见lifesinger的《JavaScript运行机制浅探

example 10

number,注意f后面那个括号。

img标签src为空的陷阱

星期六, 一月 2nd, 2010

把页面中的img标签的src设置为“”存在巨大的风险,无论是在html中写入

<img src=”" />

还是在js中写入

var img = new Image(); img.src = “”;

出现一次这样的标签会导致向你的服务器多做一次请求。

  • 在IE中,这样做会请求一次当前页面所在的目录。如在http://playgoogle.com/demo/a.html 中出现这种空src的标签,会导致重新请求一次://playgoogle.com/demo/
  • 在Safari 和 Chrome中,将请求当前页面本身。
  • 在Firefox 3.5以前的版本中,有和Safari同样的问题,但是在3.5中修正了这个BUG。
  • 在Opera 中,不会做额外的请求。

在一个访问量不高的网站中,多一个这样的请求也无所谓(甚至可以让你的网站浏览看上去翻番),但在一个千万级访问量甚至更高的WEB站点里,这样会导致你的服务器和带宽的成本显著增加。 另外一个隐患是,重新请求某个页面可能会导致用户的一些信息被无意中修改,例如cookies,或者ajax操作。

你永远不会写出这样的代码?

我并不这么认为,很多时候这种情况在无意中出现,比如下面这段php代码:

<img src="$imageUrl" >

你原计划是从服务器端读取这个src地址,但是由于某个原因,这个地址还未设置,或者代码的BUG导致读取失败,就会出现空的src标签。

其他的标签中的空src会不会导致这样的问题?

好消息是,在IE中只有image标签有这个问题。坏消息是,在Chrome, Safari, 和 Firefox中<script src=""><link href="">都会导致出现一个新的请求。

如何解决这个问题?

可以从两方面着手,一是尽量避免这种坏的编程方式,不要出现空的src标签。另外,可以从服务器端着手,在发现时这种无意义的请求时不要返回任何东西给客户端。

<?php
    //Works for IE only when using path URLs and not file URLs

    //get the referrer
    $referrer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';

    //current URL (assuming HTTP and default port)
    $url = "http://" . $_SERVER['HTTP_HOST']  . $_SERVER['REQUEST_URI'];

    //make sure they're not the same
    if ($referrer == $url){
        exit;
    }
?>

原文网址:

Empty image src can destroy your site

国产JS框架–Como JS 1.0 正式发布

星期三, 十二月 23rd, 2009

综合了多个框架的优势,希望能给各位开发人员带来更大的方便。

由于框架刚发布,会存在很多不足之处,我们会在后续不断推出新的版本,也希望大家有任何意见或建议与我们联系。

Email/Gtalk/MSN: KevinComo@gmail.com

Como 是一款代码简易而功能强大的Javascript框架,这也是开发者在工作过程中的经验总结,实用性强;通过它,能够简化Javascript代码开发,增强代码重用性,能够异步按需加载js和css文件,增强page的加载速度;
Como完全兼容市场目前的主流浏览器:Internet Explorer6+, Firefox 2, Safari 2+, Chrome, Opera 9+, 及其他基于IE内核的浏览器如Maxthon、360、Sogou等
待Como完善成一套框架体系后,我们将提供出一套基于BSD协议的开源方案,供大家学习和分享。我们也将逐步推出一些扩展组件,比如编辑器等。

递归练习,做个奥数题

星期三, 十月 28th, 2009

今天有个朋友问我一个小学生的奥数题,酒1元钱1瓶,2个空瓶子可以换一瓶酒,问20块钱可以喝多少瓶酒。我OUT了,我被鄙视了。

我不会,但是谁让我是写代码的呢,写了个小程序用来鄙视他。告诉他,我不仅知道20块能买多少酒,你就是20000块我也能算出来。

var allWine = 0;  // 20块钱可以买20瓶酒
var otherBottle = 0;
//递归函数,瓶换酒
function bottleExchangeWine(bottle)
{
    if(bottle % 2==1){ //保证瓶子的数量为偶数
        if(otherBottle>=1){//若不为偶数,且之前有剩余的空瓶子
        bottle += 1;//加一个瓶子
        otherBottle -= 1;
        }else{
            bottle -= 1
            otherBottle +=1;
        }
    }
    var newWine =bottle/2;
    allWine +=newWine ;
    if(newWine>=1)
    {
        bottleExchangeWine(newWine);
    }
    return;
   
}
function getWine(){
    otherBottle = 0;
    allWine = parseInt(document.getElementById("txtMoney").value);
    bottleExchangeWine(allWine);
    alert("共有:"+allWine+"瓶酒,共有:"+otherBottle+"个空瓶子还没有换!");
}

点击查看