在插件中使用AJAX

42条评论

简介

Ajax(异步JavaScript与XML)是一种允许网页在没有完全重新加载的情况下更新某些信息的技术。WordPress管理面板用Ajax来自动保存日志、添加新分类等。一些WordPress插件也用AJAX来计算日志投票数和镜像更新。

这篇文章向插件开发人员介绍了怎样在插件中添加Ajax。继续阅读前,最好对下面这些内容有所了解:

  • Ajax——Ajax技术简介
  • 插件开发——插件编写方法
  • 插件API—介绍过滤器钩子和动作钩子及其用法
  • 怎样在适当的WordPress页面、日志或界面中添加HTML代码——例如,如果希望在自己创建的管理界面上加入Ajax,首先需要了解怎样为WordPress添加管理菜单;如果需要用Ajax来显示某篇特殊日志,则必须要学会用适当的过滤器函数和动作函数将HTML添加到访问者正浏览的博客页面上。本文不涉及此类内容。
  • 要在WordPress中使用Ajax,还需要熟悉客户端的Javascript、PHP和HTML编程语言。

试验表明,在插件中加入Ajax并非易事,这取决于我们需要将Ajax的功能整合到WordPress后台,还是利用Ajax为WordPress网站/博客的访问者展示信息。(介绍一些常见问题后)本文将分别讨论这两种情况。

Ajax基本知识

总体来说,进行一次Ajax请求需要三个步骤:

1. 用户执行某个操作(如点击或拖拽鼠标),之后嵌套在网页HTML中的Javascript会生成一个“请求”并将请求发送到web服务器上某个特定URL,以此响应用户的操作。考虑到安全因素,接收请求的URL与存储Javascript的文件必须位于同一台web服务器上。

2. web服务器上的某个脚本或程序(在WordPress中通常是一个或一个以上的PHP函数)处理请求并将信息传回浏览器。

3. 利用Javascript显示返回的信息。

令人遗憾的是,大多数运行在用户的web浏览器中的Ajax都是Javascript,有些web浏览器甚至不能执行Ajax调用或响应Ajax调用。为方便起见,大多数Ajax开发人员选择使用经过测试的跨浏览器库,这个浏览器库利用一个带有文件记录的标准类来包装浏览器特质。在这篇文章中我们也利用WordPress中一个类似的浏览器库——SACK(简易Ajax代码工具包)展开说明。文章的延伸阅读部分(见文章最下方)中也介绍了一些用JQuery来执行AJAX的资料——JQuery也是WordPress中的浏览器库,JQuery和SACK基本原理大致相同,只是在句法上有些差别。

首先我们需要确定,组成Ajax请求的SACK库和Javascript函数都已经被包含在网页的HTML的head版块中,Ajax请求会在这个网页上触发;之后我们会分别介绍怎样在WordPress后台和访问者浏览页面上执行Ajax。

用SACK库生成Ajax请求时,我们还需要提供以下信息;文章稍后会详细说明这些信息在WordPress后台和访问者浏览页面上的作用:

  • 请求URL:服务器上用以处理Ajax请求的URL
  • 自定义请求变量:SACK允许用户设置任意请求变量,变量将通过POST或GET传输到服务器上。POST和GET还能够传输cookie信息。
  • 出错时如何继续:出现Ajax错误时调用特定Javascript函数。

默认情况下SACK假定服务器返回的信息为进入(异步)服务器后已被执行的Javascript代码。在下面的示例中,我们遵循SACK的假定,因此(处理Ajax请求的)PHP函数需要将其结果形成Javascript命令。我们打算在插件中对返回的信息进行其它操作前,可能还需要进入SACK的主页下载压缩文件并阅读说明文档,了解相关信息。

还有一个细节需要注意:处理Ajax请求的PHP函数应使用PHP die函数来传回Javascript信息。示例:die("javascript_commands_here")。

稍后的两个示例以上述Ajax基本知识为起点,分别介绍了怎样在WordPress后台和访问者浏览页面上使用Ajax。两个示例部分都是独立存在的,读者可以选择性阅读。

WordPress后台的Ajax

Ajax已经存在于WordPress管理界面中,因此在插件中添加管理界面端的Ajax功能相当容易,接下来我们会详细介绍添加过程。如果要在WordPress网站/博客的访问者浏览页面上使用Ajax,可以跳过这部分内容。

假设目前有一个地域标签插件,用户会在插件中为日志设置经度和纬度,然后插件利用一项网络服务来查看日志所在地点的海拔。我们将这个插件作为在WordPress插件后台使用Ajax的实例。进行Ajax编程前,首先要提供一些字段,让用户能够输入日志的经纬度,一个用来查询海拔的按钮,以及一个显示海拔高度的字段。假设我们已经知道怎样在适当位置上添加Ajax,怎样按照自己的喜好调整字段宽度、文本以及字段样式;然后在后台的HTML表中加入以下代码:

Latitude: <input type="text" name="latitude_field" />
Longitude: <input type="text" name="longitude_field" />
<input type="button" value="Look Up Elevation" 
onclick="myplugin_ajax_elevation(this.form.latitude_field, this.form.longitude_field, this.form.elevation_field);" 
/>
Elevation: <input type="text" name="elevation_field" id="elevation_field" />

接下来我们需要定义Javascript函数myplugin_ajax_elevation,而OnClick动作函数则会读取用户输入的信息,用SACK生成一个请求,然后将请求发送给插件进行处理。之前也曾提到,这个Javascript函数和SACK库都需要被包含在相应管理界面HTML代码的head版块中;最简单的方法是利用admin_print_scripts动作钩子函数将JavaScript函数添加到所有管理界面中:

add_action('admin_print_scripts', 'myplugin_js_admin_header' );

function myplugin_js_admin_header() // this is a PHP function
{
  // use JavaScript SACK library for Ajax
  wp_print_scripts( array( 'sack' ));

  // Define custom JavaScript function
?>
<script type="text/javascript">
//<![CDATA[
function myplugin_ajax_elevation( lat_field, long_field, elev_field )
{
    // function body defined below

} // end of JavaScript function myplugin_ajax_elevation
//]]>
</script>
<?php
} // end of PHP function myplugin_js_admin_header

下一步需要填写JavaScript函数myplugin_ajax_elevation的主体部分,该部分需要能够从表单域中读取经纬度,用SACK生成一个AJAX请求,然后将请求发送给服务器。在这个例子中,我们需要设置以下信息:

  • 请求URL:我们要将请求发送到特定URL中,WordPress管理菜单系统已经定义了这个URL:(bloghome)/wp-admin/admin-ajax.php。下面我们介绍怎样将某个Ajax动作钩子添加到WordPress中,告诉脚本接收到请求时应该调用哪个插件PHP函数。为方便叙述,我们假设动作钩子名为"myplugin_elev_lookup"。
  • 自定义请求变量:我们要将经纬度发送给服务器;我们也需要要将动作钩子名称发送给admin-ajax.php脚本。此外,我们还需要发送当前页面的cookies(其中包含登录信息)。最后,由于服务器端函数需要返回JavaScript以显示结果,我们还需要将海拔字段ID发送到服务器端函数上。

整合所有信息后,JavaScript函数的主体显示如下:

function myplugin_ajax_elevation( lat_field, long_field, elev_field )
{
   var mysack = new sack( 
       "<?php bloginfo( 'wpurl' ); ?>/wp-admin/admin-ajax.php" );    

  mysack.execute = 1;
  mysack.method = 'POST';
  mysack.setVar( "action", "myplugin_elev_lookup" );
  mysack.setVar( "latitude", lat_field.value );
  mysack.setVar( "longitude", long_field.value );
  mysack.setVar( "elev_field_id", elev_field.id );
  mysack.encVar( "cookie", document.cookie, false );
  mysack.onError = function() { alert('Ajax error in looking up elevation' )};
  mysack.runAJAX();

  return true;
} // end of JavaScript function myplugin_ajax_elevation

接下来是最后一步——定义Ajax请求到达服务器时的反应。根据文章之前的描述,我们将请求发送到(bloghome)/wp-admin/admin-ajax.php,动作函数的参数为"myplugin_elev_lookup"。我们用 wp_ajax_*动作函数告诉WordPress接收到Ajax请求时应该调用插件中的哪个PHP函数。

PHP函数将经纬度发送给海拔查询服务器,等待响应并解析返回的结果,最终根据JavaScript的指令传回返回的信息。我们用web请求PHP类“Snoopy”(WordPress内置类)来发送web请求。步骤如下:

add_action('wp_ajax_myplugin_elev_lookup', 'myplugin_ajax_elev_lookup' );

function myplugin_ajax_elev_lookup()
{
  // read submitted information

  $lat = $_POST['latitude'];
  $long = $_POST['longitude'];
  $field_id = $_POST['elev_field_id'];
  $units = "FEET";

  // Build Snoopy URL request

  require_once( ABSPATH . WPINC . '/class-snoopy.php' );
  $sno = new Snoopy();
  $sno->agent = 'WordPress/' . $wp_version;
  $sno->read_timeout = 2;
  $reqURL = "http://gisdata.usgs.gov/XMLWebServices/TNM_Elevation_Service.asmx/getElevation?Y_Value=$lat&X_Value=$long&Elevation_Units=$units&Source_Layer=-1&Elevation_Only=1";

  // Send request to elevation server 
  if( !$sno->fetchtext( $reqURL )) {
    die( "alert('Could not connect to lookup host.')" );
  } 

  // Parse response
  if( !preg_match("|<Elevation>([\d.-]+)</Elevation>|",$sno->results)){ 
     die( "alert('Could not find elevation in lookup results.')" );
  } 
 
 $matches=preg_split( "|<Elevation>([\d.-]+)</Elevation>|",$sno->results); //REGEX BUG: but it'll return info

  // Compose JavaScript for return
  die( "document.getElementById('$field_id').value = " .
       $matches[1] );
} // end of myplugin_axax_elev_lookup function

OK!这时我们还需要加入一些细节内容,像是Ajax请求是否来自正确地址的检查和验证等,希望大家能够通过这个例子对WordPress后台上的Ajax用法有基本了解。

访问者页面上的Ajax

在访问者所浏览的WordPress网页上使用Ajax与在WordPress后台使用Ajax略有不同。这是因为WordPress本身不存在内置的访问者页面Ajax,但这并不重要,接下来我们会介绍怎样在访问者页面上使用Ajax。如果要在WordPress后台上使用Ajax,可以忽略这部分内容。

在这个示例中我们假设目前有一个插件,网站/博客访问者可以对某项内容投票或评分。这可以是一个投票插件、日志评分插件或者其它类似插件。我们希望达到的效果是:通过Ajax提交访问者的投票,这样访问者就不需要等待页面刷新;投票被计入结果后,更新显示当前投票总数。为方便起见,我们假设投票和显示结果都是基于文本的,并且假设我们已经编辑了主题或用WordPress过滤器函数或动作函数在WordPress访问者页面适当的位置上添加了HTML代码。所添加的代码内容如下:

<form>
Your vote: <input type="text" name="uservote" />
<input type="button" value="Vote!" onclick="myplugin_cast_vote(this.form.uservote,'voteresults');" />
<div id="voteresults">
(previous results output from your PHP function)
</div>
</form>

接下来我们需要定义Javascript函数 myplugin_cast_vote,而OnClick动作函数则会读取用户输入的信息,用SACK生成一个请求,然后将请求发送给插件进行处理。我们在简介中提到,这个Javascript函数和SACK库都需要被包含在访问者浏览页面的HTML代码的head版块中。我们可以利用wp_head动作钩子函数(当然也可以使用条件标签is_xyz()中的某些函数)将JavaScript函数添加到所有访问者浏览页面中:

add_action('wp_head', 'myplugin_js_header' );

function myplugin_js_header() // this is a PHP function
{
  // use JavaScript SACK library for Ajax
  wp_print_scripts( array( 'sack' ));

  // Define custom JavaScript function
?>
<script type="text/javascript">
//<![CDATA[
function myplugin_cast_vote( vote_field, results_div )
{
    // function body defined below

} // end of JavaScript function myplugin_cast_vote
//]]>
</script>
<?php
} // end of PHP function myplugin_js_header

之后需要填写JavaScript函数myplugin_cast_vote的主体部分,该部分需要能够从表单域中读取投票信息,用SACK生成一个Ajax请求,然后将请求发送给服务器。在这个例子中,我们需要设置以下信息:

  • 请求URL:我们要将请求发送到某个插件PHP文件中,可能是插件主PHP文件,也可能是独立PHP文件。在独立PHP文件中发送请求相对方便。为方便叙述,我们假设这个独立PHP文件名是"myplugin_ajax.php",位于WordPress标准目录wp-content/plugins中。
  • 自定义请求变量:在这个例子中我们只需要发送投票信息和div的ID(返回的结果将进入服务器)。

整合所有信息后,JavaScript函数的主体显示如下:

function myplugin_cast_vote( vote_field, results_div )
{
   var mysack = new sack( 
       "<?php bloginfo( 'wpurl' ); ?>/wp-content/plugins/myplugin_ajax.php" );    

  mysack.execute = 1;
  mysack.method = 'POST';
  mysack.setVar( "vote", vote_field.value );
  mysack.setVar( "results_div_id", results_div );
  mysack.onError = function() { alert('Ajax error in voting' )};
  mysack.runAJAX();

  return true;

} // end of JavaScript function myplugin_cast_vote

接下来是最后一步——对Ajax请求到达服务器时的反应进行定义。这将会在myplugin_ajax.php文件中完成,该文件读取已提交的投票信息,确认投票的有效性,将投票信息存入数据库,统计最新投票结果,最终将统计信息返回浏览器。如果暂时不考虑投票的处理过程,myplugin_ajax.php文件中应包含以下信息:

<?php

// Put your author and license information here
// TO DO!

// Check request came from valid source here
// TO DO!

// read submitted information

$vote = $_POST['vote'];
$results_id = $_POST['results_div_id'];

// Put your vote processing code here
// In this example, assume 
//  a) The processing code sets global
// variable $error to a message if there is an error
//  b) If there is no error, $results contains
// the HTML to put into the results DIV on the screen

if( $error ) {
   die( "alert('$error')" );
} 

// Compose JavaScript for return
die( "document.getElementById('$results_id').innerHTML = '$results'" );

?>

OK!这时我们还需要加入一些细节内容,像是Ajax请求是否来自正确地址的检查和验证等,希望大家能够通过这个例子对WordPress访问者页面上的Ajax用法有基本了解。

延伸阅读

#1
官网上的英文版和这篇文章似乎有些不太一样,尤其是面向用户页面的AJAX部分,官网上的比较新,并且增加了新的hook--wp_ajax_nopriv_(函数名)
#2
感谢您的认真阅读和反馈。 随着WordPress的版本升级,WordPress开发团队会对官网上的内容进行相应更新,而WordPress啦的中文手册是根据当时的CODEX内容进行汉化的(背景为WordPress 2.8)。
#3
Good site,
#4
Perfect!
#5
Hi there!
#6
Sorry but,
#7
Good site,
#8
Hello!