Обновить всю страницу по запросу Ajax
У меня есть запрос AJAX, который может иметь два возможных результата:
- Сервер отвечает сообщением, которое я должен разместить в
<div>
- Сервер отвечает HTML-страницей, в этом случае мне нужно заменить текущую страницу на новую и изменить адрес (клиент знает адрес перед запросом).
Каким будет решение, если у меня есть запрос AJAX, который должен обрабатывать оба этих случая?
url = "http://example.com"
ajax.request(callback)
function callback(response) {
if (case2(response)) {
history.pushState({}, "New page", url);
document.innerHTML = response
} else {
updateDiv(response)
}
}
Мне интересен правильный способ реализации первой ветки, или если сервер может каким-то образом составить заголовки, которые заставят браузер обрабатывать ответ как обычный ответ HTTP и обновлять местоположение и содержимое страницы, что-то вроде перенаправления с данным контентом.
Я понимаю, что сервер может возвращать ссылку вместо страницы, но в этом случае на клиенте потребуется один дополнительный этап - перенаправление и последующее заполнение новой страницы на сервере.
Ответы
Ответ 1
Совершенно откровенно, я думаю, что этот подход в основном нарушается дизайном. Вы не должны принимать это решение в этом месте. Например, ответ ajax может сигнализировать только о том, что нужно загрузить всю новую страницу, а затем новый контент будет генерироваться на втором (не-ajax) запросе на новый URL-адрес.
Если вы вынуждены пойти по пути, который вы уже прошли, и при условии, что контент ответа не очень велик, вы можете попробовать Javascript-URI. В принципе, URI в форме javascript:"string"
будет загружать новую страницу, для которой эта строка является исходным кодом. Итак, если response
уже является строкой, достаточно просто назначить javascript:response
на window.location.href
. Может быть, вам нужно немного ускользнуть. И я не знаю, как кросс-браузер-совместимый этот подход.
<a href="javascript:response">load</a>
также возможно.
Вариант этого заключается в создании URL-адреса не с именем переменной, а с фактическими строковыми данными. Как
function source2url(src) {
// make valid javascript string from source text
var esc1 = src
.replace(/\\/g, '\\\\')
.replace(/\'/g, '\\\'')
.replace(/\x0A/g, '\\x0A')
.replace(/\x0D/g, '\\x0D');
// make valid url from that
return "javascript:'" + encodeURIComponent(esc1) + "'";
}
window.location.href = source2url(response);
Это, конечно же, генерирует довольно большие URI. И вы всегда будете иметь Javascript-URI в адресной строке.
UPDATE
Аналогичный подход заключается в использовании кодирования base64 в URI данных. Запись в Википедии объясняет, как это работает, включая пример javascript. Однако вы должны base64-encode контент каким-то образом. (Примечание. Вы можете использовать URI данных с кодировкой base64 или без нее. Вы должны увидеть, что дает более короткие URI для вашего конкретного контента.)
Ответ 2
У меня была аналогичная проблема. Была возвращена полная страница ошибки вместо простого фрагмента HTML. Мы в конце концов зафиксировали это, изменив логику, но вот одно из решений, которые я нашел:
document.open();
document.write(responseText);
document.close();
Причина, по которой мы отказались от этого, заключается в том, что в IE возникли некоторые проблемы. Я не терял времени, чтобы исследовать причину, но при попытке написать строку он выбрал исключение "Доступ запрещен". Я думаю, что есть теги <meta>
, которые путают IE, или, может быть, условные комментарии, я не уверен. (Он работал, когда я использовал несколько простых страниц...)
Нижняя строка: вам не нужно это делать, но если вы ничего не можете сделать (например, вернуть строку url), код может работать.
Ответ 3
Это очень просто, если ответ действителен XML.
var new_doc = (new DOMParser).parseFromString(response, "application/xml");
document.replaceChild(document.adoptNode(new_doc.doctype), document.doctype);
document.replaceChild(document.adoptNode(new_doc.documentElement), document.documentElement);
Ответ 4
Поскольку запрос содержит обновленный ответ, здесь мое решение с использованием HTML5 API истории с jQuery. Он должен работать легко, объединяя PHP и HTML-части в один файл.
Мое решение позволяет AJAX возвращать следующее:
- Сообщение через AJAX, которое обновляет контейнер
<div>
.
- URL-адрес, который заставляет браузер перенаправлять URL-адрес
- Полная HTML-страница, которая вызывает API истории
history.pushState()
, чтобы добавить текущий URL-адрес в историю браузера и заменяет весь HTML на странице HTML-кодом, возвращенным из AJAX.
PHP
Это всего лишь пример того, что PHP script должен будет вернуть, когда он вызывается через AJAX. Он показывает, как кодировать флаги, чтобы определить, должен ли вызов AJAX обновлять контейнер или загружать новую страницу, и как вернуть его результат через JSON через json_encode. Для полноты я назвал это script test.php.
<?php
// Random messages to return
$messages = array(
'Stack Overflow',
'Error Message',
'Testing'
);
// If the page was requested via AJAX
if( isset( $_POST['ajax']))
{
$response = array(
'redirect' => // Flag to redirect
( rand() % 2 == 0) ? true : false,
'load_html' => // Flag to load HTML or do URL redirect
( rand() % 2 == 0) ? true : false,
'html' => // Returned HTML
'<html><head><title>AJAX Loaded Title</title></head><body>It works!</body></html>',
'title' => 'History API previous title',
'message' => // Random message
$messages[ (rand() % count( $messages)) ]
);
echo json_encode( $response);
exit;
}
JS
Поскольку я использую jQuery, давайте начнем с этого. Следующее отправляет AJAX POST на сервер, на указанный выше PHP script по адресу URL test.php. Обратите внимание, что он также устанавливает для параметра POST ajax
значение true
, что позволяет PHP скрипт обнаруживать, что он получил запрос AJAX. Поле dataType
сообщает jQuery, что ответ сервера будет в JSON и что он должен декодировать JSON для объекта JSON в ответном обратном ответе. Наконец, обратный вызов success
, который запускается при успешном получении ответа AJAX, определяет, что делать на основе флагов, отправленных с сервера.
$.ajax({
type: 'POST',
url: "/test.php",
data: {ajax : true},
dataType: "json",
success: function( json) {
if( json.redirect) {
if( json.load_html) {
// If the History API is available
if( !(typeof history.pushState === 'undefined')) {
history.pushState(
{ url: redirect_url, title: document.title},
document.title, // Can also use json.title to set previous page title on server
redirect_url
);
}
// Output the HTML
document.open();
document.write( json.html);
document.close();
}
else {
window.location = redirect_url;
}
}
else {
$('#message').html( json.message);
}
},
});
HTML
Вот полный источник HTML моего тестируемого файла. Я тестировал его в FF4 - FF8. Обратите внимание, что jQuery предоставляет метод ready
для предотвращения выполнения JS до загрузки DOM. Я также использовал Google хостинг jQuery, поэтому вам не нужно загружать копию jQuery на ваш сервер, чтобы проверить это.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js" type="text/javascript"></script>
<title>Default Page</title>
<script type="text/javascript"">
$( document).ready( function() {
$('#ajax_link').click( function() {
var redirect_url = "/test.php";
$.ajax({
type: 'POST',
url: "/test.php",
data: {ajax : true},
dataType: "json",
success: function( json) {
if( json.redirect) {
if( json.load_html) {
// If the History API is available
if( !(typeof history.pushState === 'undefined')) {
history.pushState(
{ url: redirect_url, title: document.title},
document.title, // Can also use json.title to set previous page title on server
redirect_url
);
}
document.open();
document.write( json.html);
document.close();
}
else {
window.location = redirect_url;
}
}
else {
$('#message').html( json.message);
}
},
});
})
});
</script>
</head>
<body>
<div id="message">The default contents of the message</div>
<a id="ajax_link" href="#">Fire AJAX</a>
</body>
</html>
Ответ 5
Дайте id телу <body id="page">
, а ваш другой div будет <div id="message"></div>
, теперь ваш ajax будет выглядеть как
$.ajax({
url:'myAjax.php',
data:{datakey:datavalue},
dataType:"JSON",
success: function (response) {
if(response.message=="your message")
{
$('#message').html(response.content);
}
else
{
$('#page').html(response.content);
}
}
});
Ответ 6
как говорит T-Bull... весь процесс здесь неправильный....
вы просто слишком усложняете вещи, и вы знаете, что infact:
Я понимаю, что сервер может вернуть ссылку вместо страницы, но в этом случае на клиента потребуется еще один этап - перенаправить и затем заполнить новую страницу на сервере.
перестаньте усложнять и начните делать это хорошо...
- Клиент открывает страницу в первый раз, поэтому отслеживать ее
$_SESSION['tmp_client_id'] = 'client_'.session_id();
, очевидно, лучше, если клиент уже подписан, так или иначе, помещает материал в таблицу temp ИЛИ в другой сеанс var и т.д.
- Клиент заполнит форму;
- Клиент отправит форму;
- Сделать запрос AJAX;
- Хранить
$_POST
переменную внутри tmp_client_tbl
с ее уникальной tmp_client_id
ИЛИ просто $_SESSION['client_'.session_id()] = json_encode($_POST);
- Результат №1? отобразить сообщение в
</div>
- Результат №2? обновить страницу и проверить
if( isset($_SESSION['client_'.session_id()])) {
, если это позволяет снова отобразить форму с заполненными полями: } else {
показать пустую форму;
-
SELECT * FROM tmp_client_tbl WHERE tmp_client_id = '{$_SESSION['tmp_client_id']}'
ИЛИ json_decode($_SESSION['client_'.session_id()]);
-
$form_data = $mysql_rows;
ИЛИ $json_array;
-
foreach($form_data as $name => $value) { echo "<input name='$name' value='$value' />" }
в способе ниндзя, предполагающем, что у вас есть такой массив форм-форм, где $form = array('text' => array('name','lastname'), 'select' => array('countries'), ... )
, ИЛИ просто <input name='lastname' value='{$lastname}' />
, где значения полей предварительно распределены с пустыми vars;
- прошло время, произошла ошибка, браузер закрыт?
session_destroy();
или unset($_SESSION['client_'.session_id()]);