使用jQuery和YQL,以Ajax方式加載外部內容 – Javascript教程_JS教程_技術文章 – 程式設計聯盟

我們來看看怎樣使用jQuery,以Ajax方式加載外部(其他域上)的內容。這裡的所有代碼都可以從GitHub下載,也可以在這個演示頁面中獲取,因而不用復制粘貼瞭。
OK,Ajax通過jQuery是很容易做到的,大多數解決方案就幾行代碼:
$(document).ready(function(){ $('.ajaxtrigger').click(function(){$('#target').load('ajaxcontent.html'); }); });
查看這個簡單但有點粗陋的Ajax演示就可以看到結果。
這會將所有帶ajaxtrigger類的元素轉換成觸發器來加載ajaxcontent.html,並在ID為target的元素中顯示其內容。
這樣不好,因為多數時候這意味著人們將使用<a href="#">click me</a>這種空鏈接,但這不是我們現在要討論的問題。我在撰寫一篇更長的文章,其中會提到增強Ajax可用性和可訪問性的所有技巧。
要使其能夠重用可以像下面這樣:
$(document).ready(function(){ $('.ajaxtrigger').click(function(){$('#target').load($(this).attr('href')); return false; }); });
這樣,你可以使用<a href="ajaxcontent.html" class="ajaxtrigger">load some content</a>來加載內容,而所有JavaScript代碼都可以重用。
查看這個可重用Ajax演示就能看到結果。
我要解決的問題發生在點擊演示頁面中的第二個鏈接時:加載外部內容失敗,因為Ajax不允許跨域加載內容。這意味著,<a href="http://icant.co.uk/" class="ajaxtrigger">see my portfolio</a>加載Ajax內容將失敗,而且沒有提示。盡管你無數遍地點擊這個鏈接,但是什麼都不會發生。避免出現這種情況的一個方法,是簡單地讓瀏覽器加載該文檔,但前提是用戶真的想加載外部鏈接。
查看這個允許加載外部鏈接的演示就能看到結果。
$(document).ready(function(){ $('.ajaxtrigger').click(function(){ var url =$(this).attr('href'); if(url.match('^http')){ return true; } else {$('#target').load(url); return false; } }); });
使用PHP代理
如果瀏覽Web,你會發現大多數的解決方案是PHP(或其他語言)代理腳本。比如,下面是使用cURL的proxy.php代理腳本:
<?php $url = $_GET['url']; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL,$url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $output = curl_exec($ch);curl_close($ch); echo $content; ?>
然後可以稍作修改使用這個腳本(使用代理):
$(document).ready(function(){ $('.ajaxtrigger').click(function(){ var url =$(this).attr('href'); if(url.match('^http')){ url = 'proxy.php?url=' + url; }$('#target').load(url); return false; }); });
用這樣的代理腳本依舊是個很蠢的辦法,因為不進行過濾,人們就可以使用這個腳本來加載你服務器上的任何文檔,並將其內容顯示在自己的頁面中(用firebug來重命名鏈接,就能看到你服務器上的任何內容),他們可以使用它將郵件群發腳本插入文檔,或者簡單地使用它來重定向到任何其他Web資源,並且讓你的服務器看上去就是發送請求的那個服務器。垃圾郵件制造者就有瞭施展才華的地方瞭。
使用白名單和過濾代理
因而,要想使用代理,就得確保有被認可的URI的白名單。此外,除瞭另一個HTML文檔的主體,其他的都除去比較好。另一個好辦法是過濾腳本。這會避免顯示錯誤和執行你本不想在網站上執行的腳本。
就像下面這樣:
<?php $url = $_GET['url']; $allowedurls = array( 'http://developer.yahoo.com','http://icant.co.uk' ); if(in_array($url,$allowedurls)){ $ch = curl_init();curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);$output = curl_exec($ch); curl_close($ch); $content = preg_replace('/.*<body[^>]*>/msi','',$output); $content =preg_replace('/</body>.*/msi','',$content); $content =preg_replace('/<?/body[^>]*>/msi','',$content); $content =preg_replace('/[r|n]+/msi','',$content); $content = preg_replace('/<–[Ss]*?–>/msi','',$content); $content = preg_replace('/<noscript[^>]*>[Ss]*?</noscript>/msi','',$content); $content = preg_replace('/<script[^>]*>[Ss]*?</script>/msi','',$content); $content =preg_replace('/<script.*/>/msi','',$content); echo $content; } else { echo'Error: URL not allowed to load here.'; } ?>
使用YQL的純JavaScript解決方案
但是,如果沒有權利訪問服務器,或者你隻想使用JavaScript,怎麼辦?不用擔心,這是可以做到的。借助YQL可以加載任何HTML文檔,並以JSON格式返回。jQuery具有加載JSON的好接口,因此與YQL一起使用就可以達到我們的目的。
從YQL獲取HTML很容易,使用下面語句即可:
select * from html where url="http://icant.co.uk"
YQL還可以完成下面一些事:
• 加載並清理HTML文檔
• 使用HTML Tidy運行HTML文檔來刪除不好的標記
• 緩存HTML
• 隻返回HTML的主體內容,因而除內聯樣式外不需處理其他樣式
數據輸出格式可以是XML或JSON。如果為JSON定義瞭回調參數,就表明要使用JSON-P,所有HTML都會保存在一個JavaScript對象中——這不適合重組。
foo({ "query":{ <a href=""1" title="">count</a>", <a href=""2010-01-10T07:51:43Z" title="">created</a>", <a href=""en-US" title="">lang</a>", <a href=""2010-01-10T07:51:43Z" title="">updated</a>", <a href=""http://query.yahoo[…whatever…]k%22" title="">uri</a>", "results":{"body":{ "p":{ <a href=""doc2" title="">id</a>", <a href="[{"id":"hd" title="">p</a>", <a href=""icant.co.uk" title="">h1</a> – everything Christian Heilmann" }, {<a href=""bd" title="">id</a>", "p":[ {<a href="[{"h2":"About" title="">p</a> this and me","[… and so on…] }}}}}}}});
當定義瞭帶XML輸出的回調時,會得到將HTML數據作為數組中字符串的函數調用,簡單多瞭:
foo({ "query":{ <a href=""1" title="">count</a>", <a href=""2010-01-10T07:47:40Z" title="">created</a>", <a href=""en-US" title="">lang</a>", <a href=""2010-01-10T07:47:40Z" title="">updated</a>", <a href=""http://query.y[…who" title="">uri</a> cares…]%22"}, "results":[ "<body>n <p id="doc2">n <p id="hd">n <h1>icant.co.uk – everything Christian Heilmann</h1>n … and so on …" ] });
使用jQuery的getJSON()方法,訪問YQL端點,這很容易實現:
$.getJSON("http://query.yahooapis.com/v1/public/yql?"+"q=select%20*%20from%20html%20where%20url%3D%22"+ encodeURIComponent(url)+"%22&format=xml'&callback=?", function(data){ if(data.results[0]){ var data =filterData(data.results[0]); container.html(data); } else { var errormsg ='<p>Error: could not load the page.</p>'; container.html(errormsg); } } );
組合在一起可以得到使用jQuery和YQL的跨域Ajax解決方案:
$(document).ready(function(){ var container = $('#target');$('.ajaxtrigger').click(function(){ doAjax($(this).attr('href')); return false;}); function doAjax(url){ // 如果它是個外部URI if(url.match('^http')){ // 調用YQL$.getJSON("http://query.yahooapis.com/v1/public/yql?"+"q=select%20*%20from%20html%20where%20url%3D%22"+ encodeURIComponent(url)+"%22&format=xml'&callback=?", // 這個函數得到的數據來自成功的JSON-P調用function(data){ // 如果有數據,過濾它並呈現出來 if(data.results[0]){ var data =filterData(data.results[0]); container.html(data); // 否則提示出錯瞭 } else { varerrormsg = '<p>Error: could not load the page.</p>'; container.html(errormsg); }} ); // 如果它不是外部URI,使用Ajax的load()方法 } else { $('#target').load(url); } }// 過濾掉一些不好的東西 function filterData(data){ data =data.replace(/<?/body[^>]*>/g,''); data = data.replace(/[r|n]+/g,''); data =data.replace(/<–[Ss]*?–>/g,''); data = data.replace(/<noscript[^>]*>[Ss]*?</noscript>/g,''); data = data.replace(/<script[^>]*>[Ss]*?</script>/g,''); data = data.replace(/<script.*/>/,''); return data; } });
當然,這個例子還很粗糙。實際的Ajax解決方案應該考慮超時,以及未找到文檔的情況。查看帶加載指示器、異常處理和黃褪技術的完整代碼以獲得靈感。

 摘自 圖靈教育

發佈留言