WordPress ウィジェットでAjaxを使う:潜入編

ブラウザーの幅に応じて広告のサイズを切り替えられないか調べていると、wp_is_mobile – WordPress Codex という関数を使用すれば、HTTPヘッダー内のユーザーエージェント情報からデバイスを判定することが出来、デバイス名から表示幅を想定してコンテンツを切り替える方法が見つかりました

だが、しかし、ブラウザの幅は Javascript を使えば取得出来るので、直接ブラウザの幅に応じて切り替えることが出来そうな気がする、Ajaxを使ってテキストウィジェットの表示内容を動的に切り替えることがきっと出来るはず…(^^)

ところが、広告は、スクリプトコードなので、クロスドメインの制約?により、Ajaxを使って後からスクリプトを実行することは難しいようです
ならぬものはならぬのです (^_^;)

まあ、別の手を考えるとして、当初の目的は果たせなかったのですが、スクリプト以外なら ajax を使い、後から内容を変更することが出来ることは、わかりました。

ブラウザー幅に応じた広告サイズ切り替えは、AdSense 側で対応してくれそうな感じです

Webサイトの収益を更に最適化!Google Adsenseのポリシー改訂でレスポンシブデザインでも柔軟に利用可能になった!

前置きはこのぐらいにして、ウィジェットでAjaxを使用する為のサンプルコードとして、作成した is_mobile_text_widget というプログラムを使って、ajax を使うために必要なポイントをかる~く紹介します

ベースとなるウィジェット作成

まず、is_mobile_text_widget というウィジェットを作ります
標準のテキストウィジェットをベースに動作を確認するために少し修正を加えたものです
出力を有効にするウィジェット幅の最小、最大pxを指定できるようにパラメータを追加します

ウィジェットの作り方は、プログラム開発雑記 に参考になるリンクが張ってあります
そこを参考にして頂ければと思います

現時点のコードはこんな感じです

<?php
/*
  Plugin Name: is_mobile Text Widget
  Plugin URI: 
  Description: ajax is_mobile Text Widget.
  Author: enomoto@celtislab
  Version: 0.1.0
  Author URI: https://celtislab.net/
  License: GPLv2
  Text Domain: is_mobile_text_widget
  Domain Path: /languages
 */

//widgets_init にフックさせて、ウイジェットをインスタンス化する
add_action( 'widgets_init', create_function('', 'return register_widget("Is_Mobile_Text");') );

class Is_Mobile_Text extends WP_Widget {

    const   DOMAIN = 'is_mobile_text_widget';    //翻訳用ドメイン定義
    
    //コンストラクタ ウイジェット情報の登録
    function __construct() {
        if (function_exists("load_plugin_textdomain"))
            load_plugin_textdomain(self::DOMAIN, false, basename( dirname( __FILE__ ) ).'/languages' );
        
        $name = 'is_mobile '.__('Text');
        $widget_ops = array('classname' => 'Is_Mobile_Text', 'description' => __('Any text and HTML with Text Widget Width px ', self::DOMAIN));
        $control_ops = array('width' => 400, 'height' => 350);
        parent::__construct( false, $name, $widget_ops, $control_ops );

        //jQuery 使用
        wp_enqueue_script( 'jquery' );
        wp_enqueue_script( 'IMT-ajax-handle', plugin_dir_url( __FILE__ ) . 'is_mobile_getwidth.js', array( 'jquery' ), false, true );
        //Javascript に渡すデータ
        wp_localize_script('IMT-ajax-handle', 'is_mobile_text_ajax', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'widget_id' => "", 'id_nums' =>"" ) );
    }

    //ウイジェットのコンテンツ出力(echo)    
    function widget( $args, $instance ) {
        //連想配列 $args を変数名($before_widget, $before_title, $after_title, $after_widget, ...)に展開
        extract($args);
        //値がない項目に対して引数で指定した初期値を設定
        $default  = array( 'title' => '', 'cmtext' => '', 'filter'=> FALSE, 'hidetitle'=> FALSE, 'smallest' => 0, 'largest' => 9999 );
        $instance = wp_parse_args( (array) $instance,  $default);

        $title = $instance['title'];
        $hide  = $instance['hidetitle'];
        $filter= $instance['filter'];
        $cmtext= $instance['cmtext'];

        echo $before_widget;
        if ( !empty( $title ) && empty( $hide )) {
            echo $before_title . $title . $after_title;
        }
        ?><div class="textwidget"><?php echo !empty( $filter ) ? wpautop( $cmtext ) : $cmtext; ?></div><?php
        echo $after_widget;
?>
<?php
    }

    //アップデート 設定値確認と保存処理
    // OK: Return 新しいインスタンス(インスタンスは自動的に保存更新)
    // NG: Return  false           (インスタンスは保存/更新されません)    
    function update( $new_instance, $old_instance ) {
        $instance = $old_instance;
        $instance['title'] = strip_tags($new_instance['title']);
        $instance['hidetitle'] = isset($new_instance['hidetitle']);

        $instance['smallest'] = (intval($new_instance['smallest']) >= 0 ) ? intval( $new_instance['smallest'] ) : 0;
        $instance['largest']  = (intval($new_instance['largest']) <=  9999 )  ? intval( $new_instance['largest'] ) : 9999;
        if($instance['smallest'] > $instance['largest']){
            $instance['smallest'] = 0;
            $instance['largest']  = 9999;
        }
        if ( current_user_can('unfiltered_html') ){
            $instance['cmtext'] =  $new_instance['cmtext'];
        }
        else {
            $instance['cmtext'] = stripslashes( wp_filter_post_kses( addslashes($new_instance['cmtext']) ) ); // wp_filter_post_kses() expects slashed
        }
        $instance['filter'] = isset($new_instance['filter']);
        
        return $instance;        
    }

    //設定値の入力フォーム    
    function form( $instance ) {
        //値がない項目に対して引数で指定した初期値を設定
        $default  = array( 'title' => '', 'cmtext' => '', 'filter'=> FALSE, 'hidetitle'=> FALSE, 'smallest' => 0, 'largest' => 9999 );
        $instance = wp_parse_args( (array) $instance,  $default);
        
        $ttl    = array('id'  => $this->get_field_id('title'),
                        'name'=> $this->get_field_name('title'),
                        'val' => strip_tags($instance['title']) );
        $swidth  = array('id'  => $this->get_field_id('smallest'), 
                        'name'=> $this->get_field_name('smallest'),
                        'val' => $instance['smallest'] );
        $lwidth  = array('id'  => $this->get_field_id('largest'), 
                        'name'=> $this->get_field_name('largest'),
                        'val' => $instance['largest'] );
        $cmt    = array('id'  => $this->get_field_id('cmtext'),
                        'name'=> $this->get_field_name('cmtext'),
                        'val' => esc_textarea($instance['cmtext']) );
        $hid    = array('id'  => $this->get_field_id('hidetitle'),
                        'name'=> $this->get_field_name('hidetitle'),
                        'val' => $instance['hidetitle'] );
        $flt    = array('id'  => $this->get_field_id('filter'),
                        'name'=> $this->get_field_name('filter'),
                        'val' => $instance['filter'] );
?>
        <p>
            <label for="<?php echo $ttl['id']; ?>"><strong><?php _e('Title:'); ?></strong></label>&nbsp;
            <input id="<?php echo $hid['id']; ?>" name="<?php echo $hid['name']; ?>" type="checkbox" <?php checked(isset($hid['val']) ? $hid['val'] : 0); ?> />&nbsp;
            <label for="<?php echo $hid['id']; ?>"><?php _e('Hide'); ?></label>
            <input class="widefat" id="<?php echo $ttl['id']; ?>" name="<?php echo $ttl['name']; ?>" type="text" value="<?php echo esc_attr($ttl['val']); ?>" />
        </p>
        <p><strong><?php _e('Output enable conditions', self::DOMAIN ); ?></strong></p>
        <div class="is_mobile_text_option">
            <p>
                <?php _e('Text Widget Width (px):', self::DOMAIN); ?>&nbsp;
                <input id="<?php echo $swidth['id']; ?>" name="<?php echo $swidth['name']; ?>" type="text" value="<?php echo $swidth['val']; ?>" size="4" />
                <?php _e(' - ' ); ?>
                <input id="<?php echo $lwidth['id']; ?>" name="<?php echo $lwidth['name']; ?>" type="text" value="<?php echo $lwidth['val']; ?>" size="4" />
            </p>
            <textarea class="widefat" rows="16" cols="20" id="<?php echo $cmt['id']; ?>" name="<?php echo $cmt['name']; ?>"><?php echo $cmt['val']; ?></textarea>
        </div>
        <p>
            <input id="<?php echo $flt['id']; ?>" name="<?php echo $flt['name']; ?>" type="checkbox" <?php checked(isset($flt['val']) ? $flt['val'] : 0); ?> />&nbsp;
            <label for="<?php echo $flt['id']; ?>"><?php _e('Automatically add paragraphs'); ?></label>
        </p>
<?php

    }
}
?>

これをベースに ajax 機能を追加していきます

ブラウザ潜入編

WordPress からブラウザへ JavaScript のスクリプトコードとデータを渡します

WordPressは、ブラウザから要求されたページのHTMLを動的に生成して送信することで、ブラウザーがそのHTMLデータを受信して表示を行うわけですが、そのHTMLデータ内に JavaScript コードを挿入しておくことで、ブラウザ側で JavaScript プログラムを実行することが出来ます

イメージとしては、サーバー国のワードプレス情報局が、JavaScript という諜報員を使い、クライアント国のブラウザ組織に潜入させ様々な活動を行なわせて情報を取得している感じです

よけいに判り難いか (^_^;)

JavaScript側

まず、 JavaScript 諜報員を養成と任務です

  1. ブラウザでページがロードされるまで待機
  2. ロードされたら指示に従い情報収集
  3. 合言葉を付けて情報を報告

これらの任務を遂行する、JavaScript を作成します

WordPress では、簡単に jQUery という秘密兵器を使えるので、これを使ってスクリプトプログラムを書きます

こんな感じの is_mobile_getwidth.js ファイルを作成しました

jQuery(function($){
    //ajax 通信処理
    function post_widgetwidth(width){
        //POSTメソッドで送るデータを定義します var data = {パラメータ名 : 値};

        $.ajax({
            type: 'POST',
            url: is_mobile_text_ajax.ajaxurl,
            data: {
                "action"    : "IMTrequest",
                "imt_width" : width
            },
            dataType: 'json',
            //ajax通信が成功したらPHPから返ってきた jsonデータを処理する
            success: function(response, dataType)
            {
               //json データからキーと値を取得して、指定タグへ出力する
               for ( item in response ){
                   if(item.toString().indexOf("is_mobile_text") >= 0){
                       $(item + " .textwidget").html( response[item] );
                   }
               }
            },
            error: function(XMLHttpRequest, textStatus, errorThrown)
            {
                //エラー処理を記述
                //alert('Error : ' + errorThrown);
            }
        });
        return false;
    };
      
    //ページがロードされたら実行
    $(window).load(function(){
        var wobj = {};
        //var id_nums = is_mobile_text_ajax.id_nums;    //PHP側の wp_localize_script により対象ウィジェット数取得
        var id_nums = is_mobile_text_ajax["id_nums"];    //PHP側の wp_localize_script により対象ウィジェット数取得
        for(var i=0, len=id_nums.length; i<len; i++){
            var idtag = "#" + is_mobile_text_ajax.widget_id + "-" + id_nums[i] +  " .textwidget";
            wobj[ id_nums[i] ] = $( idtag ).width();
        }
        if(i > 0)
            post_widgetwidth( wobj );
    });
});

もう少し説明すると

$(window).load(function()) でページがロードされるまで待機します

次にロードされたら指示に従い情報収集して、合言葉を付けて情報を報告するわけですが、現時点ではまだ、is_mobile_text_ajax.id_num に値をセットしていないのでこれ以上は動作しません

次回に動作するようにして、中身も解説していきます

WordPress側

次に JavaScript を潜入させるための協力者について紹介します

これをウィジェットのコンストラクタ部へ記述します

//jQuery 使用
wp_enqueue_script( 'jquery' );
wp_enqueue_script( 'IMT-ajax-handle', plugin_dir_url( __FILE__ ) . 'is_mobile_getwidth.js', array( 'jquery' ), false, true );
//Javascript に渡すデータ
wp_localize_script('IMT-ajax-handle', 'is_mobile_text_ajax', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'widget_id' => "", 'id_nums' =>"" ) );
    

それぞれどんな役割があるかというと

wp_enqueu_script は、HTMLの <head> タグ内もしくは、指定すればフッターの </body> タグの前に JavaScript ファイルを読み込む為のスクリプトを挿入してくれます

また、同じスクリプトが重複しないように、キュー管理しています

これにより、ブラウザがページを表示するときに指定したJavaScript ファイルを読み込ませるっていう寸法です

ここでは、プラグインと同じ場所にある is_mobile_getwidth.js ファイルを </body> タグの前で読み込ませています

wp_localize_script は、何をするかというと情報があまりなかったのですが、JavaScript 側で使用するグローバル変数を定義できます

WordPress から JavaScript の初期データを設定するということです

どーゆーこと? と思われたでしょうが、動かしてみてようやく理解出来ました

  • ‘is_mobile_text_ajax’ がJavaScript側の変数名となります
  • 連想配列 array( ‘ajaxurl’ => admin_url( ‘admin-ajax.php’ ), ‘widget_id’ => "", ‘id_nums’ =>"" ) が json データに変換され var is_mobile_text_ajax に代入されます

ちなみに Chrome のデベロッパツールを使えばデータを確認出来ます

ここでは、同じ変数名で wp_localize_script を複数回実行した時の様子となっていますが、異なる変数名を指定して wp_localize_script を実行すれば、様々なデータを JavaScript へ渡すことも可能となります

image

参考サイト 5 tips for properly using AJAX in WordPress themes and plugins

JavaScript側では、変数を is_mobile_text_ajax.ajaxurl や is_mobile_text["ajaxurl"] のように連想配列として簡単に使用することができます

なかなか巧妙な仕掛けとなっています (^^)

長くなったので、今回はここまでです

WordPressから wp_enqueu_scriptwp_localize_script を使い、JavaScript を潜入させ、初期値を与える方法を紹介しました

次回は、潜入先のブラウザからWordPressへ ajaxを使い情報を送る方法について解説します


まとめ記事紹介

go-to-top