Кросдоменная передача данных между html-страницами
Итак, представим себе ситуацию, что на некотором сайте в некоторой форме есть поле, в которое нужно ввести логин пользователя, но не свой собственный, а чужой логин, предположим, пользователя, которого надо добавить в друзья или в черный список. Но посетитель может не помнить наизусть, как пишется этот логин, а потому мы сделаем так, чтобы он мог выбрать его из списка, причем список этот должен открыться в отдельном окне и там помимо списка логинов пользователей должны отображаться еще и их фотографии, ФИО, возраст и т.д. Предположим, что мы даже создали такую страницу со списком пользователей. Возникает вопрос – как передать из одного окна браузера в другое окно некоторые данные (в данном случае это – логин пользователя)?
Справочник по 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.
Вот и весь секрет кросдоменной передачи данных между окнами.
Вот здесь (Передача данных между окнами в разных доменах) можно протестировать работу вышеприведенного алгоритма.
Замечу, что у всех приведенных здесь решений есть существенный недостаток – корректно передаются только символы английского алфавита, цифры и знаки препинания. Решение этой проблемы оставлю на одну из следующих статей.
В тему:
Спасибо за полезную статью, подписался на ваш блог 🙂
Adam, July 14, 2010 1:37 amОгромное спасибо за статью!
Geo, October 5, 2010 2:32 pmСпасибо за алгоритрм. Для этого нужно вникать в это дело, а у меня терпения нехватает.
collaps, March 17, 2012 1:23 amа что делать, если нужно передать два и более данных из popup окна?
владимир, September 2, 2013 9:03 amк элементу data еще добавить GET-параметров, и каждый так же обрабатывать, как и data.
Вячеслав Гринин, September 2, 2013 10:49 am