根据 Ajax 请求更新整个页面


Update whole page on Ajax request

我有一个 AJAX 请求,它可能有两种可能的结果:

  1. 服务器响应一条消息,我应该将其放在<div>
  2. 服务器以 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 响应处理并更新页面位置和内容,例如重定向给定内容。

我知道服务器可以返回链接而不是页面,但在这种情况下,客户端上需要一个额外的阶段 - 重定向,然后在服务器上填充新页面。

坦率地说,我认为这种方法基本上被设计打破了。你不应该在那个地方做出这个决定。例如,ajax 响应只能指示应该加载一个全新的页面,然后在对新 URL 的第二个(非 ajax)请求中生成新内容。

如果你被迫采取你已经走的路,并且响应内容不是很大,你可以尝试Javascript-URI。基本上,javascript:"string"形式的 URI 将加载一个新页面,该字符串是该页面的源代码。因此,如果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。

更新

类似的方法是在数据 URI 中使用 base64 编码。维基百科条目解释了它是如何工作的,包括一个javascript示例。但是,您必须以某种方式对内容进行base64编码。(注意:您可以使用带或不带 base64 编码的数据 URI。您必须查看是什么为您的特定内容提供了更短的 URI。

我曾经遇到过类似的问题。返回了完整的错误页面,而不是简单的 HTML 代码段。我们最终通过更改逻辑来解决此问题,但这是我找到的解决方案之一:

document.open();
document.write(responseText);
document.close();

我们放弃这个的原因是在IE上存在一些问题。我没有浪费任何时间来调查原因,但是在尝试写入字符串时,它抛出了"访问被拒绝"异常。我认为有一些<meta>标签使IE感到困惑,或者可能是条件注释,我不确定。(当我使用一些简单的页面时,它起作用了...

底线是:你不应该这样做,但如果你没有其他事情可以做(比如返回一个 url 字符串),上面的代码可能会起作用。

如果响应是有效的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);

由于请求是为了更新答案,这是我使用 HTML5 的 History API 和 jQuery 的解决方案。通过将PHP和HTML部分合并到一个文件中,它应该可以轻松运行。

我的解决方案允许 AJAX 返回以下内容:

  1. 通过 AJAX 的消息,用于更新<div>容器。
  2. 一个 URL,它会导致浏览器重定向到 URL
  3. 一个完整的 HTML 页面,它调用历史记录 API 的history.pushState()将当前 URL 添加到浏览器的历史记录中,并将页面上的整个 HTML 替换为从 AJAX 返回的 HTML。

.PHP

这只是通过 AJAX 调用 PHP 脚本时需要返回的示例。它演示如何对标志进行编码以确定 AJAX 调用是否应更新容器或加载新页面,以及如何通过 JSON 到 json_encode 返回其结果。为了完整起见,我将这个脚本测试命名为.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 到服务器,在 URL test.php 到上面的 PHP 脚本。请注意,它还将 POST 参数ajax设置为true,使 PHP 脚本能够检测到它收到了 AJAX 请求。dataType字段告诉 jQuery 服务器的响应将以 JSON 格式,并且它应该在响应回调中将该 JSON 解码为 JSON 对象。最后,成功接收 AJAX 响应时触发的success回调根据从服务器发送的标志确定要执行的操作。

$.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>

给正文<body id="page">一个id,你的另一个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);   
           }
      }
     });
正如

T-Bull所说...整个过程在这里是错误的。

你只是把事情复杂化了,你知道事实上:

我知道服务器可以返回链接而不是页面,但是 在这种情况下,客户端上需要一个额外的阶段 - 重定向,然后在服务器上填充新页面。

停止复杂化并开始做好它...

  1. 客户端第一次打开页面,因此,跟踪它$_SESSION['tmp_client_id'] = 'client_'.session_id();如果客户端已经订阅,显然更好,无论如何,将内容放入临时表中或放入另一个会话变量等......
  2. 客户填写表格;
  3. 客户提交表格;
  4. 发出 AJAX 请求;
  5. 将变量存储在tmp_client_tbl $_POST内,并具有唯一的tmp_client_id或只是$_SESSION['client_'.session_id()] = json_encode($_POST);
  6. 结果 #1 ? 在</div>中显示消息
  7. 结果 #2 ? 刷新页面并检查if( isset($_SESSION['client_'.session_id()])) {如果是这样,让我们再次显示带有填充字段的表单:} else {显示空表单;
  8. SELECT * FROM tmp_client_tbl WHERE tmp_client_id = '{$_SESSION['tmp_client_id']}'json_decode($_SESSION['client_'.session_id()]);
  9. $form_data = $mysql_rows;$json_array;
  10. 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}' />,其中字段值预先填充了空变量;
  11. 时间已过,发生错误,浏览器关闭? session_destroy();unset($_SESSION['client_'.session_id()]);