在不重新加載頁面的情況下提交表單:WordPress 中的 AJAX 實現
已發表: 2022-03-10如果您曾經想在不重新加載頁面的情況下發送表單,提供前瞻搜索功能,在用戶鍵入時提示他們建議,或者自動保存文檔,那麼您需要的是AJAX (也稱為XHR )。 後台請求被發送到服務器,並將數據返回到您的表單。 每當您在頁面上執行某些操作後看到加載程序動畫時,這可能是一個 AJAX 請求正在提交給服務器。
在本文中,我將指導您完成創建和處理 AJAX 調用的整個過程。 您不僅將學習如何進行 AJAX 調用,還將學習如何使用 WordPress 為開發人員提供的開箱即用功能以最佳方式進行調用。 我們將構建一個簡單的插件,允許讀者向管理員發送報告,其中包含他們可能在您的網站上發現的任何錯誤的信息。

在文章的最後,您可以下載工作示例並完整檢查代碼。
讓我們深入挖掘!
AJAX
“無需重新加載頁面”是這裡的關鍵語句。 AJAX 代表異步 JavaScript 和 XML ,因為最初,從服務器返回的數據應該是 XML。 但是,以 JSON 格式發送它們更容易,這是 JavaScript 更喜歡的。
我們將使用 AJAX 發送電子郵件。 您無法從前端執行此操作,因此您必須調用後端。 通常,我們會向服務器發送一個 POST 請求,處理它並將用戶重定向回帶有表單的頁面。 在這個迭代中,我們不想重新加載頁面。 相反,我們直接調用後端,我們將使用 JavaScript 捕獲表單數據,然後向服務器發送異步請求以處理響應。
WordPress AJAX 需要做三件事——其中五件事要正常工作。 這些是:
- AJAX 操作的對象
- JavaScript 腳本
- WordPress 動作
- 保護
- 錯誤處理
讓我們仔細看看它們中的每一個。
目的
我們的對像是形式。 這是我們用 JavaScript 處理的事情。 我開始創建一個帶有 WordPress 插件所需標題的文件,並在其中放入一個空對象。 最後,我正在創建插件類的新實例。
<?php /* Plugin Name: Report a bug Description: Allow your visitors to report a bug in your articles Author: Jakub Mikita Author URI: https://underdev.it Version: 1.0 License: GPL2 Text Domain: reportabug */ class Report_a_bug { } new Report_a_bug();
儘管我在這裡使用了一些 OOP,但我們不會使用任何高級實踐。 當使用單獨的函數以程序方式編寫時,代碼也可以正常工作。 但是 WordPress 插件中的對像有一個優點:您不必為函數添加前綴——只有類名必須是唯一的。
讓我們向用戶展示我們的表單。 我們將連接到the_content
過濾器並將表單嵌入到每個帖子的末尾。 在類的構造函數中,我添加了過濾器:
public function __construct() { add_filter( 'the_content', array( $this, 'report_button' ), 10, 1 ); }
並使用表單 HTML 創建了一個回調方法:
public function report_button( $content ) { // display button only on posts if ( ! is_single() ) { return $content; } $content .= '<div class="report-a-bug"> <button class="show-form" data-post_>' . __( 'Report a bug', 'reportabug' ) . '</button> <textarea class="report-a-bug-message" placeholder="' . __( 'Describe what\'s wrong...', 'reportabug' ) . '"></textarea> <p class="report-a-bug-response"></p> </div>'; return $content; }
該表單具有所有需要的標記:
- 顯示表單和發送消息的按鈕;
-
textarea
; - 響應的容器(我們稍後將使用它)。
按鈕具有存儲當前帖子 ID 的data-post_id
屬性。 我們將在 JavaScript 中抓取它來識別文章。
我們只需要一些基本的樣式,所以讓我們用wp_enqueue_scripts
動作和相應的回調註冊我們自己的樣式表:
public function __construct() { add_filter( 'the_content', array( $this, 'report_button' ), 10, 1 ); add_action( 'wp_enqueue_scripts', array( $this, 'scripts' ) ); } function scripts() { wp_enqueue_style( 'report-a-bug', plugin_dir_url( __FILE__ ) . 'css/style.css' ); }
樣式表本身非常簡單。 我們想整理設計。
.report-a-bug-message { display: none; margin-top: 1em; } .report-a-bug-response { margin-top: 1em; }
這是按鈕的外觀:

腳本
我們已經準備好給它注入一些生命的對象。 我們將使用 WordPress 默認在每個頁面上加載的 jQuery,而不是使用純 JavaScript 執行此操作。
在現有的腳本方法中,我將使用wp_enqueue_script
函數添加 JS 文件。
function scripts() { wp_enqueue_style( 'report-a-bug', plugin_dir_url( __FILE__ ) . 'css/style.css' ); wp_enqueue_script( 'report-a-bug', plugin_dir_url( __FILE__ ) . 'js/scripts.js', array( 'jquery' ), null, true ); // set variables for script wp_localize_script( 'report-a-bug', 'settings', array( 'send_label' => __( 'Send report', 'reportabug' ) ) ); }
我還使用wp_localize_script
函數將翻譯後的按鈕標籤傳遞給 JS。 我們在 JavaScript 中沒有任何gettext
函數,所以我們必須在這裡做。 此函數將創建可以直接從我們的腳本訪問的設置對象。
在我們的腳本中,我們將監聽按鈕點擊。 一旦發生這種情況,我們將顯示textarea
並切換按鈕類以偵聽發送表單的第二次單擊。
( function( $ ) { $( document ).ready( function() { $( '.report-a-bug' ).on( 'click', '.show-form', function( event ) { // change label and switch class $( this ).text( settings.send_label ).removeClass( 'show-form' ).addClass( 'send-report' ); // show textarea $( '.report-a-bug-message' ).slideDown( 'slow' ); }) }); })( jQuery );
以下是目前的進展:

textarea
下拉菜單現在是創建 AJAX 請求的最佳時機!
對 WordPress 的 AJAX 請求
要發送 AJAX 請求,您實際上只需要一個參數:請求的 URL。 WordPress 有用於 AJAX 的特殊文件,因此我們不必創建自己的文件。 它是/wp-admin/admin-ajax.php
。
只要我們在wp-admin
區域,這個文件 URL 在 JS 中的ajaxurl
變量中是可用的。 在前端,我們必須自己傳遞這個變量。 幸運的是,我們已經使用了wp_localize_script
函數,所以我們可以添加另一個鍵:
wp_localize_script( 'report-a-bug', 'settings', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'send_label' => __( 'Send report', 'reportabug' ) ) );
我們已經準備好所有變量,所以讓我們創建 AJAX 調用。 一旦用戶第二次單擊按鈕,我們將發送它。
$( '.report-a-bug' ).on( 'click', '.send-report', function( event ) { var $button = $( this ); $button.width( $button.width() ).text('...'); // set ajax data var data = { 'action' : 'send_bug_report', 'post_id': $button.data( 'post_id' ), 'report' : $( '.report-a-bug-message' ).val() }; $.post( settings.ajaxurl, data, function( response ) { console.log( 'ok' ); } ); } );
我們正在監聽我們之前打開按鈕的課程的點擊。
如您所見,我將按鈕文本更改為“...” 為什麼? 因為向用戶展示正在發生的事情是一種很好的做法。 AJAX 請求取決於服務器性能並且需要一些時間。 可能是 30 毫秒,但也可能是 3 秒。 如果單擊按鈕後似乎沒有任何效果,則很可能會再次單擊該按鈕。 這會重複請求,因為您現在知道,這些是異步的。
接下來,我正在創建data
對象。 這包含將發送到服務器回調的所有變量。 WordPress admin-ajax.php
文件需要 action 屬性。 這必須是唯一的,除非您希望其他插件處理您的請求。 其餘參數是可選的。 我正在從textarea
發送帖子 ID 和報告消息。
然後我們調用$.post
方法。 接下來,你猜對了; 它正在發送 POST 請求。 這是一種速記方法,但您也可以使用$.ajax
方法,它有更多選項。 作為第一個參數,我們必須傳遞我們的處理程序文件 URL,然後是參數,然後是成功回調函數。 這是我們處理響應的地方。 現在,我們只是將簡單的消息發送到瀏覽器的控制台。

我們已準備好在後端處理請求。

WordPress 動作
您可能想知道我們如何掛鉤到admin-ajax.php 。 當然是用行動! WordPress 有兩種操作類型:
wp_ajax_nopriv_{$action} wp_ajax_{$action}
$action
在哪裡,AJAX 參數中傳遞的動作名稱。 在我們的例子中是send_bug_report
。
這些操作中的第一個將僅對未登錄的用戶執行。 第二個僅適用於登錄用戶。 因此,如果您希望兩者都處理請求,則必須同時定義兩者。 這是我在類的構造函數中所做的:
public function __construct() { add_filter( 'the_content', array( $this, 'report_button' ), 10, 1 ); add_action( 'wp_enqueue_scripts', array( $this, 'scripts' ) ); add_action( 'wp_ajax_nopriv_send_bug_report', array( $this, 'send_bug_report' ) ); add_action( 'wp_ajax_send_bug_report', array( $this, 'send_bug_report' ) ); }
在回調中,我只是得到帖子標題,我正在發送電子郵件:
function send_bug_report() { $data = $_POST; $post_title = get_the_title( intval( $data['post_id'] ) ); wp_mail( '[email protected]', 'Bug report in post: ' . $post_title, $data['report'] ); wp_send_json_success( __( 'Thanks for reporting!', 'reportabug' ) ); }
在 WordPress 中處理 AJAX 方面最重要的部分是最後一個函數 - wp_send_json_success
,它打印 JSON 編碼的輸出並終止。 我們只需要通過 AJAX 成功回調來獲取響應。 它還有一個孿生兄弟wp_send_json_error
,但我們稍後會談到這部分。
這些函數的對像有兩個屬性:
- 成功
是一個布爾值,取決於您將其稱為成功函數還是錯誤函數。 - 數據
如果您提供函數的參數。
這一切都來自後端。 讓我們在 JS 中處理響應。
我們將移除按鈕和文本區域,並在我們之前準備的容器中顯示從服務器返回的消息:
$.post(settings.ajaxurl, data, function(response) { // remove button and textarea $button.remove(); $('.report-a-bug-message').remove(); // display success message $('.report-a-bug-response').html( response.data ); });
就是這樣! 我們有有效的 AJAX 調用! 但不要止步於此。 我們的請求不安全,當然也不是用戶友好的。 我們必須確保請求僅在必要時才被執行。
保護
隨機數
nonce 是一個“使用過一次的數字”。 它是從輸入字符串創建的短散列。 我們可以使用它來驗證請求——如果它真的是由 WordPress 製作的並且沒有人偽造它。
我們將為按鈕使用另一個數據屬性:
$nonce = wp_create_nonce( 'report_a_bug_' . get_the_ID() ); $content .= '<div class="report-a-bug"> <button class="show-form" data-nonce="' . $nonce . '" data-post_>' . __( 'Report a bug', 'reportabug' ) . '</button> <textarea class="report-a-bug-message" placeholder="' . __( 'Describe what\'s wrong...', 'reportabug' ) . '"></textarea> <p class="report-a-bug-response"></p> </div>';
如您所見,我將帖子 ID 添加到輸入字符串中。 我們將在 AJAX 請求屬性中提供此 ID,因此它更加安全。
現在我們必須將 nonce 添加到 AJAX 屬性中:
// set ajax data var data = { 'action' : 'send_bug_report', 'post_id': $button.data('post_id'), 'nonce' : $button.data('nonce'), 'report' : $('.report-a-bug-message').val() };
現在我們將使用 WordPress 提供的check_ajax_referer
函數在後端驗證它:
function send_bug_report() { $data = $_POST; // check the nonce if ( check_ajax_referer( 'report_a_bug_' . $data['post_id'], 'nonce', false ) == false ) { wp_send_json_error(); } $post_title = get_the_title( intval( $data['post_id'] ) ); wp_mail( '[email protected]', 'Bug report in post: ' . $post_title, sanitize_text_field( $data['report'] ) ); wp_send_json_success( __( 'Thanks for reporting!', 'reportabug' ) ); }
為了驗證請求,我們必須重新生成輸入字符串,所以我使用從 AJAX 發送的post_id
鍵。 第二個參數是$_REQUEST array
中的鍵。 如果 nonce 不匹配,第三個控制自動wp_die
。
我不希望它自己死去。 相反,我正在捕捉這個函數的結果,並在一個不錯的對像中發送 JSON 錯誤。
您可能還會注意到在電子郵件參數中使用了sanitize_text_field
函數。 這只是為了確保用戶不會發送任何有害的腳本或 HTML。
最後,我們需要在 if 語句中將 JS 中的 AJAX 成功回調包裝起來,以檢查請求是否成功:
$.post(settings.ajaxurl, data, function(response) { if ( response.success == true ) { // remove button and textarea $button.remove(); $('.report-a-bug-message').remove(); // display success message $('.report-a-bug-response').html( response.data ); } });
按鍵保護
您知道用戶可以在 AJAX 完成調用之前第二次單擊該按鈕。 但是有一個簡單的技巧可以阻止第二次點擊——禁用按鈕。 所以點擊後,我要屏蔽它,得到響應後解除屏蔽:
$('.report-a-bug').on('click', '.send-report', function(event) { var $button = $(this); $button.width( $button.width() ).text('...').prop('disabled', true); // set ajax data var data = {...}; $.post(settings.ajaxurl, data, function(response) { if ( response.success == true ) {...} // enable button $button.prop('disabled', false); }); });
錯誤處理
驗證
如果用戶嘗試發送空消息怎麼辦? 我不想被這樣的電子郵件打擾。 讓我們用驗證技術阻止這些嘗試。
在 JS 中,我將添加一個簡單的驗證,以向用戶顯示出了問題。 如果消息為空,用戶將看到文本區域周圍的紅色邊框。 如果那裡有消息,我們正在恢復中性邊界:
$('.report-a-bug').on('click', '.send-report', function(event) { var $button = $(this); // check if message is not empty if ( $( '.report-a-bug-message' ).val().length === 0 ) { $( '.report-a-bug-message' ).css( 'border', '1px solid red' ); return false; } else { $( '.report-a-bug-message' ).css( 'border', '1px solid rgba(51, 51, 51, 0.1)' ); } // ... ajax });
檢查 WordPress 操作中的錯誤
我們也可能在發送電子郵件時遇到問題。 如果我們不檢查wp_mail
函數的結果,即使沒有發送電子郵件,用戶也會收到成功消息。
讓我們處理這個:
function send_bug_report() { $data = $_POST; // check the nonce if ( check_ajax_referer( 'report_a_bug_' . $data['post_id'], 'nonce', false ) == false ) { wp_send_json_error(); } $post_title = get_the_title( intval( $data['post_id'] ) ); $result = wp_mail( '[email protected]', 'Bug report in post: ' . $post_title, sanitize_text_field( $data['report'] ) ); if ( $result == false ) { wp_send_json_success( __( 'Thanks for reporting!', 'reportabug' ) ); } else { wp_send_json_error(); } }
如您所見,我們兩次使用了wp_send_json_error
函數,但沒有必要為用戶顯示唯一的消息。 相反,傳遞確切的錯誤描述,我將向我們的腳本設置對象添加另一個鍵,它將涵蓋這兩個錯誤:
// set variables for script wp_localize_script( 'report-a-bug', 'settings', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'send_label' => __( 'Send report', 'reportabug' ), 'error' => __( 'Sorry, something went wrong. Please try again', 'reportabug' ) ) );
剩下要做的就是向用戶顯示錯誤:
$.post( settings.ajaxurl, data, function( response ) { if ( response.success == true ) { // remove button and textarea $button.remove(); $( '.report-a-bug-message' ).remove(); // display success message $( '.report-a-bug-response' ).html( response.data ); } else { // display error message $( '.report-a-bug-response' ).html( settings.error ); } // enable button and revert label $button.text( settings.send_label ).prop( 'disabled', false ); } );
完整示例
我們已經做到了! 我們的 AJAX 請求已經發出; 它安全可靠且用戶友好。 這是用戶在出現任何錯誤時將看到的內容:

您可以在下面下載完整的插件並查看用戶指南:
- 在 Github 上下載插件
- 在 Wordpress.org 上下載插件
- 轉到用戶指南
我希望這為您自己的“無頁面刷新”解決方案奠定了良好的基礎。 您可以對網站上的所有表格執行此操作。 這也是優化不需要立即加載的網站的任何繁重部分的好方法,例如下拉列表中的大產品列表。 您可以在單擊下拉菜單後通過 AJAX 加載它們——就這麼簡單。
可能性幾乎是無限的。 在評論中讓我知道你的想法是什麼!