ラベル JavaScript の投稿を表示しています。 すべての投稿を表示
ラベル JavaScript の投稿を表示しています。 すべての投稿を表示

2014年7月9日水曜日

capybaraのpage.execute_scriptとjQueryの組み合わせの汎用性がすごい

Webアプリ開発のテストに便利な Capybara(+Cucumber) ですが、テストに使うdriver がJavascriptに対応していれば、テスト中にJavascriptの実行を含めてブラウザの動きをシミュレートできるという素晴らしい特長があります。Javascript対応のdriverとしては、Capybara-WebkitやPoltergeistなんかが有名です。

ボタンやリンクのクリックなどのユーザの動作で発火するイベントは、Capybaraでclickなどのメソッドで指定すれば、Javascript側のイベントの動作もそのままシミュレートしてくれるようですが、マウスオーバーなどのイベントはどうやったらテストできるのでしょうか。

実は、Capybaraには execute_script メソッドがありまして、これでJavascriptのコード片を直接記述して実行できます。この中ではページで読み込んだライブラリなどが有効です。なのでjQueryが使えます。よってjQueryを使ってイベントを発火させることにより、かなりの汎用性を持ちえます。

例えば、マウスオーバーのステップ定義はこんなふうに書けてしまいます。

もし(/^".*?\((.*?)\)" にマウスポインタを乗せる$/) do |t_css|
  page.execute_script("$('#{t_css}').trigger('mouseenter')")
end

上記ステップ定義に従い、featureファイルにはこんなふうに書けますね。

もし "メニュー1(#menu1)" にマウスポインタを乗せる

カッコ内のCSSセレクタで指定した要素にmouseenterイベントが発生し、マウスオーバーの動作テストを実行することができます。

という感じで、execute_script メソッド内でjQueryを使うと簡単な記述で様々なことが実現出来そうです。特にテスト実行上重要なイベントの発火が非常に簡潔に書けるのでいいですね。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年3月22日土曜日

jQueryでHTMLの内容を入れ替えるいくつかのパターン

ユーザのアクションに応じてHTMLの内容を入れ替えるというのは、jQueryを使うと手軽に実現できますよね。幾つか書いてみようと思います。

1.文字を書き換える


文章だけ変化すればいい場合、text()メソッドで書き換えればよいです。

<html>
  <head>
    <script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
    <script src="./script.js"></script>
  </head>
  <body>
    <div id="container">
      <div id="content1">click me!</div>
    </div>
  </body>
</html>

$(function(){
  $('#content1').click(function(){
    $(this).text('fuga');
  });
});


2.要素を生成・削除して入れ替える


要素自体を別のものに変えたいような場合は、新たに要素を生成して古い要素の後ろに追加し、古い要素を削除します。

<html>
  <head>
    <script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
    <script src="./script2.js"></script>
  </head>
  <body>
    <div id="container">
      <div id="content1">click me!</div>
    </div>
  </body>
</html>

$(function(){
  $('#content1').click(function(){
    var newElem = $('<p>foo</p>')
    $(this).after(newElem).remove();
  });
});


3.要素の表示・非表示を切り替える


大きめの画像などを入れ替える場合、HTMLの内容を書き換えて新しくimg要素を追加すると、画像の表示にタイムラグが発生したりします。そういった場合、あらかじめ表示させる予定の要素を全て並列に用意しておき、初期状態で表示させたいもの以外はCSSでdisplay:"none" を設定しておきます。そしてjQueryのshow()とhide()メソッドで表示・非表示を切り替えます。

<html>
  <head>
    <script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
    <script src="./script3.js"></script>
  </head>
  <body>
    <div id="container">
      <div id="content1"><img src="sample1.png"/>click me!</div>
      <div id="content2" style="display:none"><img src="sample2.png"/>bar</div>
    </div>
  </body>
</html>

$(function(){
  $('#content1').click(function(){
    $(this).hide();
    $('#content2').show();
  });
});


今回挙げたのはあくまで一例にすぎませんが、jQueryだと記述が分かりやすく状況に応じて最適な方法を模索しやすいのがいいですね。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

2014年2月10日月曜日

BloggerでSyntaxHighlighterをAutoloadしつつObjective-Cもハイライト

このブログでも使っているSyntaxHighlighterは、ブログなどでソースコードのハイライトを手軽に実現してくれる便利なスクリプトで、サーバへのスクリプト配置に制限がある環境でも公式サイトのホスティングを使えば比較的少ない手順でソースコードのハイライトを実現できる。

Brushと呼ばれる言語ごとのハイライトのルールを記述したスクリプトと対応CSSを読み込むことで複数言語に対応しているのだが、ブログの記事ごとにSyntaxHighlighterの設定コードを書き分けると面倒だし、かといって共通部分にSyntaxHighlighterの設定コードを埋め込むなら、ブログで使いそうな全ての言語についてBrushを読み込まなくてはならず、表示速度が遅くなってしまう。

そこでAutoloader機能を使えば、必要なBrushと対応CSSだけを自動的に読み込むことで表示速度を遅くせずに済む。これで記事ごとにSyntaxHighlighterの設定コードを書き分けなくても、共通部分に1つコードを埋め込んでおけばいいってことになる。

ただ、公式ページに掲載されているAutoloader対応スクリプトは、カスタムBrushに対応したコードではない。そこで今回公式にはBrushのないObjective-Cのコードを掲載するにあたり、設定コードを書いてみた。

下記のコードをHTML/Javascriptガジェットとしてタイトル無しでサイドバーの一番下に配置し読み込んで使っている。コード中のObjective-CのカスタムBrushはここからダウンロードして少しだけ色をいじったもの。

<!-- Common Scripts -->
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shCore.css" rel="stylesheet" type="text/css" />
<!--<link href="http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css" rel="stylesheet" type="text/css" />-->
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shThemeMidnight.css" rel="stylesheet" type="text/css" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shAutoloader.js" type="text/javascript"></script>

<script type="text/javascript">
////////////////////////////////////
// Autoloader
////////////////////////////////////
function path()
{
  var args = arguments,
      result = []
      ;

  for(var i = 0; i < args.length; i++)
      result.push(args[i].replace('@', 'http://alexgorbatchev.com/pub/sh/current/scripts/'));

  return result
};

SyntaxHighlighter.autoloader.apply(null, path(
  'applescript            @shBrushAppleScript.js',
  'actionscript3 as3      @shBrushAS3.js',
  'bash shell             @shBrushBash.js',
  'coldfusion cf          @shBrushColdFusion.js',
  'cpp c                  @shBrushCpp.js',
  'c# c-sharp csharp      @shBrushCSharp.js',
  'css                    @shBrushCss.js',
  'delphi pascal          @shBrushDelphi.js',
  'diff patch pas         @shBrushDiff.js',
  'erl erlang             @shBrushErlang.js',
  'groovy                 @shBrushGroovy.js',
  'java                   @shBrushJava.js',
  'jfx javafx             @shBrushJavaFX.js',
  'js jscript javascript  @shBrushJScript.js',
  'objc obj-c             @shBrushPlain.js', // dummy
  'perl pl                @shBrushPerl.js',
  'php                    @shBrushPhp.js',
  'text plain             @shBrushPlain.js',
  'py python              @shBrushPython.js',
  'ruby rails ror rb      @shBrushRuby.js',
  'sass scss              @shBrushSass.js',
  'scala                  @shBrushScala.js',
  'sql                    @shBrushSql.js',
  'vb vbnet               @shBrushVb.js',
  'xml xhtml xslt html    @shBrushXml.js'
));

////////////////////////////////////
// Custom Brush Objective-C
////////////////////////////////////
/**
 * SyntaxHighlighter - Objective-C Brush
 * http://codepirate.seaandco.com/
 *
 * @version
 * 1.0.0 (February 22 2009)
 *
 * @author
 * Geoffrey Byers
 *
 * @copyright
 * Copyright (C) 2009 Geoffrey Byers.
 *
 * Licensed under a GNU Lesser General Public License.
 * http://creativecommons.org/licenses/LGPL/2.1/
 *
 *  Updated From:
 *  Code Syntax Highlighter for Objective-C.
 *  Version 0.0.2
 *  Copyright (C) 2006 Shin, YoungJin.
 *  http://scottdensmore.typepad.com/blog/2008/12/objective-c-cocoa-syntax-highlighter.html
 */

SyntaxHighlighter.brushes.ObjC = function()
{
  var datatypes =  'char bool BOOL double float int long short id void';

  var keywords = 'IBAction IBOutlet SEL YES NO readwrite readonly nonatomic nil NULL ';
  keywords += 'super self copy ';
  keywords += 'break case catch class const copy __finally __exception __try ';
  keywords += 'const_cast continue private public protected __declspec ';
  keywords += 'default delete deprecated dllexport dllimport do dynamic_cast ';
  keywords += 'else enum explicit extern if for friend goto inline ';
  keywords += 'mutable naked namespace new noinline noreturn nothrow ';
  keywords += 'register reinterpret_cast return selectany ';
  keywords += 'sizeof static static_cast struct switch template this ';
  keywords += 'thread throw true false try typedef typeid typename union ';
  keywords += 'using uuid virtual volatile whcar_t while';
  // keywords += '@property @selector @interface @end @implementation @synthesize ';


  this.regexList = [
    { regex: SyntaxHighlighter.regexLib.singleLineCComments,  css: 'comments' },    // one line comments
    { regex: SyntaxHighlighter.regexLib.multiLineCComments,   css: 'comments' },    // multiline comments
    { regex: SyntaxHighlighter.regexLib.doubleQuotedString,   css: 'string' },      // double quoted strings
    { regex: SyntaxHighlighter.regexLib.singleQuotedString,   css: 'string' },      // single quoted strings
    { regex: new RegExp('^ *#.*', 'gm'),                      css: 'variable' },    // preprocessor
    { regex: new RegExp(this.getKeywords(datatypes), 'gm'),   css: 'functions' },   // datatypes
    { regex: new RegExp(this.getKeywords(keywords), 'gm'),    css: 'functions' },   // keyword
    { regex: new RegExp('\\bNS\\w+\\b', 'g'),                 css: 'constants' },   // keyword
    { regex: new RegExp('\\bUI\\w+\\b', 'g'),                 css: 'constants' },   // keyword
    { regex: new RegExp('\\bCG\\w+\\b', 'g'),                 css: 'constants' },   // keyword
    { regex: new RegExp('@\\w+\\b', 'g'),                     css: 'functions' },   // keyword
    ];

}

SyntaxHighlighter.brushes.ObjC.prototype = new SyntaxHighlighter.Highlighter();
SyntaxHighlighter.brushes.ObjC.aliases = ['objc', 'obj-c'];

////////////////////////////////////
// Settings and initialize
////////////////////////////////////
SyntaxHighlighter.config.bloggerMode = true;
SyntaxHighlighter.defaults['toolbar'] = false;
//SyntaxHighlighter.defaults['gutter'] = false;
SyntaxHighlighter.all();
</script>

ポイントはAutoloaderの設定部分で、ファイルタイプに対応するBrushスクリプトのURLにダミーのURLを設定すること(SyntaxHighlighter.autoloader.applyの部分)。その設定後に、カスタムBrushのコードを追加して上書きしている。
実際の動作サンプルは、昨日の記事の下の方をご覧頂ければ。

参考サイト:
SyntaxHighlighter 3.0 にアップデート - 電脳スピーチ blog
アプリ開発の記録 SyntaxHighlighterでObjective-Cをきれいに表示

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうそ。