Кросдоменная передача данных между 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://easy4web.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.
Вот и весь секрет кросдоменной передачи данных между окнами.
Вот здесь (Передача данных между окнами в разных доменах) можно протестировать работу вышеприведенного алгоритма.
Замечу, что у всех приведенных здесь решений есть существенный недостаток – корректно передаются только символы английского алфавита, цифры и знаки препинания. Решение этой проблемы оставлю на одну из следующих статей.
Прочтите еще:

