Archive

Posts Tagged ‘JavaScript’

jQuery: Отмена активных ajax запросов

October 27th, 2011 3 comments

Иногда требуется отменить все активные XHR, либо какую-то часть из них. Для этой нужны я накидал плагин  для jQuery, которые автоматически добавляет все XHR в коллекцию с возможностью указать тег.

 

(function($) {
	$.xhrPool = {};
 
	$.xhrPool.abortAll = function() {
		$.each(this, function(pool, map) {
			$.each(map, function(idx, jqXHR) {
				jqXHR.abort();
				$.xhrPool[pool].splice(idx, 1);
			});
		});
	};
 
	$.xhrPool.abort = function(pool) {
		if (typeof this[pool] != "undefined") {
			$.each(this[pool], function(idx, jqXHR) {
				try {jqXHR.abort();} catch (e) {}
				$.xhrPool[pool].splice(idx, 1);
			});
		}
	};
 
	$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
		var pool = options.xhrpool || "global";
 
		if ( ! $.xhrPool[pool]) {
			$.xhrPool[pool] = [];
		}
 
		$.xhrPool[pool].push(jqXHR);
	});
})(jQuery)

Тег указывается в вызове $.ajax() опцией xhrpool. Без указания этой опции все XHR будут добавляться в $.xhrPool.global. Также доступны 2 метода: $.xhrPool.abortAll() — останавливает все запросы и $.xhrPool.abort(“%pool%”) — останавливает запросы в коллекции с указанным тегом.

Отправка большой формы через Ajax

October 11th, 2011 No comments

Любой кто хотя-бы раз отправить большую форму через Ajax – сталкивался с проблемой, когда в функции отправки приходилось перечислять заново все поля.

Решение проблемы достаточно просто: нам нужна функция которая сама разберет форму.

Первый вариант который приходит в голову:

function parseForm(identifier){
  // Получаем все инпуты содержащиеся в форме
  var inputs = document.getElementById(identifier).getElementsByTagName('input');
  // Инициализируем массив для результатов
  var values = {};
  // Перебираем элементы
  for (var n in inputs){
    values[inputs.n.getAttribute('name')] = inputs.n.getAttribute('value');
  }
}

Но тут есть проблема, в частности с input’ами типа checkbox и radio.

Немного модифицируем функцию что-бы корректно обрабатывать их.

function parseForm(identifier){
  // Получаем все инпуты содержащиеся в форме
  var inputs = document.getElementById(identifier).getElementsByTagName('input');
  // Инициализируем массив для результатов
  var values = {};
  // Перебираем элементы
  for (var n in inputs){
    if(inputs[n].getAttribute('type') == "checkbox"){
      // Если тип - checkbox, то получаем значение checked
      values[inputs[n].getAttribute('name')] = inputs[n].getAttribute('checked');
    }else if(inputs[n].getAttribute('type') == "radio"){
      // Если тип - radio, получаем список всех input с этим именем
      var radios = document.getElementById(identifier).getElementsByName(inputs[n].getAttribute('name'));
      // Перебираем их
      for (var i in radios){
        // Если текущий элемент выделен то ложим в результат его значение
        if(radios[i].getAttribute('checked')){
          values[inputs[n].getAttribute('name')] = radios[i].getAttribute('value');
        }
      }
    }else{
      values[inputs[n].getAttribute('name')] = inputs[n].getAttribute('value');
    }
  }
 
  return values;
}

Вот казалось бы и все, но мы еще забыли про select.
Для него придется добавить в функцию еще часть:

var selects = document.getElementById(identifier).getElementsByTagName("select");
 
for(s in selects){
    var options = selects[s].getElementsByTagName("option");
    // Перебираем их
    for (var i in options){
      // Если текущий элемент выделен то ложим в результат его значение
      if(options[i].getAttribute('selected')){
        values[selects[n].getAttribute('name')] = options[i].getAttribute('value');
      }
    }
}

Реализация этого кода на jQuery останется домашним заданием 🙂

Кроссдоменный AJAX

September 24th, 2011 No comments

Самое простой способ — использование объекта XMLHTTPRequest в современных браузерах(Chrome 6+, Firefox 3.5+, Internet Explorer 8+, Safari 4+) и наличие у сервера заголовка Access-Control-Allow-Origin: *

Все бы хорошо, вот только заметили ли вы в списке Оперу? Нет, она еще не поддерживает XMLHTTPRequest 2. Да и то что IE не поддерживает в 7-ой версии может оказаться для кого-то критичным. Ниже последуют другие способы кроссдоменного ajax’a.

Проксирование

Довольно популярный метод, представляет из себя отправку запроса на серверный скрипт, который получает целевой url и возвращает его в ответе на запрос. Привязка к серверному скрипту делает этот метод недопустимым в некоторых случаях. Да и как минус можно задержку отметить.

 JSONP

JSON Padding или так называемый “JSON с подкладкой” — способ с JSON при котором имя калбэка указывается в аргументах запроса. При этом запрос подгружается через тег script, а в ответе json-объект должен быть заключен в вызов того самого переданного калбэка. Пример:

запрос http://example.com/?q=somevalue&callback=mycallback
ответ  mycallback({"foo": "bar", "a": "x"})

Из минусов возможность использовать только GET-запрос.

Политика безопасности также позволяет загружать css’ники с любых доменов, поэтому аналогично JSONP появился метод CSSHttpRequest. Особой выгоды в нем не вижу, кому интересно могут глянуть на странице проекта.

XHRIframeProxy

Метод основан на том, что ифреймы находясь на разных доменах могут общаться через url hash.

Для использования необходимо 2 ифрейма, первый – клиентский, с тем же url, что и основное окно. Второй – внутри клиентского, с серверным url. Данные отправляем через форму с указанным target=имя фрейма. Затем запускаем ф-цию с интервалом в 1 секунду и ждем пока хэш страницы не изменится – тогда мы получили ответ от сервера. Идентификатор изменяется серверным скриптом через self.parent.location = ‘…’

Стоит иметь ввиду, что в Internet Explorer есть ограничение на кол-во символов в url и составляет  2048 символов. Так что при передачи большого кол-ва данных придется делить на различные запросы. Кроме того необходимо в серверный фрейм передавать url основного окна – серверный фрейм его узнать не сможет.

При использовании https  этот способ корректно работать не будет.

postMessage (HTML 5)

В спецификации HTML 5 был введен метод

otherWindow.postMessage(message, targetOrigin);

в котором otherWindow – ссылка на другой объект window (например ифрейм на этой странице), message – данные которые должны послать, targetOrigin – домен на которой должны отослать данные, либо “*” – для всех доменов. targetOrigin должен соответствовать url объекта window на который посылаются данные.

В окне-приемнике должен быть зарегистрирован обработчик onmessage чтобы получить сообщение. Пример:

if (window.addEventListener){ 
    window.addEventListener(“message”, listener,false);
} else {
    window.attachEvent(“onmessage”, listener);
}

Ф-ция listener получается один параметр – объект event, по свойству origin которого можно узнать домен, с которого отправляли данные.

Работает postMessage в FF3+, IE8+ , Chrome, Safari 5, Opera10+. Стоит отметить что в IE8 нельзя общаться через окна/табы.

Особенности переноса < script >.

September 18th, 2011 No comments

Если перед вами встанет задача изменить положение контейнера (DIV’а, SPAN’а, etc), содержащего внутри себя тег SCRIPT, при помощи JavaScript то вы столкнетесь с такой проблемой: после переноса все содержимое страницы будет заменено на результат работы содержимого этого SCRIPT (в независимости от того подключает ли этот тег внешний скрипт или содержит внутренний).

Есть простое решение, этот скрипт уже отработал и больше запускаться не должен можно просто удалить его перед переносом, например вот так:

var scripts = obj.getElementsByTagName("script");
for (var n in scripts){
  obj.removeChild(scripts[n]);
}
// И уже чистый элемент
target.appendChild(obj);

То же самое на jQuery:

$(obj).find("script").remove();
// И уже чистый элемент
$(obj).appendTo(target);

Если же вы хотите что бы скрипты остались в рабочем состоянии то придется немного извратится:

var scripts = obj.getElementsByTagName("script");
var scripts_src_backup = Array();
var scripts_content_backup = Array();
for (var n in scripts){
  if(scripts[n].src !== undefined){
    scripts_src_backup.push(scripts[n].src);
  }else{
    scripts_content_backup.push(scripts[n].innerHTML);
  }
  obj.removeChild(scripts[n]);
}
// Переносим чистый элемент
target.appendChild(obj);
// Возвращаем скрипты на место
for (var n in scripts_src_backup){
  var new = document.createElement("script");
  new.src = scripts_src_backup[n];
  obj.appendChild(new);
}
delete(scripts_src_backup);
for (var n in scripts_content_backup){
  var new = document.createElement("script");
  new.innerHTML = scripts_content_backup[n];
  obj.appendChild(new);
}
delete(scripts_content_backup);

То же на jQuery:

var scripts_src_backup = Array();
var scripts_content_backup = Array();
$(obj).find("script").each(function(index, element){
  if(element.attr("src") !== undefined){
    scripts_src_backup.push(element.attr("src"));
  }else{
    scripts_content_backup.push(element.html());
  }
}).remove();
 
// Переносим чистый элемент
$(obj).appendTo(target);
 
// Возвращаем скрипты на место
for (var n in scripts_src_backup){
  $('< script type="text/javascript" ></ script >').attr("src", scripts_src_backup[n]).appendTo(obj);
}
delete(scripts_src_backup);
for (var n in scripts_content_backup){
  $('< script type="text/javascript" ></ script >').html(scripts_content_backup[n]).appendTo(obj);
}
delete(scripts_content_backup);

Таким образом элемент нормально переместился и скрипты остались рабочими.