Archive for Javascript

笔记:canvas 支持retina显示解决模糊的问题

之所以模糊是在浏览器上生成的图片是1x的非矢量图,所以在retina屏幕上会出现模糊的问题(手机版的浏览器无此问题),以下是我整理的一段代码。

原理很简单,先算出 ratio 值(2,3等)然后根据 ratio 值对 canvas 的 width 和 height 调整,并且一定要对

canvas.getContext("2d")

获取的对象进行 scale(ratio, ratio) 相同的倍数,否则图片会被拉小。

function reviseRatio(canvas, width, height)
{
    var context = canvas.getContext("2d");
    var backingStoreRatio = context.webkitBackingStorePixelRatio ||
        context.mozBackingStorePixelRatio ||
        context.msBackingStorePixelRatio ||
        context.oBackingStorePixelRatio ||
        context.backingStorePixelRatio || 1;

    var devicePixelRatio = window.devicePixelRatio || 1;

    // canvas的实际渲染倍率
    var ratio = devicePixelRatio / backingStoreRatio;

    if (ratio > 1)
    {
         canvas.width = width * ratio;
         canvas.height = height * ratio;
         context.scale(ratio, ratio);
     }
    else
    {
        canvas.width = width;
        canvas.height = height;
    }
    canvas.style.width = width;
    canvas.style.height = height;
}

直接调用方法

reviseRatio(canvas, 100, 80);

即可修正参数。

其中, canvas 是需要修正的canvas的dom对象,100是显示的宽度,80是高度(会渲染出一个 200 * 160 的canvas的图片)

让jQuery支持Ajax的Streaming获取推送消息回调执行JS,并支持http的分块协议

我曾经在2011年写过一个关于ajax使用Streaming获取推送数据的文章:
https://www.queyang.com/blog/archives/54 让ajax更加完美——Ajax Streaming研究和使用

Ajax Streaming常用的场景:

  • 你通过AJAX向服务器提交一个请求,服务器会在接下来一段时间(比如1分钟)向客户端推送一些执行的信息,这样可以实时的看到进度,而传统的方式只能等到服务器执行完毕才能知道结果。
  • 通过ajax获取服务器上一些实时数据,却不想使用定时(比如间隔1秒)ajax不断的请求
  • 不需要客户端向服务器反馈交互的服务器推送数据显示方式

当然,在html5中有websocket可以满足页面和服务器交互的操作,这个是ajax stream所做不到的。

之前那篇文章简单介绍了使用stream的原理,然而jQuery一直不支持,今天闲来写了一个扩展jQuery的插件,可以让jQuery直接支持使用,增加了一个 message 的参数,当收到内容当前的内容会存放在 jqXHR.currentResponseText 中并回调message方法执行(注意:老版本的IE浏览器不支持,若需要支持,得用那篇在2011年写的那个文章的模拟方法)

此扩展支持HTTP的分块协议,可以自动将分块的长度数据给自动过滤只保留正文部分,HTTP分块协议见: http://zh.wikipedia.org/wiki/分块传输编码

插件扩展

(function ($) {
    var ajax = $.ajax;

    $.ajax = function (options)
    {
        var jqXHR;
        if ($.isFunction(options.message))
        {
            if ($.isFunction(options.xhr))
            {
                options._xhr = options.xhr;
            }
            options.xhr = function() {
                try
                {
                    var xmlhttp = $.isFunction(this._xhr)? this._xhr() : new XMLHttpRequest();

                    var self = this;
                    xmlhttp.streaming = {
                        oldResponseLength : 0
                    };

                    var hexdec = function (hex_string)
                    {
                        hex_string = (hex_string + '').replace(/[^a-f0-9]/gi, '');
                        return parseInt(hex_string, 16);
                    };

                    /**
                     * 将HTTP协议中定义分块大小的长度部分移除
                     *
                     * @see http://zh.wikipedia.org/wiki/%E5%88%86%E5%9D%97%E4%BC%A0%E8%BE%93%E7%BC%96%E7%A0%81
                     * @param data
                     * @returns string
                     */

                    var format = function(data)
                    {
                        if (data==="0\r\n\r\n")return '';   //分块输出结束符

                        data = $.trim(data);
                        var d = data.indexOf("\r\n");
                        if (d)
                        {
                            var len = hexdec(data.substr(0, d));

                            if (len>0 && len + 2 + d == data.length)
                            {
                                data = data.substr(d+2);
                            }
                        }

                        return data;
                    };

                    xmlhttp.onreadystatechange = function()
                    {
                        if (this.readyState == 3)
                        {
                            var data = this.responseText;
                            if (this.streaming.oldResponseLength>0)
                            {
                                data = data.substr(this.streaming.oldResponseLength);
                            }
                            this.streaming.oldResponseLength = this.responseText.length;

                            data = format(data);
                            if (data.length>0)
                            {
                                jqXHR.currentResponseText = data;
                                self.message.call(self, jqXHR, 'stream');
                            }
                        }
                    };
                    return xmlhttp;
                }catch(e){}
            };

            var obs = null;
            if ($.isFunction(options.beforeSend))
            {
                obs = options.beforeSend;
            }

            options.beforeSend = function(xhr) {
                jqXHR = xhr;
                if (obs)return obs.apply(this, arguments);
            };
        }

        return ajax(options);
    }
})(jQuery);

使用方法:

$.ajax({ url: "test.php", complete: function(){
    console.log('done');
}, message: function(xhr) {
    // 当收到消息时调用
    console.log(xhr.currentResponseText);
}});

test.php 代码例子

<?php
// 先输出1024长度内容,避免firefox等不识别
echo str_pad('', 1024), "\r\n";
flush();   // 将内容推送到浏览器

for($i=0; $i<10; $i++)
{
    echo 'step:'.$i.' time:'.time();
    flush();  //推送数据
    sleep(1);
}

谈谈AngularJS的优缺点以及如何取舍

AngularJS可以说是一个非常完美的JS框架。先通过这篇文章来看看AngularJS的几大特性 http://blog.csdn.net/vking_wang/article/details/8817232
文章总结了有这些特性

  • 特性一:双向数据绑定
  • 特性二:模板
  • 特性三:MVC
  • 特性四:依赖注入(Dependency Injection,DI)
  • 特性五:Directives(指令)

再来看看我之前写的一篇文章:https://www.queyang.com/blog/?p=419

想必如果你是一个最求时髦的前端攻城狮一定已经蠢蠢欲动,想在手上的项目里开始小试牛刀了吧。
但是,在上项目前,你务必要了知道AngularJS的脾性,其实在如今的网站实例中,还是有很多类型的不适用它的。那么先说说它比较合适使用的场景:

首先是网站的后台

网站的后台通常都是功能复杂、模块化比较清晰的页面,而且不需要考虑SEO相关的场景,另外就是对浏览器的要求不如前端那么苛刻,你的很多想法可以在后台里体现出来。不过就我了解的情况来看,后台大多是讲究实用的地方,不需要华丽的界面,不需要超前的技术,只要你能赶紧实现一些需求,并且大部分是和服务器、数据库打交道,所以这时候就没前端攻城狮什么事情了,除非你是即做后端又热爱后端的人,比如像我这样的,丑陋的后台是我受不了的。
所以这样来看,AngularJS在后台里只能算是一个配角角色,而且能不能用不是看前端而是看做后台的同学的前端水平了。

网站用户管理页面

这些地方的页面不需要SEO但是直接面对用户,所以还是相当重要的,如果能在这样的页面用AngularJS做好那一定是件非常令人激动的事情。不过现在很多网站的用户管理页面其实还是很薄弱,可能就寥寥几页,这样AngularJS就显得有点没有太大用处的赶脚,不过,如果像淘宝这样的用户中心或者是支付宝里的页面都用上AngularJS来处理页面,那肯定是很赞的。

web应用页面

Web应用页面是非常适合AngularJS使用的,同样不需要SEO,同样可以多方模块化加载,而且经常需要页面上的调整,这样就能够发挥AngularJS的优势了。

单页应用

这样的页面在最近些年开始流行了,所有的内容在单页里,然后分不同的情景呈现给用户,一次做好就不用多改了,这样的页面可以用AngularJS来做,不过貌似也体现不出什么太大的优势来。

那么有哪些场景不适合AngularJS的使用呢?

我个人觉得目前来说期望做较好SEO的页面还是不适合用AngularJS来做,如果你用它做好页面,一定要针对SEO来做一个供蜘蛛使用的页面,否则你页面就基本进不了搜索引擎的门了
当然了,解决方案其实还是有一些的,不过现在来说一来复杂,二来不成熟,也许过些年针对AngularJS的SEO的方案会越来越简单、成熟。

那么除了对SEO有要求的页面外,还有哪些页面不适合用AngularJS呢?答曰:类WEB1.0页面,这种像新浪、搜狐这样一个页面堆叠了N多内容的老古板的页面还是用用老技术来作吧,新玩意不适合他们,呵呵。
我甚至盯着我们公司的 http://www.verycd.com/ 的首页想了老半天,如果这个首页用AngularJS做会出现怎样的情况?
这个首页是由很多模块(部件)组成的一个页面,如果按照AngularJS的完全分离的做法,这个首页在打开时或许会有N多个模板、资源页面需要通过ajax来载入,届时可能不得不担心的就是异步请求太多了,一来会拖慢浏览器端的显示速度,而来会大幅增加服务器的负担。

总的来说,对于前端,不得不考虑模块化带来的各种负面问题,那就是页面需要成倍的增加连接数量。而我曾经在负责VeryCD时甚至会把几个css合并成一个文件来载入以加快页面载入速度,所以说,在前端是接受不了N多个CSS连接、js、模板分别请求的。

除了jQuery,你该用用 AngularJS 了

AngularJS http://www.angularjs.org/ 是google在几年前打造的一个全新的JS的框架,如今已经很成熟,它的理念完全颠覆了我曾经对js的使用习惯和理解。
在js的世界里,做前端的或许没有人不知道jQuery,然而如果你掌握了AngularJS,那么你会发现她是如此的完美和与众不同。

本篇文章不会谈及技术性的问题,旨在帮你引荐AngularJS,让你对它有个初步的认识

在谈及AngularJS有多好之前,我来说说在以前我们是怎么做的:
如果以前要制作一个网站,对于前端来说需要规划整体框架的HTML,然后是包括首页等等各种页面里的内嵌的页面,当我们制作好HTML页面后还得交由网站的程序开发把类似PHP这样的服务器代码嵌入到程序视图里,在这过程中也许前端制作好的HTML到程序那会变得面目全非,甚至页面出来后和制作的HTML有很大的差异,前端和后端甚至会打起来,orz…
项目在刚刚开始制作好后还不会有什么问题,但是随着时间的推移,项目需求的不断变化,甚至经历了N个开发人员的离职、入职等等,我们通常会发现现在的项目代码完全无法直视,各种风格的JS,各种不同风格的HTML标签,或许还夹杂着程序员完全不考虑美感的代码,在还没有类似Bootstrap这样的CSS框架之前甚至会出现各种不一样的HTML布局,各种重复的HTML等等。作为项目里的人看到这样的情况会怎么想,怎么做?答案是:只能重构。这是普遍存在的问题:HTML的不断调整、人员的变动、和程序配合的调整等等,使得传统的制作网站的方式如此不堪。

jQuery是一个非常优秀的DOM操作的类库,AngularJS其实也并不排斥jQuery,反而如果你在页面里加载jQuery后它的很多DOM操作都是依赖jQuery的(页面没有载入jQuery的话AngularJS内置jqlite),所以先请不要把jQuery和AngularJS作为对立面来理解,你可以把jQuery当做AngularJS处理DOM的一个强大的助手的关系。

那么今天有了AngularJS后,原先的工作方式会发生怎样的变化呢?
依靠AngularJS强大的MVC和数据绑定功能,我们的工作可以和服务器程序最大程度的分离,我们可以让程序完全只要提供数据即可,所有的逻辑和页面呈现完全由我们来控制,不管将来怎么改动,服务器程序只要改动他们的数据内容格式,前端就负责自己的指令控制部分和模板即可。
除了和服务器程序可以最大的分离工作,前端的设计理念也会发生很多变化:
你的关注的重点是如何编写自己的指令控制,如果编写控制器以便获取需要的数据并把数据通过模板显示出来。你可以制作很多指令集,然后每个不同的页面的模板引用相应的指令即可。这相当于你制作了很多零件,然后制作页面只要把这些零件进行拼装即可。
每一个指令可以配合一个模板,每个页面也可以有一个独立的模板和控制器,然后模板里可以定义好需要用到的HTML及指令,控制器主要处理页面内的一些独立逻辑。
这样项目完成后,将来不管怎么调整,我们只要分模块进行维护即可。

在没有接触AngularJS时,这些或许看上去一头雾水,我切身的体验是:AngularJS入门好难。确实好难,一开始找不到门路。初学者会带着传统的思想去使用AngularJS,但是这是错的,你必须用数据即显示,数据即一切的思想去使用它,你不用去操作DOM,所有要操作DOM的代码请封装在指令(directive)里,在页面的控制器里,只要操作数据的相关代码,这样,你就可以很容易入门了。

那么它都有哪些优点呢?下面我来简单的说说(其实在很多人的文章里也都有)

数据绑定

在AngularJS的世界里,和DOM打交道的东西都交给directive吧(在下面会讲),你在使用时,只需要关心js里的$scope里的数据,当变量中的数据发生变化时,对应的DOM也会能够体现出来。

变量注入

相关的文章其实也很多,理论方面可以讲一大篇文章,简单的说就是AngularJS通过一些机制可以把本来属于内部的(或局部)变量在你调用自己设置的function时可以把它们注入到你的function中,这样你就可以顺利使用这些变量,比如在AngularJS里常见的代码,其中$scope就是被注入的变量,你可以直接使用

function Controller($scope) {
    $scope.test = 132;
}

 

指令控制

不知有没有人用过jQuery Mobile?在它里面对代码的处理完全是属于传统的思想理念,但是在AngularJS里会变得相当不一样,这就激发了我们全新的HTML前端设计理念的改变。并且,在AngularJS里有非常多的内置的指令功能很好用,比如a,form,ngClick,ngSubmit 等等

MVC和CMD规范

AngularJS的mvc还是很赞的,很容易使用,而且它内置CMD规范,但是CMD规范只服务于它内置的功能,所以如果你和我一样会加载很多别的类库甚至希望把css也纳入到模块加载里,你可以考虑再加一个requirejs(http://requirejs.org/) 或者国内大牛们开发的seajs(http://seajs.org/)来加载类库和css

下面推荐一些不错的文章,可以帮你学习使用AngularJS

Angular 2.0的未来展望(翻译)

js模块化加载之 seajs 和 requirejs

先来说说国产的 seajs http://seajs.org/ 在关于页面里可以看到:Sea.js 是一个开源项目,目前由阿里、腾讯等公司共同维护。
汇集了国内一流公司的大牛们的技术,显然是一个值得推荐使用的模块化加载类库了。

而 requirejs http://requirejs.org/ 则是国际流行的一个模块加载类库,拥有相当活跃的社区和第三方插件以及解决方案

就我本人来说,是首先接触使用 seajs 的,但是现在的项目我选择了使用 requirejs 。在实际使用中体会到了二者相似但各有优缺点。
seajs的优点是:国产,文档还算丰富,社区也不错,有问题在github里提交后会有人帮忙解决,并且它支持css的加载,这个 requirejs 是不支持的。
requirejs 的优点则是细节做的比较到位,可以设置资源加载成功的回调方法,js的以来关系等等,这些我在 seajs 里找了老半天也没找到,甚至由于我的项目的依赖关系比较复杂,一度出现各种诡异的问题,比如刷新下页面没问题,再刷新下有可能页面就会报错,总结下来就是依赖类库的执行顺序差异导致的。
有这一片文字介绍了 seajs 和 requirejs 的加载区别 http://www.douban.com/note/283566440/,虽然文章里的观点是说 requirejs 不如 seajs,会埋下坑,但是我实际用下来还是比较赞同 requirejs 的加载理念。
我甚至看了这篇文章后有重新用回seajs,并尝试把项目改成用 seajs 加载,但是当我做完了所有的修改,最后始终无法成功运行,归集最后还是依赖关系顺序加载有问题导致的,这个应该算是seajs的最大的问题了吧。

文章里其实也说了,二者遵循的规则是AMD和CMD,AMD是requirejs用的规范,在实际使用中,我还发现很多国外类库都是可以在 requirejs 里总结用的,即支持 AMD 规范,但是在seajs里就会有问题,不得不对其进行稍微修改后才可以使用,比如鼎鼎大名的jQuery,这一点恐怕就会吓走不少人了。

当然,requirejs 不能加载css确实是一大问题,这样我就不得不自己去实现模块化加载的方法,相比使用seajs就比较麻烦了。

总的来说,两个模块化加载的类库都是非常不错的,我本人比较推荐使用 requirejs

推荐一个神奇的网站 — silk — 鼠标轻轻点两下即可绘制出梦幻般激光效果的图片来

http://weavesilk.com/ 是用HTML5制作的一个非常具有梦幻效果的绘制工具。你不需要会任何技术,即可激发出无尽的灵感。

在你下笔的那一刹那,或许你根本不知道想画什么,但是两笔下去,你的灵感就被激发出来了,也许自己也会被惊讶到,哇,好神奇!

网站左侧有工具选项,点击倒数第二个可展开色彩和画笔类型的选择,最下面还有2个开关选项,分别是保持居中和对称输出。

来欣赏下绘制的图片的效果 :

另外,颜色选择上可以单色,也可以为过渡色,这个技巧我是在iphone里发现的,发现网页上也可以,就是在一个颜色上拖到到另外一个颜色上就可以了,如下图:

原创:无需修改任何HTML解决firefox里js不支持onmousewheel事件,让firefox完美支持onmousewheel,附带让input支持使用滚轮滚动递增、递减数值

终于发现firefox也有让人蛋疼的地方了,几乎主流浏览器都支持onmousewheel的情况下,居然只有firefox执着的不支持onmousewheel事件。

PS:onmousewheel是当滚动滚轮时触发的事件。

在google,baidu等主流网站上搜索了老半天居然没有找到一个完美的解决办法,经过2天的不断修炼,最终完美实现,并且增加了一个鼠标点击到input框里后可以滚动滚轮递增或递减数值的功能,完全原创。

把下面代码加入页面任意地方即可(建议加到全局共用的js里)

下面是完整的代码:


// firefox 支持 onmousewheel,文本框支持滚动条滚动改变数字,input内支持max,min
(function()
{
function listener_onmousewheel(e)
{
    e = e||event;
    var obj = document.activeElement;
    //有onmousewheel方法,则跳过
    if (typeof obj.onmousewheel =='function')return;
    //非input跳过
    if (obj.tagName!='INPUT')return;
    if (obj.type!='text')return;

    //实现滚动滚轮改变input值
    var re = /^(\-)?\d*$/i;
    var r = obj.value.match(re);
    if (r!=null &amp;&amp; r[0]!='')
    {
        var max = obj.getAttribute('max');
        var min = obj.getAttribute('min');
        if(e.preventDefault)e.preventDefault();
        e.returnValue=false;
        var old_value = obj.value;
        var n = old_value-0;
        var nn = (typeof e.wheelDelta=='number'?e.wheelDelta:-(e.detail))&gt;0?1:-1;
        n += nn;
        if (null!=max)
        {
            n = Math.min(max,n);
        }
        if (null!=min)
        {
            n = Math.max(min,n);
        }
        obj.value = n;
        obj.select();

        if (obj.onchange)
        {
            // 修复因为滚动而改变数值后onchange失效的问题
            if (typeof obj._tmp_onblur=='undefined')
            {
                obj._old_value = old_value;
                obj._tmp_onblur = obj.onblur?obj.onblur:function(){};
                obj._tmp_onchange = obj.onchange;
                obj.onchange = function(){};
                obj.onblur = function()
                {
                    if (this._old_value!=obj.value)this._tmp_onchange();
                    this._tmp_onblur(e);
                    this.onblur = this._tmp_onblur;
                    this.onchange = this._tmp_onchange;
                    this._tmp_onblur = undefined;
                    this._old_value = undefined;
                }
            }
        }
    }
}

if (navigator.userAgent.toLowerCase().indexOf('firefox')&gt;=0)
{
    //firefox支持onmousewheel
    addEventListener('DOMMouseScroll',function(e)
    {
        var onmousewheel = e.target.getAttribute('onmousewheel');
        if (onmousewheel)
        {
            if(e.preventDefault)e.preventDefault();
            e.returnValue=false;    //禁止页面滚动

            if ( typeof e.target.onmousewheel!='function' )
            {
                //将onmousewheel转换成function
                eval('window._tmpFun = function(event){'+onmousewheel+'}');
                e.target.onmousewheel = window._tmpFun;
                window._tmpFun = null;
            }
            // 不直接执行是因为若onmousewheel(e)运行时间较长的话,会导致锁定滚动失效,使用setTimeout可避免
            setTimeout(function(){
                e.target.onmousewheel(e);
            },1);
        }
        else
        {
            //没有onmousewheel则监听默认
            listener_onmousewheel(e);
        }
    },false);
}
else
{
    if( document.addEventListener )
    {
        document.addEventListener('DOMMouseScroll',listener_onmousewheel,false);
    }
    document.onmousewheel = listener_onmousewheel;
}
})();

如果只需要firefox下支持onmousewheel功能,只需要以下代码:

if (navigator.userAgent.toLowerCase().indexOf('firefox')&gt;=0)
{
    //firefox支持onmousewheel
    addEventListener('DOMMouseScroll',function(e)
    {
        var onmousewheel = e.target.getAttribute('onmousewheel');
        if (onmousewheel)
        {
            if(e.preventDefault)e.preventDefault();
            e.returnValue=false;    //禁止页面滚动

            if ( typeof e.target.onmousewheel!='function' )
            {
                //将onmousewheel转换成function
                eval('window._tmpFun = function(event){'+onmousewheel+'}');
                e.target.onmousewheel = window._tmpFun;
                window._tmpFun = null;
            }
            // 不直接执行是因为若onmousewheel(e)运行时间较长的话,会导致锁定滚动失效,使用setTimeout可避免
            setTimeout(function(){
                e.target.onmousewheel(e);
            },1);
        }
    },false);
}

让ajax更加完美——Ajax Streaming研究和使用

Ajax Streaming是什么,可以起到什么作用?或许我给你举个例子你就会明白了,并且会喜欢上它

比如通过ajax请求一个安装脚本,此脚本可能需要执行1分钟,传统的ajax只有在页面执行完毕后才会得到执行情况,那么怎么可以让页面实时知道服务器上执行的状态呢?此时,Ajax Streaming就发挥作用了,可以帮你实现输出每个步骤的详细信息。

在服务器上,可通过特殊的处理,将每一个执行的步骤推送到浏览器。

这样就可以输出类似下面的信息了:

执行初始化成功…
开始执行步骤一
步骤一执行成功
步骤二开始
步骤二结束

全部执行完毕

PHP代码示例:

<?php
if (ob_get_level() == 0) ob_start();

// echo str_pad("", 1000);ob_flush();flush();    //兼容IE的堵塞,否则IE下只有执行完毕才会显示

for ($i = 0; $i<10; $i++)
{
        echo "<br> Line to show.";
        echo str_pad('',4096)."\n";    

        ob_flush();
        flush();
        sleep(1);
}
echo "Done.";
ob_end_flush();

上面的代码取自 http://php.net/manual/zh/function.flush.php

注意,nginx + php-fpm 模式需要在 nginx 的配置里加入

fastcgi_buffering of;

的配置

上面这样做后,大部分浏览器都可支持,但是,若此请求是在ajax中,则被称为Ajax Streaming,然后目前,经我测试Firefox,Chrome,Safari等都已支持,而IE不支持,看到有人说IE8支持,但我测试下来也是不支持的,所以在IE里需要寻求别的解决的方案,比如通过框架的方式是可以实现的。

在支持Ajax Streaming的浏览器中,不需要做特别的处理,每当浏览器收到新数据后,都会运行XMLHttpRequest的onreadystatechange方法,且XMLHttpRequest的状态readyState=3

例如:

xmlhttp=new XMLHttpRequest();
xmlhttp.XMLHttpRequest = function(){
   if (xmlhttp.readyState==3){
      alert(xmlhttp.responseText);
   }else if (xmlhttp.readyState==4){
      alert('ok');
   }
}

你会发现,当readyState==3的时候会被多此运行。

IE里只会执行1次,所以我采用了iframe的实现。普通的iframe会让浏览器产生一个loading进度条,这让人很不爽,所以采用了 http://infrequently.org/2006/02/what-else-is-burried-down-in-the-depths-of-googles-amazing-javascript/ 的方案。

通过创建一个new ActiveXObject(“htmlfile”);再特殊处理就可以不产生那个讨厌的加载进度了。

下面是我写的一个整合的代码,通过它完全模拟出了一个xmlhttp,

var self = this;
// 构造一个虚拟的xmlhttp对象
this.xmlhttp = {
    responseXML: null,
    responseText: '',
    responseType: null,
    response: '',
    status: 0,
    readyState: 0,
    method: 'POST',
    open: function(method, url) {
        this.method = method;
        var newurl = [];
        if (url.indexOf('#') != -1) {
            newurl = url.split('#');
        }
        else {
            newurl = [url];
        }
        if (newurl[0].indexOf('?') == -1) {
            newurl[0] += '?_ajax=true';
        }
        else {
            newurl[0] += '&_ajax=true';
        }
        url = newurl.join('#');
        this.url = url;
        this.ifrDiv.innerHTML = '<iframe id="myqee_ajax_iframe" onload="alert(888)" name="myqee_ajax_iframe"></iframe><form id="myqee_ajax_fram" target="myqee_ajax_iframe" action="' + url + '" method="POST"></form>';
        var objframe = this.obj.getElementById('myqee_ajax_iframe');
        var getdata = function() {
            if (objframe.readyState == 'loadinng') {
                if (self.xmlhttp.readyState == 0) {
                    self.xmlhttp.readyState = 1;
                    self.xmlhttp.onreadystatechange();
                }
            }
            else if (objframe.readyState == 'interactive') {
                if (self.xmlhttp.readyState == 0) {
                    self.xmlhttp.readyState = 1;
                    self.xmlhttp.onreadystatechange();
                }
                if (self.xmlhttp.readyState == 1) {
                    self.xmlhttp.readyState = 2;
                    self.xmlhttp.onreadystatechange();
                }
                if (self.xmlhttp.readyState == 2) {
                    self.xmlhttp.readyState = 3;
                }
                if (self.xmlhttp.response != objframe.contentWindow.document.body.innerHTML) {
                    self.xmlhttp.response = self.xmlhttp.responseText = objframe.contentWindow.document.body.innerHTML;
                    self.xmlhttp.onreadystatechange();
                }
            }
            else if (objframe.readyState == 'complete') {
                clearInterval(runtime);
                runtime = null;
                if (self.xmlhttp.response != objframe.contentWindow.document.body.innerHTML) {
                    self.xmlhttp.response = self.xmlhttp.responseText = objframe.contentWindow.document.body.innerHTML;
                    self.xmlhttp.onreadystatechange();
                }
                self.xmlhttp.readyState = 4;
                self.xmlhttp.status = 200;
                self.xmlhttp.onreadystatechange();
            }
        };
        var runtime = setInterval(getdata, 10);
    },
    send: function(data) {
        if (this.method == 'GET') {
            //GET方式最简单了
            this.obj.getElementById('myqee_ajax_iframe').src = this.url;
        }
        else {
            var newdata = data.split('&');
            var html = '';
            for (var i = 0; i < newdata.length; i++) {
                var index = newdata[i].indexOf('=');
                var k = decodeURIComponent(newdata[i].substr(0, index));
                var v = decodeURIComponent(newdata[i].substr(index + 1));
                html += '<textarea name="' + k.replace(/"/g, '&quot;') + '">' + v.replace(/</g, '&lt;') + '</textarea>' + "\n";
            }
            this.obj.getElementById('myqee_ajax_fram').innerHTML = html;
            this.obj.getElementById('myqee_ajax_fram').submit();
            //构造出一个表单提交
        }
    },
    obj: new ActiveXObject("htmlfile")
};
this.xmlhttp.obj.open();
this.xmlhttp.obj.write("<html>");
this.xmlhttp.obj.write("</html>");
this.xmlhttp.obj.close();
this.xmlhttp.ifrDiv = this.xmlhttp.obj.createElement("div");
this.xmlhttp.obj.appendChild(this.xmlhttp.ifrDiv);