Кросдоменная передача данных между html-страницами

Вячеслав Гринин, April 29, 2010

Итак, представим себе ситуацию, что на некотором сайте в некоторой форме есть поле, в которое нужно ввести логин пользователя, но не свой собственный, а чужой логин, предположим, пользователя, которого надо добавить в друзья или в черный список. Но посетитель может не помнить наизусть, как пишется этот логин, а потому мы сделаем так, чтобы он мог выбрать его из списка, причем список этот должен открыться в отдельном окне и там помимо списка логинов пользователей должны отображаться еще и их фотографии, ФИО, возраст и т.д. Предположим, что мы даже создали такую страницу со списком пользователей. Возникает вопрос – как передать из одного окна браузера в другое окно некоторые данные (в данном случае это – логин пользователя)?

Справочник по JavaScript и объектной документной модели DOM говорит нам, что для открытия нового окна нужно использовать метод window.open(), а для доступа из “дочернего” окна в “родительское” (то есть то, которое и породило новое окно) нужно использовать указатель opener. Рассмотрим этот факт на примере:

index.htm

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
 <title>Главное окно</title>
 <script>
 function openWindow() {
 window.open("http://easy-4-web.ru/samples/transfer/popup.htm",
    "contents", "toolbar=no", "status=no");
 }
 </script>
 </head>
 <body>
 <input type="button" onclick="openWindow()"
    value="Показать диалог"/><br />
 <input type="text" id="data" />
 </body>
</html>

popup.htm

<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
 <title>Диалоговое</title>
 <script>
 function transferData() {
 opener.document.getElementById("data").value =
    document.getElementById("inp").value;
 window.close();
 }
 </script>
 </head>
 <body>
 <input type="text" id="inp" name="inp"/>
 <input type="button" value="OK" onclick="transferData()"/>
 </body>
</html>

В главной странице мы видим кнопку, по нажатии на которую открывается новое окно, и TEXTBOX с айдишником data, в него-то и будет попадать текст, введенный в диалоговом окне.

А вот и диалоговое окно, в нем мы видим TEXTBOX и кнопку по нажатию на которую текст введенный в текстовое поле присваивается текстовому полю data из главного окна. А доступ к нему мы и получаем при помощи указателя opener.

Вот здесь (Передача данных между окнами в рамках одного домена) вы можете протестировать работу этого алгоритма.

Здесь все просто, и я не стал бы писать эту статью, если бы хотел рассказать только про это.

А хочу я теперь рассказать о том, как быть если окна эти расположены в разных доменах. Когда такое может произойти? Предположим, есть Ваш форум, а есть специализированный сервис для загрузки и хранения фотографий и вот теперь владелец форума хочет договориться с владельцем фото-сервиса, что на форму он разместит кнопку “Добавить изображение”, которая будет открывать окно созданное в рамках фото-сервиса, в этом окне пользователь загрузит фотографии, а затем по нажатию кнопки “ОК” ссылки на фотографии скопируются в окно редактирования сообщения на форуме. Как видим, здесь есть два окна в разных доменах и нам нужно передать текст из одного окна в другое.
“Что тут сложного?” – спросите вы. А давайте попробуем.

Невозможность передачи данных между окнами в разных доменах

При попытке передачи данных между окнами получим ошибку Error: Access is denied.. Происходит это потому что прежде чем получить доступ к любому методу или свойству объекта opener браузер сравнит домен, в котором существует этот объект и домен, из которого происходит вызов собственно метода или свойства объекта opener. И если доменные имена не совпадают, будет возбуждено исключение “Доступ запрещен”.

Решить эту проблему можно. И мы решим ее без использования каких-либо серверных технологий, только силами JavaScript. Это становится возможным, если мы узнаем еще вот какую тонкость. При изменении свойства location.href в порожденном окне в него загрузится страница, заданная ссылкой, но значение указателя opener не изменится, он так и будет продолжать указывать на породившее его главное окно. А теперь представим себе, что мы в диалоговое окно загрузили все ту же главную страницу, или любую другую но с того же домена, где лежит главная страница, а после этого обратились к объекту opener, теперь домены диалогового окна и объекта opener снова совпадают, а значит исключение возбуждено не будет.

А теперь – время для исходников, иллюстрирующих пример:

index.htm

<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
 <title>Главное окно</title>
 <script>
 function openWindow() {
 var par = "?loc=" + location.href + "&id=data";
 window.open("http://easyapi.ru/easy4web/transfer/popup2.htm" + par, 
    "contents", "toolbar=no", "status=no");
 }
 function getDataFromUrl() {
 var txt_id, data;
 var url = location.href;
 var query = url.split("?")[1];
 if(query) {
 var params = query.split("&");
 for(var i = 0; i < params.length; i++) {
 var keyval = params[i].split("=");
 if(keyval[0] == "data") {
 data = keyval[1];
 }
 if(keyval[0] == "id") {
 txt_id = keyval[1];
 }
 }
 }
 if(txt_id && data) {
 opener.document.getElementById(txt_id).value = data;
 close();
 }
 }
 getDataFromUrl();
 </script>
 </head>
 <body>
 <input type="button" onclick="openWindow()" 
    value="Показать диалог"/><br/>
 <input type="text" id="data" />
 <script>
 getDataFromUrl();
 </script>
 </body>
</html>

Итак, здесь мы видим, что процедура openWindow() все также открывает диалоговое окно, однако теперь она передает ему параметры: loc – указывающий на URL самой порождающей страницы; id – айдишник текстбокса, в который будет вставлен текст из дочернего окна.
А еще мы видим, что после загрузки контента главной страницы вызывается функция getDataFromUrl(), которая проверяет, есть ли GET-параметры id и data (айдишник текстбокса и текст, который мы в него будем вставлять). Параметры эти при изначальной загрузке страницы не заданы, а сначит при первой загрузке страницы код обновляющий содержимое текстбокса не выполнится. А когда он выполнится мы узнаем после того, как рассмотрим исходники диалогового окна.

popup.htm

<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
 <title>Диалог</title>
 <script>
 function transferData() {
 var url = location.href;
 var query = url.split("?")[1];
 if(query) {
 var params = query.split("&");
 for(var i = 0; i < params.length; i++) {
 var keyval = params[i].split("=");
 if(keyval[0] == "loc") {
 var loc = keyval[1];
 }
 if(keyval[0] == "id") {
 var txt_id = keyval[1];
 }
 }
 }
 if(loc && txt_id) {
 location.href = loc + "?id=" + txt_id + 
    "&data=" + document.getElementById("inp").value;
 }
 }
 </script>
 </head>
 <body>
 <input type="text" id="inp" name="inp"/>
 <input type="button" value="OK" onclick="transferData()"/>
 </body>
</html>

Здесь при клике по кнопке выполнится функция transferData(), которая сначала извлечет из адресной строки параметры loc и id, те самые, которые мы передали окну при его порождении. И если эти параметры есть – присвоим location.href адрес страницы loc(главной страницы), а в GET-параметры ей передадим id и data(айдишник текстбокса и данные, которые мы в него запишем). При этом в текущее окно (диалоговое) загрузится содержимое главнйо страницы и выполнится та самая функция getDataFromUrl() которая на этот раз извлечет все необходимые параметры из GET-строки и выполнит метод getElementById() объекта opener.document.

Вот и весь секрет кросдоменной передачи данных между окнами.

Вот здесь (Передача данных между окнами в разных доменах) можно протестировать работу вышеприведенного алгоритма.

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

В тему:

5комментариев

Спасибо за полезную статью, подписался на ваш блог 🙂

Adam, July 14, 2010 1:37 am Reply

Огромное спасибо за статью!

Geo, October 5, 2010 2:32 pm Reply

Спасибо за алгоритрм. Для этого нужно вникать в это дело, а у меня терпения нехватает.

collaps, March 17, 2012 1:23 am Reply

а что делать, если нужно передать два и более данных из popup окна?

владимир, September 2, 2013 9:03 am Reply

к элементу data еще добавить GET-параметров, и каждый так же обрабатывать, как и data.

Вячеслав Гринин, September 2, 2013 10:49 am Reply
Ваше имя
Ваш email*
Ваш сайт
Текст вашего комментария:

Поиск по блогу:
Подписаться:
Популярные:
Облако тегов:
Разное:
Счетчик: