В последнее время довольно часто замечал в разных статьях упоминание о JSONP. Судя по названию это тот же JSON, но с каким-то наворотом. Поразобравшись немного с этим, оказалось что этот формат нужен для того, чтобы иметь возможность выполнять асинхронные запросы к серверу расположенному на другом домене. Напомню, что для объекта XMLHTTPRequest есть такое ограничение. То есть, если ваш сайт расположен по адресу mysite.com, то AJAX запрос на сервер anothersite.com выполнить не удастся.
Так вот, чтобы обойти это ограничение поступали следующим образом – динамически создавали и добавляли в документ новый тег <script>, который подгружал скрипт с нужными данными:
<script src="/some/url/here" type="text/javascript"></script>
При динамической подгрузке данных нужно выполнять callback, а для этого в конец подгружаемого скрипта добавляем вызов этой функции. В общем, получаемый от сервера скрипт может выглядеть следующим образом:
var data = {
one : "12",
two : "34"
}
someCallback(data);
Если в указанном выше примере не объявлять переменную data, а сразу поместить её в вызов someCallback, то получим то, что сейчас стали называть JSONP:
someCallback({ one : "12", two : "34" });
В данный момент некоторые популярные сервисы поддерживают как JSON, так и JSONP. Типичным примером является API для поиска от YAHOO. Чтобы получить данные от сервера в виде JSON, мы указываем параметр output=json:
Теперь, чтобы получить данные в виде JSONP, просто добавляем название функции-callback, в которую будут “завернуты” возвращаемые данные. Указанная выше ссылка будет выглядеть так:
По этой ссылке сервер вернет следующие данные:
loadit({"ResultSet":{"type":"web","totalResultsAvailable":22600000,"totalResultsReturned":10, ... } });
Теперь, короткий пример получения и обработки формата JSONP:
<html>
<head>
<script type="text/javascript">
// callback
function loadit(data) {
// работаем с данными
if (!data) {
return;
}
var oTotal = document.getElementById("total");
oTotal.innerHTML = data.ResultSet.totalResultsAvailable;
}
// здесь будет ссылка на новый элемент script
var oElem = null;
function sendJSONP(url, callback) {
if (!url || !callback)
return;
url += '&callback='+callback;
// выполняем запрос JSONP
if (oElem) {
oElem.parentNode.removeChild(oElem);
}
oElem = document.createElement('script');
oElem.setAttribute('type','text/javascript');
document.getElementsByTagName('head')[0].appendChild(oElem);
oElem.setAttribute('src', url);
}
window.onload = function(){
document.getElementById("searchBtn").onclick = function(){
var query = document.getElementById("query").value;
sendJSONP('http://search.yahooapis.com/WebSearchService/V1/webSearch?appid=YahooDemo&query='+encodeURIComponent(query)+'&output=json', 'loadit');
}
}
</script>
</head>
<body>
<input type="text" name="search" value="" id="query" /><input type="button" value="Искать" name="s" id="searchBtn"/>
<div id="total"></div>
</body>
</html>
Этот скрипт работает следующим образом: пользователь вводит в поле поиска искомую фразу и нажимает на кнопку “Искать”. Из возвращаемых результатов поиска мы выделяем значение общего количества результатов totalResultsAvailable и показываем это значение в элементе <div id=total>. Запрос к серверу выполняется в функции sendJSONP, которая принимает 2 параметра – url и callback.
В библиотеке jQuery имеется встроенная поддержка формата JSONP. При использовании этого фреймворка, нет нужды самому код, который добавляет тег SCRIPT в DOM. Всё, что нужно будет сделать, это изменить URL, чтобы указать jQuery, что выполняется JSONP запрос. Заканчивая URL вопросительным знаком, мы даем jQuery понять, что получаемые данные будут в формате JSONP. Не нужно также и указывать название функции обработчика. Пример:
<html>
<head>
<script src="/jquery/jquery.js"></script>
<script>
var baseurl = 'http://search.yahooapis.com/ImageSearchService/V1/imageSearch?appid=YahooDemo&output=json&query='
function search() {
var search = $("#search").val()
console.log(search)
var surl = baseurl + escape(search) + '&callback=?'
$.getJSON(surl, function(data) {
var res = '<h1>Search for '+search+'</h1>'
res += '<p>There were '+data.ResultSet.totalResultsAvailable+' results.</p>'
for(var i=0; i<data.ResultSet.Result.length; i++) {
var result = data.ResultSet.Result[i]
var resultStr = '<img src="'+result['Thumbnail']['Url']+'" align="left">';
resultStr += '<a href="'+result['ClickUrl']+'">'+result['Title']+'</a><br clear="left"/>'
res+=resultStr
}
$("#result").html(res)
})
}
$(document).ready(function() {
$("#searchBtn").click(search)
});
</script>
</head>
<body>
<input type="text" name="search" id="search"> <input type="button" value="Search" id="searchBtn">
<div id="result"></div>
</body>
</html>
Этот пример для работы с jQuery позаимствован на InsideRIA.
Поддержка JSONP скоро будет доступна и в Mootools. В данный момент она уже есть в версии 1.3rc1. Выглядит это так:
new JsonP('http://api.cnet.com/restApi/v1.0/techProductSearch', {
data: {
partTag: 'mtvo',
iod: 'hlPrice',
iewType: 'json',
results: '100',
query: 'ipod'
},
onComplete: myFunction.bind(someObject)
}).request();
На мой взгляд гораздо понятнее и проще, чем чудеса с вопросительными знаками в jQuery
