JavaScript DOM高级程序设计 7.向应用程序加入Ajax--我要坚持到底!

有时候,或许是因为理解能力,也或许是因为浮躁,看东西总是不入心,而且还老是想跳过本节,或者赶紧看完本节,这样的恶性循环,让我在即没有真正的学习到知识,又打击我的学习信心,还浪费了我很多事件,我想,当遇到这种情况的时候,能做的就是放慢自己的学习脚步,让大脑有充分的时间去理解。

经过一个星期,把这本书看的差不多了,前面还好,后面就迷迷糊糊了。特别是7章之后,就是自己随便的看看了,也不想在像刚开始那样的一点一点的敲了,后面的几个案例,菜鸟对着代码,把整个电子书看完了(后面看的很粗)。菜鸟写的这些博客,不是想让别人从中学到什么,而是想给自己看书留下点痕迹,虽然不是很清晰,至少这条路曾经走过,同事在用EditPlus的过程中,发现自己敲代码速度变快了。好开心。对于那些案例,我以后没事还会过来敲两下,学习的路还很长。菜鸟要是就是坚持。

在不同的浏览器之间,XMLHttpRequest方法包括

  • open(method,URL[,asynchronous[,password]]]):用于指定请求的URL、方法以及与请求相关的其他可选属性。
  • setRequestHeader(label,value)用于以给定的label和value为请求应用一个头部信息。该方法必须在open()之后且在send()之前调用。
  • send(content)用于发送请求。
  • abort()用于停止当前的请求
  • getAllResponseHeaders()返回字符串形式的完整的头部信息集合。
  • getResponseHeader(label)返回字符串形式的完整的头部信息集合。
  • 生成一次新请求
function stateChangeListener()
{
    //某些代码
}
var request = false;
if (window.XMLHttpRequest)
{
    var request=new window.XMLHttpRequrst();
}
else if (window.ActiveXObject)
{
    var request=new window.ActiveXObject('Microsoft.XMLHTTP');
}
if (request)
{
    request.onreadystatechange=stateChangeLinstener;
    request.open('GET','/your/script/?var=value&var2=value',true);
    request.send(null);
    /*如果是post请求*/
    //request.open('POST','/your/script',true);
    //request.send('var=value&var2=value');

    /*遗憾的是js不能访问系统中的文件,这意味这无法使用file输入框
    ,通过包含选中的文件来执行一次经过多重加密后的POST请求*/
}
  • 处理响应

XMLHttpRequest对象的属性

  • readyState:0--尚未初始化;1--载入中;2--载入完成;3--交互;4--完成
  • responseText是一个在相应中返回的数据的字符串表示
  • responseXML是一个兼容DOM核心的文档对象,
  • status是一个表示请求状态的数字代码。
  • statusText是与状态代码相关的一条信息。
  • onreadystatechange应该包含在请求的不同readyState状态下被调用的方法。下面就是一个典型的onreadystatchange方法:
function stateChangeListener()
{
    switch (request.readyState)
    {

        case 1:
            //载入中
            break;
        case 2:
            //载入完成
            break;
        case 3:
            //交互
            break;
        case 4:
            //完成
            if (request.status==200)
            {
                //对request.responseText或request.responseXML进行处理
            }
            else
            {
                //request.status中可能包含某个错误代码而request.statusText中则包含报告的错误信息
            }
            break;
    }
}

如果你希望甲苯中剩余的操作等到请求完成后在执行,那么他们就应该在响应返回后通过onreadystatechange方法被执行

//创建请求对象
//创建你希望运行的方法。
function alertAndDoWhatever()
{
    //r是通过onreadystatechange真挺传递进来的请求
    alert(r.responseText);
}
request.onreadystatechange=function()
{
    //当求情完成运行相应的方法
    if (request.readyState==4&&request.status=='200')
    {
        //请求已经成功完成,需要调用你创建的方法
        alertAndDoWhatever(request);
    }

}
//启动请求
request.open('GET','/youserscript/?var=value',true);
//发送请求
request.send(null);
  • 在服务器上识别Ajax请求

这部分我是没怎么看懂。。。

  • XML

Ajax混合技术中的最后一部分是XML相应,作为一种数据传输机制,XML在允许开发者从DOM层次上遍历、读取和操纵相应的数据访问方面是值得称道的,而且,如果在处理过程中整合了一个SXLT解析机制,那么服务器和客户端就能共享XSLT文件以确保在服务器和浏览器中生成相同的标记:

var xsltProcessor=new XSLTProcessor();
var xslStylesheet;
var xmlDoc;

//异步取得一个XSL文件
var requestXsl= new XMLHttpRequest();
requestXsl.onreadystatechange=function(request)
{
    xslStylesheet=request.responseXML;
}
requestXsl.open('GET',"example.xsl",true);
requestXml.send(null);

var processor=function()
{
    if (xslStylesheet&&smlDoc)
    {
        clearInterval(this);
        //通过XSLT转换XML
        xsltProcessor.importStylesheet(xslStylesheet);
        var fragment=xsltProcessor.transformToFragment(xmlDoc,document);
        ADS.S('example').appendChild(fragment);
    }
}

//每200毫秒检查一次文件是否载入完成
setInterval(processor,200);
//正如我们前面所提到的XML意味这可以对XML相应使用DOM2核心方法
var messages=request.responseXML.getElementsByTagName('message');
for (var i = 0;i<messages.length ;i++ )
{
    ADS.$('example').appendChild(messages[i]);
}

如果你在使用XML时遇到了问题。或者他不具有什么优势,那么可以考虑使用其他代替方案。不过在选择时一定要认真谨慎,因为错误的方案有可能会导致用那个用程序的品质降低

纯文本:

request.onreadystatechange=function()
{
    if (this.readyState==4&&request.status==200)
    {
        if (request.respenseText=='t')
        {
            //服务器处理成功
        }
        else
        {
            //服务器处理失败
        }
    }
}

HTML

另一个尝尽的选择是以常规的HTML代码作为相应:

<p class ="success">The response was successful</p>

这样就具备了通过元素属性添加元数据能力。而且还能使用innerHTML属性将responseText中的代码插入到文档中:

request.onreadystatechange=function()
{
    if (request.readyState==4&&request.status==200)
    {
        ADS.$('example').innerHTML=request.reponseText;
    }
}

JavaScript代码

(非JSON)而是调用简单的alert()方法完成:

request.onreadystatechange=function()
{
    if (request.readyState==4&&request.status==200)
    {
        eval(request.responseText);
    }
}

JSON

一般而言,在想用中使用JSON对象时,都意味着返回一个简单的javascript对象。

{
    message:'The response was successful',
    type: 'success'
}

然后使用eval()来将JSON解析为本地javascript对象,从该对象中在添加想要的信息

request.onreadystatechange=function()
{
    if (this.readyState==4&&request.status==200)
    {
        //对JSON求值以生成response对象
        var response=eval('('+this.responseText+')');
        //通过innerHTML对response执行某些操作

        ADS.$('example').innerHTML='<p class="'        +response.type
            +'">'
        +reponse.message
            +</p>;
        //或者直接对用户给出提示
        alert(response.message);
        //或使用DOM
        var p = document.createElement('p');
        p.appendChild(document.createTextNode(response.message));
        ADS.$('example').appendChild('p');
    }
}
  • 一个可重用的对象

为了处理XMLHttpRequest对象更家容易,库中添加两个方法:(我表示一个连JSON都不知道干嘛的人看这个确实吃力。copy吧,以后再补补)

/*
parseJSON(string,filter)
A slightly modified version of the public domain method 
at http://www.json.org/json.js This method parses a JSON text 
to produce an object or array. It can throw a 
SyntaxError exception.

The optional filter parameter is a function which can filter and
transform the results. It receives each of the keys and values, and
its return value is used instead of the original value. If it
returns what it received, then structure is not modified. If it
returns undefined then the member is deleted.

Example:

// Parse the text. If a key contains the string 'date' then
// convert the value to a date.

myData = parseJSON(string,function (key, value) {
    return key.indexOf('date') >= 0 ? new Date(value) : value;
});

*/
function parseJSON(s,filter) {
    var j;

    function walk(k, v) {
        var i;
        if (v && typeof v === 'object') {
            for (i in v) {
                if (v.hasOwnProperty(i)) {
                    v[i] = walk(i, v[i]);
                }
            }
        }
        return filter(k, v);
    }


// Parsing happens in three stages. In the first stage, we run the 
// text against a regular expression which looks for non-JSON 
// characters. We are especially concerned with '()' and 'new' 
// because they can cause invocation, and '=' because it can cause 
// mutation. But just to be safe, we will reject all unexpected 
// characters.
/*解析通过三个极端进行:
1,通过正则表达式检测JSON文本,查找非JSON字符。其中特别关注“0”和“new”,
因为他们会引起语句的调用,还有“=”因为他会导致变量的值发生改变,不过为安全起见
这里会拒绝所有不希望出现的字符
*/
 if (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.
            test(s)) {

// In the second stage we use the eval function to compile the text 
// into a JavaScript structure. The '{' operator is subject to a 
// syntactic ambiguity in JavaScript: it can begin a block or an 
// object literal. We wrap the text in parens to eliminate 
// the ambiguity.
/*

*/
        try {
            j = eval('(' + s + ')');
        } catch (e) {
            throw new SyntaxError("parseJSON");
        }
    } else {
        throw new SyntaxError("parseJSON");
    }

// In the optional third stage, we recursively walk the new structure, 
// passing each name/value pair to a filter function for possible 
// transformation.

    if (typeof filter === 'function') {
        j = walk('', j);
    }
    return j;
};
    
        
/**
 * Setup the various parts of an XMLHttpRequest Object 
 */
function getRequestObject(url,options) {
    
    // Initialize the request object
    var req = false;
    if(window.XMLHttpRequest) {
        var req = new window.XMLHttpRequest();
    } else if (window.ActiveXObject) {
        var req = new window.ActiveXObject('Microsoft.XMLHTTP');
    }
    if(!req) return false;
    
    // Define the default options
    options = options || {};
    options.method = options.method || 'GET';
    options.send = options.send || null;

    // Define the various listeners for each state of the request
    req.onreadystatechange = function() {
        switch (req.readyState) {
            case 1:
                // Loading
                if(options.loadListener) {
                    options.loadListener.apply(req,arguments);
                }
                break;
            case 2:
                // Loaded
                if(options.loadedListener) {
                    options.loadedListener.apply(req,arguments);
                }
                break;
            case 3:
                // Interactive
                if(options.ineractiveListener) {
                    options.ineractiveListener.apply(req,arguments);
                }
                break;
            case 4:
                // Complete
                // if aborted FF throws errors
                try { 
                if (req.status && req.status == 200) {
                    
                    // Specific listeners for content-type
                    // The Content-Type header can include the charset:
                    // Content-Type: text/html; charset=ISO-8859-4
                    // So we'll use a match to extract the part we need.
                    var contentType = req.getResponseHeader('Content-Type');
                    var mimeType = contentType.match(/\s*([^;]+)\s*(;|$)/i)[1];
                                        
                    switch(mimeType) {
                        case 'text/javascript':
                        case 'application/javascript':
                            // The response is JavaScript so use the 
                            // req.responseText as the argument to the callback
                            if(options.jsResponseListener) {
                                options.jsResponseListener.call(
                                    req,
                                    req.responseText
                                );
                            }
                            break;
                        case 'application/json':
                            // The response is json so parse   
                            // req.responseText using the an anonymous functions
                            // which simply returns the JSON object for the
                            // argument to the callback
                            if(options.jsonResponseListener) {
                                try {
                                    var json = parseJSON(
                                        req.responseText
                                    );
                                } catch(e) {
                                    var json = false;
                                }
                                options.jsonResponseListener.call(
                                    req,
                                    json
                                );
                            }
                            break;
                        case 'text/xml':
                        case 'application/xml':
                        case 'application/xhtml+xml':
                            // The response is XML so use the 
                            // req.responseXML as the argument to the callback
                            // This will be a Document object
                            if(options.xmlResponseListener) {
                                options.xmlResponseListener.call(
                                    req,
                                    req.responseXML
                                );
                            }
                            break;
                        case 'text/html':
                            // The response is HTML so use the 
                            // req.responseText as the argument to the callback
                            if(options.htmlResponseListener) {
                                options.htmlResponseListener.call(
                                    req,
                                    req.responseText
                                );
                            }
                            break;
                    }
                
                    // A complete listener
                    if(options.completeListener) {
                        options.completeListener.apply(req,arguments);
                    }

                } else {
                    // Response completed but there was an error
                    if(options.errorListener) {
                        options.errorListener.apply(req,arguments);
                    }
                }
                

                } catch(e) {
                    //ignore errors
                    //alert('Response Error: ' + e);
                }
                break;
        }
    };
    // Open the request
    req.open(options.method, url, true);
    // Add a special header to identify the requests
    req.setRequestHeader('X-ADS-Ajax-Request','AjaxRequest');
    return req;
}
window['ADS']['getRequestObject'] = getRequestObject;