Backbone.js サンプル1

<!DOCTYPE html>
<html lang="ja">



Backbone.js




Backbone.js




(function(){

 var UserModel = Backbone.Model.extend({
     defaults: {
         datetime: new Date().toISOString()
     },
     initialize: function(attrs, options) {
     }
 });

 var UserView = Backbone.View.extend({
     initialize: function(options) {
     },
     render: function() {
         $(this.$el).html(_.template($("#user_template").html(), this.model.attributes));
     }
 });

 var um = new UserModel({name: "TARO", age: 20});
 var uv = new UserView({el: $("#user"), model: um});

 uv.render();
}());

Backbone.js 設置

  • 以下のURLからZIPファイル等のリポジトリをダウンロードする。
  • 展開したフォルダの中の以下のファイルを自分のプロジェクトフォルダにコピーして設置する。
    • backbone.js
    • test/vendor/underscore.js
    • test/vendor/jquery.js

技術情報 / JavaScript / 文法 / 関数 / call()とapply()

call()とapply()

call()

function func(v) {
  alert( v + this.name );
}
func('hello, ');

↑ではthis.nameが参照出来ないが、

var obj  = { name : 'Taro' };
var func = function (v) { alert( v + this.name ); };
func.call(obj, 'Hello, ');

↑のようにcall()を使うと任意のオブジェクト { name : ‘taro’ } に対して関数 func() を実行できる。この際、オブジェクトには何の変更もない。
これを別の方法で実装すると、

var obj  = { name : 'Taro' };
obj.func = function (v) { alert( v + this.name ); };
obj.func('Hello. ');
delete obj.func;

となる。

apply()

apply()は第2引数を配列で取り([‘Hello, ‘,’Mr.’])、適用される関数には引数を並べて渡す(v1,v2)。

var obj  = { name : 'Taro' };
var func = function (v1, v2) { alert( v1 + v2 + this.name ); };
func.apply(obj, [ 'Hello, ', 'Mr.' ]);

これはcall()でも以下のようにして実装出来る。

var obj = { name : 'Taro' };
var func = function (v1, v2) { alert( v1 + v2 + this.name ); };
func.call( obj, 'Hello, ','Mr.' );

引数は省略して、argumentsを利用しても良い。

var obj    = { name : 'Taro' };
var func   = function () { alert( arguments[0] + arguments[1] + this.name ); };
var caller = function () { func.apply( obj, arguments ) };
caller( 'Hello, ','Mr.' );

技術情報 / JavaScript / jQuery / API / CSS

CSS

設定・取得

$('span').css('fontSize', '16px');
var size = $('span').css('fontSize');

まとめて設定

$('span').css({
  font-size: "16px",
  color:     "red"
});

CSSクラスの追加・削除

$('span').addClass('color-red');
$('span').removeClass('font-big');

トグル

<!DOCTYPE html>
<html>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<style type="text/css">
.highlight { background: yellow; }
</style>
<script>
$(function(){
   $("#div").on("click", function(){
     $(this).toggleClass("highlight");
     });
});
</script>
<body>
<div id="div">HELLO</div>
</body>
</html>

技術情報 / JavaScript / DOM / DOM関数

DOM関数

IDから要素を1つ取得 – document.getElementByID(id)

var elem = document.getElementByID("id");

タグ名から要素リストを取得 – elem.getElementsByTagName(name)

var inputs = document.getElementsByTagName("input");
for ( var i = 0, n = inputs.length; i < n; i++ ) {
  var input = inputs[i];
  console.log(input.name);
}
var ul = document.getElementById("ul-id");
var li = ul.getElementsByTagName("li")[0];

要素の属性値を取得・設定

var name = elem.getAttribute("name"));
elem.setAttribute("href", "http://www.yahoo.co.jp/");

直下の子ノードリスト – elem.childNodes

var nodes = document.body.childNodes;
for ( var i = 0, n = nodes.length; i < n; i++ ) {
  console.log(i + ": " + nodes[i]);
}

# <html><body>
# abc
# <div><p>...</p></div>
# xyz
# </body></html>
# 
# 0: [object Text] ( abc )
# 1: [object HTMLDivElement]
# 2: [object Text] ( xyz )

直下の最初の子ノード – elem.firstChild

childNodes[0]と同じ。

要素を作る – document.createElement(name)

var div = document.createElement("div");

テキストノードを作る – document.createTextNod(text)

var text = document.createTextNode("abc");

子ノードの最後にノードを追加する – elem.appendChild(node)

var div = document.createElement("div");
var t1 = document.createTextNode("this ");
var t2 = document.createTextNode("is ");
var t3 = document.createTextNode("a pen.");
div.appendChild(t1);
div.appendChild(t2);
div.appendChild(t3);
document.body.appendChild(div);

子ノードの前にノードを追加する – elem.insertBefore(node)

var li = document.createElement("li");
li.appendChild(document.createTextNode("0"));
var ul = document.getElementsByTagName("ul")[0];
ul.insertBefore(li, ul.firstChild);

ノードをchildNodesから削除する – elem.removeChild(node)

var ul = document.getElementsByTagName("ul")[0];
var li = ul.getElementsByTagName("li")[0];
ul.removeChild(li);

削除されるノードはchildNodesでなければならないので、あるノード内の全てのノードを再帰的に削除する場合、firstChildを使って以下のようにする。

var div = document.getElementsByTagName("div")[0];
while ( div.firstChild ) div.removeChild(div.firstChild);

技術情報 / JavaScript / jQuery / API / セレクター / end()

end()

<!DOCTYPE html>
<html>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script>
$(function(){
    var $li = $('<li><span /></li>');
    $li
    .find('span')
      .html('HELLO')
    .end()
    .appendTo('#ul');
});
</script>
<body>
<ul id="ul">
</ul>
</body>
</html>

説明

以下のようにするとspanがulの直下にappendTo()される(liは消える)。よって、上のようにend()を指定する事でfind()で選択されたspanを解除してからappendTo()する。

    $li
    .find('span')
      .html('HELLO')
    .appendTo('#ul');

技術情報 / JavaScript / Node.js / モジュール / express

express

クエリーパラメータ・フォームデータの参照

(req.params) Checks route params, ex: /user/:id 

(req.query) Checks query string params, ex: ?id=12 Checks urlencoded body params  

(req.body) ex: id=12 To utilize urlencoded request bodies, req.body should be an object. 
           This can be done by using the _express.bodyParser middleware.

参考

技術情報 / JavaScript / jQuery / API / セレクター / find()とfileter()

find()とfileter()

<!DOCTYPE html>
<html>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script>
$(function(){
    var $li_list = $("li");
    $li_list.filter(".foo").each(function(){
      console.log($(this).html());
      });
    console.log("---");
    $li_list.find(".foo").each(function(){
      console.log($(this).html());
      });
    console.log("====");

    $li_list = $("#list");
    $li_list.filter(".foo").each(function(){
      console.log($(this).html());
      });
    console.log("---");
    $li_list.find(".foo").each(function(){
      console.log($(this).html());
      });
});
</script>
<body>
<ul id="list">
  <li class="foo">foo1</li>
  <li class="">2</li>
  <ul>
    <li class="foo">foo2-1</li>
  </ul>
  <li class="foo">foo3</li>
</ul>
</body>
</html>

find()

$("li").find(".foo")

liの子孫要素からfooクラスの要素を探す。

filter()

$("li").filter(".foo")

li要素の内、fooクラスな要素を絞り込む。

技術情報 / JavaScript / OOP / 継承 / hasOwnProperty()でオブジェクトのプロバティを調べる

hasOwnProperty()でオブジェクトのプロパティを調べる

Object.prototype.bar = 1; 
var foo = {goo: undefined};

foo.bar;       // => 1
'bar' in foo; // => true

foo.hasOwnProperty('bar'); // => false
foo.hasOwnProperty('goo'); // => true

hasOwnPropery()はオブジェクトのprototypeチェーンをたどらない。

技術情報 / JavaScript / 文法 / in演算子

in演算子

オブジェクトを定義

var user = {
  name: "Taro",
  age: 10,
  email: "taro@example.com"
};

in演算子によるイテレーション

for ( var item in user ) {
    console.log( "DEBUG: " + item + " => " + user[item] );
}

in演算子による真偽判定

if ("age" in user) {
    console.log("DEBUG: Found!");
}

技術情報 / JavaScript / jQuery / jPlayerでビデオ・オーディオ再生

jPlayerでビデオ・オーディオ再生

参考

JavaScript で音声を扱う方法のまとめ
d.hatena.ne.jp/tily/20100119/js_sound

オーディオ素材

Flashに使えるサウンドエフェクト
www.designwalker.com/2008/01/sound-effect.html
Flash Kit
www.flashkit.com/soundfx/Interfaces/Clicks/

ブラウザ対応状況

HTML5 & CSS3 Support
www.findmebyip.com/litmus#html5-audio-codecs

技術情報 / JavaScript / Node.js / mocha

mocha

--- base.js.20131014    2013-10-14 21:01:29.581749224 +0900
+++ base.js     2013-10-14 21:41:37.483734057 +0900
@@ -40,23 +40,23 @@
  */

 exports.colors = {
-    'pass': 90
+    'pass': 0
   , 'fail': 31
-  , 'bright pass': 92
-  , 'bright fail': 91
-  , 'bright yellow': 93
+  , 'bright pass': 32
+  , 'bright fail': 31
+  , 'bright yellow': 33
   , 'pending': 36
   , 'suite': 0
   , 'error title': 0
   , 'error message': 31
-  , 'error stack': 90
+  , 'error stack': 33
   , 'checkmark': 32
-  , 'fast': 90
+  , 'fast': 32
   , 'medium': 33
   , 'slow': 31
   , 'green': 32
-  , 'light': 90
-  , 'diff gutter': 90
+  , 'light': 33
+  , 'diff gutter': 32
   , 'diff added': 42
   , 'diff removed': 41
 };
@@ -66,9 +66,9 @@
  */

 exports.symbols = {
-  ok: '✓',
-  err: '✖',
-  dot: '․'
+  ok: 'O',
+  err: 'X',
+  dot: '.'
 };

 // With node.js on Windows: use symbols available in terminal default fonts

技術情報 / JavaScript / Node.js / npm

npm

パッケージ一覧

npm list -g

パッケージインストール

npm install -g jshint

パッケージアップデート

パッケージ指定

npm update -g jshint

全パッケージ

npm update -g

設定

設定一覧

npm config ls -l

設定

npm conifg set HOME %APPDATA%

環境変数を使った設定

CMD> setx npm_config_userconfig "%APPDATA%\.npmrc"
Bash> export npm_config_userconfig $HOME/.npm/npmrc
  • 環境変数をnpm_config_foo = barと指定する事で、任意のnpm configの設定が出来る。
  • userconfigファイルの場所はuserconfigファイルには設定出来ないので、環境変数で設定する。

Windowsでのnpmコマンドのアップグレード

参考

npmjs.org/

技術情報 / JavaScript / イベント / onclickで割り当てるハンドラ関数に引数を渡す

onclickで割り当てるハンドラ関数に引数を渡す

引数無しのハンドラ関数割り当て

document.getElementById('foo').onclick = func;

引数有りのハンドラ関数割り当て1

目的の関数を無名関数で包んで割り当てる。

document.getElementById('foo').onclick = function () { func('hello'); };

引数有りのハンドラ関数割り当て2

変数を引数にしたい場合、以下のようにしても、ハンドラ関数が実行される(foo0がonclickされる)際にその変数(n)が評価されるので、nはfoo0が期待するような値として得られない。

for ( var n = 0; n < 3; n++ ) {
 document.getElementById('foo' + n).onclick = function () { func(n); };
}

従って、以下のようにハンドラ関数をさらに無名関数で包んで割り当てる、すなわちクロージャにする。nはイベントに割り当てられた時点で評価され、numはその値を得る事が出来る。

for ( var n = 0; n < 3; n++ ) {
  document.getElementById('foo' + n ).onclick = (function (num) { return func(num) }; })(n);
}

技術情報 / JavaScript / jQuery / 設置 / Prototype.jsと共存させる

jQueryをPrototype.jsと共存させる

<script src="prototype.js"></script>
<script src="jquery.js"></script>
<script type="text/javascript">
$.noConflict();
jQuery(document).ready(function ($) {
});
// もしくは
(function ($) {
})(jQuery);
</script>
  • 最初に$.noConflict()で$をPrototypeに戻す。
  • 次にjQuery.read()あるいは即時実行関数を使って$をjQueryにする。

技術情報 / JavaScript / jQuery / API / セレクター / SELECTタグ

SELECTタグ

値を取得

var value = $('#select1').val();
// または
var value = $('#select1 option:selected').val();

値を設定

$('#select1').val('ABC');

選択されたOPTIONタグを取得

var option = $('#select1 otpion:selected');

全てのOPTIONタグを取得

var opts = [];
$('#select1 option').each(function(){
    opts.push($(this));
});

selectedIndexを取得(設定)する

var num = $('#select1').prop('selectedIndex');
$('#select1').prop('selectedIndex', 0);

attr()よりもprop()を使った方が良い。

参考

www.jquerystudy.info/reference/attributes/prop.html

技術情報 / JavaScript / タイマー処理 / setIntervalをsetTimeoutで書き直す

setIntervalをsetTimeoutで書き直す

setInterval()

var i = 1;
setInterval(function() {
  console.log(i);
  i++;
}, 1000);

setTimeoout()

var i = 1;
function f1() {
  console.log(i);
  i++;
  setTimeout(f1,1000);
};
f1();

解説

  • 前者のコードのsetInterval()の実行する関数の中で1000ミリ秒以上かかる処理が発生すると、setInterval()は処理を重複しつつ関数を実行してしまう。
  • このような問題を避けるためには、後者のコードのようにsetTimeout()を使う。

技術情報 / JavaScript / タイマー処理 / sleepいろいろ

sleep

ビジーウェイト

function sleep(time) {
  var d1 = new Date().getTime();
  var d2 = new Date().getTime();
  while (d2 < d1 + time) {
    d2 = new Date().getTime();
   }
   return;
}
alert("1");
sleep(3000);
alert("2");

setTimeout()

function sleep(time, callback){
  setTimeout(callback, time);
}
alert("1");
sleep(3000, function (){ alert("2"); } );

jQuery

 alert("1");
 $(this).delay(1000).queue(function() {
    alert("2");
   $(this).dequeue();
 });

技術情報 / JavaScript / WYSIWYGエディタ

WYSIWYG エディタ

解説

参考

サンプル

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style type="text/css">
td {
vertical-align: top;
}
</style>
<script type="text/javascript">
( function () {
  var isFirefox = navigator.userAgent.indexOf('Firefox') != -1 ? true : false;
  var isIE = navigator.appName == 'Microsoft Internet Explorer' ? true : false;
  var $ = function (id) { return document.getElementById(id) };
  var $$ = function (tagname, element) { return element.getElementsByTagName(tagname) };
  var viewMode = ''; // 'text' or 'source'
  var updateTextArea = function (form){
    //var form = evt.target;
    if (isFirefox) console.log(form);
    form.text1.value = $('edit_iframe').contentWindow.document.body.innerHTML;

  }
  var viewSource = function () {
    var doc = $('edit_iframe').contentWindow.document;
    if (isIE) {
      var iHTML = doc.body.innerHTML;
      doc.body.innerText = iHTML;
    } else {
      var html = document.createTextNode(doc.body.innerHTML);
      doc.body.innerHTML = "";
      doc.body.appendChild(html);
    }
    viewMode = 'source';
  };
  var viewText = function () { 
    var doc = $('edit_iframe').contentWindow.document;
    if (isIE) {
      var iText = doc.body.innerText;
      doc.body.innerHTML = iText;
    } else {
      var html = doc.body.ownerDocument.createRange();
      html.selectNodeContents(doc.body);
      console.log(html.toString());
      doc.body.innerHTML = html.toString();
    }
    viewMode = 'text';
  };
  var replaceTextAreaWithIframe = function () {
    var iframe = document.createElement('iframe');
    iframe.id = 'edit_iframe';
    iframe.style.width = '500px';
    iframe.style.height = '150px';

    $('edit_ta').style.display = 'none';
    $('edit_div').insertBefore(iframe, $('edit_div').firstChild);

    var value = $('edit_ta').value;
    var doc = $('edit_iframe').contentWindow.document;
    doc.open();
    doc.write(value);
    doc.close();
    doc.body.contentEditable = true;
    doc.designMode = 'on';
  }
  var textFormat = function (target, type, selected){
    if (viewMode == 'source') { 
      alert ("don't do this");  
      return;
    }
    var doc = $(target);
    if (isFirefox) {
      doc.contentWindow.document.execCommand('styleWithCSS', false, false);
    }
    doc.contentWindow.focus();
    doc.contentWindow.document.execCommand(type, false, selected);
  }
  var test1 = function (target) {
    var iframe = $(target);
    var range;
    if (isIE) {
      range = document.selection.createRange(); 
    } else {
      range = iframe.contentWindow.getSelection().getRangeAt(0); 
    }
    if (isIE) {
      var html = '<b>hello</b>';
      range.pasteHTML(html);
    } else {
      iframe.contentWindow.document.execCommand('inserthtml',false,'<b>hello</b>');
    }
  }
  var alert = function () { alert('ALERT'); }
  if (!this['WEdit']) {
    WEdit = {
        isIE:isIE,
        isFirefox:isFirefox,
        updateTextArea:updateTextArea,
        viewSource:viewSource,
        viewText:viewText,
        replaceTextAreaWithIframe:replaceTextAreaWithIframe,
        textFormat:textFormat,
        test1:test1,
        alert:alert
    };
  }
})();
window.onload = function() {
  WEdit.replaceTextAreaWithIframe();
  if (WEdit.isIE) {
    for (var idx=0; idx < document.forms.length; idx++) {
      document.forms[idx].attachEvent('onsubmit', function(event) { WEdit.updateTextArea(event.srcElement); });
    }
  } else {
    for (var idx=0; idx < document.forms.length; idx++) {
      document.forms[idx].addEventListener('submit', function(evt) { WEdit.updateTextArea(evt) }, true);
    }
  }
};
</script>
</head>
<body>
<?php if ($_REQUEST) { 
$req = htmlspecialchars(print_r($_REQUEST,1));
print "<pre>$req</pre>";
}?>
<form id="test_form" action="index.php" method="post">
  <table>
    <tr>
      <td>
        <div id='edit_div'><textarea name="text1" id="edit_ta">test</textarea></div>
      </td>
      <td>
        <ul>
          <li><input type="button" onclick="WEdit.textFormat('edit_iframe','bold',null)" value="太"/></li>
          <li><input type="button" onclick="WEdit.textFormat('edit_iframe','forecolor','#ff0000')" value="赤"/></li>
          <li><input type="button" onclick="WEdit.textFormat('edit_iframe','forecolor','#0000ff')" value="青"/></li>
          <li><input type="button" onclick="WEdit.textFormat('edit_iframe','forecolor','#000000')" value="黒"/></li>
        </ul>
        <ul>
          <li><input type="button" onclick="WEdit.test1('edit_iframe')" value="test1"/></li>
        </ul>
        <ul>
          <li><input type="button" onclick="WEdit.viewSource()" value="ソースモード"/></li>
          <li><input type="button" onclick="WEdit.viewText()" value="テキストモード"/></li>
        </ul>
        <ul>
        <li><input type="submit"/></li>
        <ul>
      </td>
    </tr>
  </table>
</form>
</body>
</html>

技術情報 / JavaScript / XMLHttpRequest

XMLHttpRequest

function getXmlHttp(){
 if (window.ActiveXObject) {
   try {
     return new ActiveXObject("Msxml2.XMLHTTP");
   } catch (e) {
     try {
       return new ActiveXObject("Microsoft.XMLHTTP");
     } catch (e) {
       return null;
     }
   }
 } else if (window.XMLHttpRequest) {
   return new XMLHttpRequest();
 } else {
   return null;
 }
}
function getUserName(user_id) {
 var xmlhttp = getXmlHttp();
 xmlhttp.onreadystatechange = function () {
   if (xmlhttp.readyState == 4) {
     var text = xmlhttp.responseText;
     var user_name = eval(text);
   }
 }
 xmlhttp.open("GET", "index.php?user_id=" + unit_id, true);
 xmlhttp.send();
}

技術情報 / JavaScript / jQuery / API / アトリビュート

アトリビュート

属性の設定・取得

attr()

$('a').attr('href', 'http://www.yahoo.co.jp/'); // 設定
var url = $('a').attr('href');                  // 取得

value属性の設定・取得

$('input').val('ABC');                          // 設定
var value = $('input').val();                   // 取得

要素にテキスト/HTMLを設定

html()

$('span').html('<a href="http://www.yahoo.co.jp/">yahoo</a>');

text()

$('span').text('yahoo');

周囲への挿入

$('span').wrap('<strong></strong>');

技術情報 / JavaScript / メニュー / アニメーションするドロップダウンメニュー

アニメーションするドロップダウンメニュー

概要

  • 黄色のヘッダにマウスを合わせると、その下にグレーのメニューがアニメーションしながらドロップダウンする。
  • ヘッダからマウスを外すと、グレーのメニューはアニメーションしながら元に戻る。

ref(menu1.png)

 ↓

ref(menu2.png)

 ↓

ref(menu3.png)

方針

  • 黄色のヘッダはDTで表す。
  • 対応するDDは、子要素としてグレーのメニューを持っており、メニューはULで表す。
  • ページロード時点でDDの高さを0にして、結果としてメニューはまったく表示されていない状態にする。
  • ヘッダのonmouseoverイベントにハンドラ関数ddmenu()を設定し、slideUp()関数をタイマー設定して、DDの高さを徐々に増やす。
  • DDはCSSでoverflow:hiddenしてあるので、DDの高さの領域だけメニューが表示される。
  • ヘッダのonmouseoutイベントでは、逆にDDの高さを徐々に減らす事で、状態が元に戻る。

HTML&JavaScript

<html>
<head>
<script type="text/javascript">
var SPEED = 15;
var TIMER = 100;

function ddmenu(id, action){
  var header  = document.getElementById(id + '-header');
  var content = document.getElementById(id + '-content');
  clearInterval(content.timer); // メニューの上げ下げをクリアする
  // メニューを下げる
  if (action == 'down') {       // すでにメニューが下がりきっていればreturnする
    if (content.maxh && content.maxh <= content.offsetHeight) {
      return
    } else if (!content.maxh) { // 初回はこっちに分岐してくる
      content.style.display = 'block';
      content.style.height  = 'auto';               // CSSのheightを確定させてから、
      content.maxh          = content.offsetHeight; // DOMのoffsetHeightを取り出して、maxhプロパティにセットし、
      content.style.height  = '0px';                // heightを再度0にする
    }
    content.timer = setInterval( function () { slideDown(content) }, TIMER );
  // メニューを上げる
  } else if (action == 'up') {
    content.timer = setInterval( function () { slideUp(content) }, TIMER );
  }
}
// メニューを下げる
function slideDown(content){
  var currh = content.offsetHeight;                        // 現在の<dd>の高さを取り出し、
  var dist = (Math.round((content.maxh - currh) / SPEED)); // SPEEDを元に何px下げるか決め、
  dist = (dist <= 1) ? 1 : dist;                           // 1px以下の場合は調整した上で、
  content.style.height = currh + dist + 'px';              // <dd>の高さをセットする
  if (currh > (content.maxh - 2)) {                        // 十分下がっていればタイマをクリアして終了する
    clearInterval(content.timer);
  }
}
// メニューを上げる
function slideUp(content){
  var currh = content.offsetHeight;
  var dist = (Math.round(currh / SPEED));
  dist = (dist <= 1) ? 1 : dist;
  content.style.height = currh - dist + 'px';
  if (currh < 2) {
    clearInterval(content.timer);
  }
}
// <dt>からonmouseoutしてメニューを上げている最中に<dd>にonmouseoverしたら、
// メニューを下げ直す
function cancel(id){
  var header  = document.getElementById(id + '-header');
  var content = document.getElementById(id + '-content');
  clearTimeout(header.timer);
  clearInterval(content.timer);
  if (content.offsetHeight < content.maxh) {
    content.timer = setInterval( function () { slideDown(content) }, TIMER);
  }
}

</script>
<style type="text/css">
dt, dd, ul, li {
  margin: 0;
  padding: 0;
  width: 300px;
}
dt {                  /* <dt>にonmouseoverしてメニューが下がり、*/
  background: yellow; /* onmouseoutしてメニューが上がる         */
}
dd {                  /* <dd>がメニューを保持するコンテナ                        */
  display: none;      /* ページを開いた時はdisplay:noneして非表示にし、          */
  height: 0;          /* height:0pxして高さをなくす                              */
  overflow: hidden;   /* JSでheightを動的に増加させるが、overflow:hiddenなので、 */
                      /* <dd>のボックス内の部分しかメニューは表示されない        */
  position: absolute; /* また、positionとz-indexを指定する事で、                 */   
  z-index: 200;       /* <dl>の下にある要素の上にメニューが被さるような表示にする*/
}
ul {                  /* <ul>がメニュー */
}
li {
  background: silver;
  border-bottom: 1px solid black;
}
</style>
</head>
<body>
<dl>
  <dt id="menu-header" onmouseover="ddmenu('menu','down')" onmouseout="ddmenu('menu', 'up')">dropdown</dt>
  <dd id="menu-content" onmouseover="cancel('menu')" onmouseout="ddmenu('menu','up')">
    <ul>
      <li><a href="#">1</a></li>
      <li><a href="#">2</a></li>
      <li><a href="#">3</a></li>
    </ul>
  </dd>
</dl>
</body>
</html>

参考

sandbox.leigeber.com/dropdown/dropdown.html

技術情報 / JavaScript / jQuery / API / イベント

イベント

クリックイベント

設定

$('a').click(function(){
   $(this).css('color', 'red');
});

実行

$('a').click();

マウスオーバー

mouseover() / mouseout()

$('li')
.mouseover(
    function(){
        $(this).css('color', 'red');
    })
.mouseout(
    function(){
        $(this).css('color', 'blue');
    }
 );

hover()

$('li')
.hover(
    function(){
        $(this).css('color', 'red');
    },
    function(){
        $(this).css('color', 'blue');
    }
 );

技術情報 / JavaScript / OOP / 継承 / インスタンスオブジェクトの型

インスタンスオブジェクトの型

継承なし

function Human() {}

var human = new Human();

typeof human           // => "object"
human instanceof Human // => true
human.constructor      // => function Human() {}

var humanB = new human.constructor();

typeof humanB           // => "object"
humanB instanceof Human // => true
humanB.constructor      // => function Human() {}

継承あり

function Animal() {};

function Human() {};
Human.prototype = Animal.prototype;

var human = new Human();

human instanceof Human  // => true
human instanceof Animal // => true
human instanceof Object // => true

技術情報 / JavaScript / OOP / デザインパターン / オブザーバーパターン

オブザーバーパターン

var Listener = function(name, subject){
        function update(evt) {
            evt(this);
        }
        var obj = {
            name: name,
            update: update
        };
        if (subject) {
            subject.register(obj);
        }
        return obj;
};
var Subject = function(){
    var listeners = [];
    function notify(evt) {
        for (var i = 0; i < listeners.length; i++) {
            listeners[i].update(evt);
        }
    }
    function register(listener) {
        listeners.push(listener);
    }
    function unregister(listener) {
        var buf = [];
        for (var i = 0; i < listeners.length; i++) {
            if (listener != listeners[i]) {
                buf.push(listeners[i]);
            }
        }
        listeners = buf;
    }
    return {
        listeners:  listeners,
        notify:     notify,
        register:   register,
        unregister: unregister
    };
};
var s = Subject();
l1 = Listener('LISTENER1', s);
l2 = Listener('LISTENER2', s);
l3 = Listener('LISTENER3');
s.notify(function(obj){ console.log('hello, ' + obj.name)});
console.log('---');
s.register(l3);
s.notify(function(obj){ console.log('hello, ' + obj.name)});
console.log('---');
s.unregister(l2);
s.notify(function(obj){ console.log('hello, ' + obj.name)});
hello, LISTENER1
hello, LISTENER2
---
hello, LISTENER1
hello, LISTENER2
hello, LISTENER3
---
hello, LISTENER1
hello, LISTENER3

補足

Subjectがnotify()でListner群をupdate()する際にsetTimeout()でくるんであげると、update()処理が重くてもメインスレッドに影響を与えないので、あるケースでは便利。

技術情報 / JavaScript / OOP / オブジェクトの拡張を局所化したブロック内で行う

オブジェクトの拡張を局所化したブロック内で行う

オブジェクトを即時関数の引数に渡して、関数ブロック内で拡張を行う。

var obj = {};
(function (o) {
    var num = 123;
    o.num = num;
})(obj);
console.log(obj.num); // 123

ちなみに、

var num = 0;
(function (n) {
     n = 123;
})(num);
console.log(num); // 0

は、期待通りにならない。↓もダメ。

var obj = {};
(function (o) {
   var num = 123;
   o = { num:num };
})(obj);
console.log(obj.num); // undefined

技術情報 / JavaScript / OOP / クラスの作り方 / オブジェクトリテラルを使ったクラス作成 – 基本

オブジェクトリテラルを使ったクラス作成 – 基本

オブジェクトリテラルでクラスを定義・インスタンスを生成

var animal = {
   name : "Taro",
   say  : function () { alert("My name is " + this.name) }
};

インスタンスの利用

animal.say();
console.log(typeof animal); // => "object"

オブジェクトリテラルではなくObjectを使って

↑では{} を使ってオブジェクトリテラルで簡易に書いているが、↓以下のように書くのと同じ。

var animal = new Object();
animal.name = "Taro";
animal.say  = function () { alert("My name is " + this.name) };

技術情報 / JavaScript / OOP / クラスの作り方 / オブジェクトリテラルを使ったクラス作成 – 応用

オブジェクトリテラルを使ったクラス作成 – 応用

クラスの作成

var Animal = function (name, age) {
  var that = {};

  that.name    = name;
  that.age     = age;
  that.sayName = function () {
      alert("My name is " + this.name);
  }

  return that;
};

Animal()はコンストラクタであり、初期化処理後、オブジェクトリテラル(that)を返す。

インスタンスを作成・メソッドを利用

var a = Animal("Taro", 10);
a.sayName();

オブジェクトリテラルを使ったクラスのメリット

クラス内の変数・関数を隠蔽出来る。この例の場合、thatにageをセットしなければ、ageはプライベート変数になる。

継承

子クラスの作成

var Dog = function (name, age) {
   var that = Animal(name, age);

   that.sayAge = function () {
       alert("My age is " + this.age);
   }

   return that;
}

thatを(空のオブジェクトリテラルではなく)Animal()で初期化して、Animalの継承を実現している。

子クラスのインスタンスを作成・利用

var d = Dog("Jiro", 5);
d.sayName();
d.sayAge()
  • sayName()は親クラスで定義、sayAge()は子クラスで定義されている。

技術情報 / JavaScript / 文法 / / キャスト

キャスト

Number

var num = Number("12.3");   // 12.3
var int = parseInt("12.3"); // 12

String

'' + 10 == '10'

Boolean

!!'foo'; // => true
!!'';    // => false
!!'0';   // => true
!!'1';   // => true
!!'-1'   // => true
!!{};    // => true
!!true;  // => true

参考

技術情報 / JavaScript / OOP / クラスをパッケージ(名前空間)にまとめる

クラスをパッケージ(名前空間)にまとめる

目的

BarClassとBazClassという2つのクラスをFooPackageにまとめる。

ファイル構造

  • index.html
  • FooPackage.BarClass.js
  • FooPackage.BazClass.js

ソースコード

FooPackage.BarClass.js

var FooPackage = FooPackage || {};
(function(FooPackage) {
    var BarClass = (function() {

        function BarClass(mesg) {
            this.mesg = mesg;
        }

        BarClass.prototype.getMesg = function() {
            return this.mesg;
        };

        BarClass.prototype.getBazMesg = function() {
            var baz = new FooPackage.BazClass("DEBUG: Foo::Baz From Bar");
            return baz.getMesg();
        };

        return BarClass;
    })();

    FooPackage.BarClass = BarClass;
})(FooPackage);

FooPackage.BazClass.js

var FooPackage = FooPackage || {};
(function(FooPackage) {
    var BazClass = (function() {

        function BazClass(mesg) {
            this.mesg = mesg;
        }

        BazClass.prototype.getMesg = function() {
            return this.mesg;
        };

        return BazClass;
    })();

    FooPackage.BazClass = BazClass;
})(FooPackage);

index.html

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <script src="FooPackage.BarClass.js"></script>
    <script src="FooPackage.BazClass.js"></script>
    <script>
      var bar = new FooPackage.BarClass("DEBUG: Foo::Bar");
      var baz = new FooPackage.BazClass("DEBUG: Foo::Baz");
      console.log(bar.getMesg());
      console.log(baz.getMesg());
      console.log(bar.getBazMesg());
    </script>
  </head>
  <body>
    <h1>JS</h1>
  </body>
</html>

index.htmlの実行結果

DEBUG: Foo::Bar
DEBUG: Foo::Baz
DEBUG: Foo::Baz From Bar

サブパッケージを作成する

BarClassをサブパッケージにまとめる場合は、上のコードを以下のように変える。

FooPackage.SubPackage = FooPackage.SubPackage || {};
FooPackage.SubPackage.BarClass = BarClass;

技術情報 / JavaScript / コストのかかる関数の結果をメモ化する

コストのかかる関数の結果をメモ化する

function factorial(n) {
    if (!factroial.cahche) {
        factroial.cache = {
            "0" : 1,
            "1" : 1
        };
    }

    if (!factorial.cache.hasOwnProperty(n)) {
        factorial.cache[n] = n * factorial(n - 1);
    }

    return factorial.cache[n];
}
function memoizer(fundamental, cache) {
    cache = cache || {};
    var shell = function (arg) {
        if (!cache.hasOwnProperty(arg)) {
            cache[arg] = fundamental(arg);
        }
        return cache[arg];
    }
    return shell;
}

var memoized = memorizer(original, { "arg" : "value" });

出典

技術情報 / JavaScript / タイマー処理 / コールバック関数の引数の扱い

コールバック関数の引数の扱い

タイマーで呼ばれるコールバック関数が引数を受け取る場合、無名関数でラップする

function foo(a,b) { console.log(a + "," + b); }

setTimeout("foo(1,2)", 500);            
setTimeout(function(){ foo(1,2) }, 500);

前者のように”foo(1,2)”のような文字列を渡してeval()する事も出来るが、後者のように無名関数を使う方がベタープラクティス。

コールバック関数へ引数を渡す

function foo(n) { console.log("DEBUG: " + n); }

setTimeout("foo(100)", 0);
var num = 200;
setTimeout("foo(" + num + ")", 0); 

↑のように引数を含めて文字列で渡してevalする事も出来なくはないが、↓のように無名関数をでラップした方が良い。

コールバック関数へ引数を渡す方法

function foo(n) { console.log("DEBUG: " + n); }
var num = 100;
setTimeout(function () { foo(num) }, 0);

あるいは、

var num = 100;
setTimeout((function(n){
  return function(){ console.log(n); };
})(num), 0);

即時関数への引数で渡し、それをコールバック関数内で参照する。

引数の内容が確定するタイミング

↑の方法では、

function foo(n) { console.log("DEBUG: " + n); }
var num = 100;
setTimeout(function () { foo(num) }, 0);
num = 200;
DEBUG: 200

setTimeout()実行後にnumを100から200へ変えると、コールバック関数実行時に渡される引数は200を参照する。100を参照するようにするには↓のようにする。

function foo(n) { console.log("DEBUG: " + n); }
var num = 100;
setTimeout((function(n){
  return foo(n);
})(num), 0);
num = 200;
DEBUG: 100

詳しくやると↓。

var num = 100;
(function () {
  console.log("INSIDE1: " + num);
  setTimeout((function(n){
    return function(){
    console.log("CALLBACK: " + n);
    console.log("CALLBACK: " + this.num);
  };})(num), 0);
  num = 200;
  console.log("INSIDE2: " + num);
})()
console.log("OUTSIDE: " + num);
INSIDE1:  100
INSIDE2:  200
OUTSIDE:  200
CALLBACK: 100
CALLBACK: 200

コールバック関数に渡される引数nの値はsetTimeout()実行時に決まる。グローバルのnumは100から200へ変わった後の値を参照している。

技術情報 / JavaScript / タイマー処理 / コールバック関数はグローバルスコープで実行される

コールバック関数はグローバルスコープで実行される

var Foo = function () {
  this.num = 1;
  this.alertNum      = function () { alert(this.num); }
  this.alertNumLater = function () { setTimeout(this.alertNum, 1000); }
};

var num = 2;
var foo = new Foo;
foo.alertNum();       // 1
foo.alertNumLater();  // 2

showNumLater()から呼ばれるコールバック関数 showNum()、すなわち function () { alert(this.num); } はグローバルスコープで実行される。よって、this.numはグローバルオブジェクトであり、this.numはFoo.numではない。

function foo() { console.log("1"); }
function bar() { 
  function foo() { console.log("2"); }; 
  setTimeout("foo()", 1000);
} 
bar(); // 1

↑の例ではグローバルのfoo()が呼ばれる。

技術情報 / JavaScript / Backbone.js / サンプル1

Backbone.js サンプル1

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <title>Backbone.js</title>
    <script src="js/jquery.js"></script>
    <script src="js/underscore.js"></script>
    <script src="js/backbone.js"></script>
  </head>
  <body>
    <h1>Backbone.js</h1>
    <div id="user"></div>
    <script type="text/template" id="user_template">
      <dl>
        <dt>name</dt><dd><%= name %></dd>
        <dt>age</dt><dd><%= age %></dd>
        <dt>datetime</dt><dd><%= datetime %></dd>
      </dl>
    </script>
    <script src="app.js"></script>
  </body>
</html>
(function(){

    var UserModel = Backbone.Model.extend({
        defaults: {
            datetime: new Date().toISOString()
        },
        initialize: function(attrs, options) {
        }
    });

    var UserView = Backbone.View.extend({
        initialize: function(options) {
        },
        render: function() {
            $(this.$el).html(_.template($("#user_template").html(), this.model.attributes));
        }
    });

    var um = new UserModel({name: "TARO", age: 20});
    var uv = new UserView({el: $("#user"), model: um});

    uv.render();
}());

技術情報 / JavaScript / OOP / デザインパターン / シングルトンパターン(アクセス権付きで)

シングルトンパターン(アクセス権付きで)

オブジェクトを定義する

var Singleton = (function(){
  var instance;

  function init() {
    var foo     = 'FOO';
    var bar    = 'BAR';
    var getFoo  = function () { return this.foo; };
    var getBar  = function () { return bar; };
    var _getBaz = function () { return 'BAZ' };
    return {
      foo:    foo,
      getFoo: getFoo,
      getBar: getBar,
      getBaz: _getBaz
    };
  }
  return {
    getInstance: function () {
      if (!instance) {
        instance = init();
      }
      return instance;
    }
  };
})();

オブジェクトを利用する

var obj = Singleton.getInstance();
console.log('DEBUG: ' + obj.getFoo());   // getFoo()は公開メソッド。
console.log('DEBUG: ' + obj.getBar());   // getBar()も公開メソッド。
obj.foo = 'FOO2';                        // fooはパブリックプロパティなので、
console.log('DEBUG: ' + obj.getFoo());   // "FOO2"に変わってる。
obj.bar = 'BAR2';                        // barはプライベートプロパティなので、
console.log('DEBUG: ' + obj.getBar());   // "BAR"のまま。
console.log('DEBUG: ' + obj.getBaz());   // getBaz()は内部でプライベートメソッド_getBaz()を呼んでいる。

var obj2 = Singleton.getInstance();      // シングルトンなので、
console.log('DEBUG: ' + obj2.getFoo());  // obj2のfooプロパティは"FOO2"になっている。
obj2.foo = "FOO3";                       // obj2のfooを変えると、
console.log('DEBUG: ' + obj.getFoo());   // objのfooは"FOO3"になっている。
DEBUG: FOO
DEBUG: BAR
DEBUG: FOO2
DEBUG: BAR
DEBUG: BAZ
DEBUG: FOO2
DEBUG: FOO3

参考

www.addyosmani.com/resources/essentialjsdesignpatterns/book/

技術情報 / JavaScript / jQuery / API / セレクター

セレクタ

複数の要素

var h_tags = $('h1, h2, h3');

親子関係にある要素

var lis = $('ul>li');

子孫関係ににある要素

var inputs = $('form input);

リストになっている要素

var first_li = $('li:first');           // リストの最初の要素
var last_li  = $('li:last');            // リストの最後の要素
var even_lis = $('li:even');            // リストの偶数番目の要素
var odd_lis  = $('li:odd');             // リストの奇数番目の要素
var third_li = $('li:eq(2));            // 添え字で指定する要素(添え字は0スタート)
var lis      = $('li:contains("ABC")'); // 指定する文字列を持っている要素

親要素

var elems = $('div').parent(); // divの子要素も含む

子要素

var elems = $('div').children();

兄弟要素

var lis = $('li').siblings()

前の要素

var first_li = $('li:eq(1)').prev();

次の要素

var third_li = $('li:eq(1)').next();

ある要素の中だけを対象にする

var div = $('#div1');
var elements = $('.foo', div);
もしくは
var elements = div.find('.foo');

カスタム疑似セレクター

var arialDivs = [];
$("div").each(function(){
  var div = $(this);
  if (div.css("font-family") === "Arial") {
      arialDivs.push(div);
  }
});

$.extend($.expr[":"], {
    hasArialFont: function (element) {
        return $(element).css("font-family") === "Arial";
    }
});

$("div:hasArialFont").click(function(){
    alert("Element has Arial font");
});

参考

技術情報 / JavaScript / タイマー処理 / タイマーを使ってコストのかかる関数を遅延実行させる

タイマーを使ってコストのかかる関数を遅延実行させる

function swap(items, firstIndex, secondIndex) {
    var temp = items[firstIndex];
    items[firstIndex] = items[secondIndex];
    items[secondIndex] = temp;
}

function bubbleSort(array, onComplete) {
    var pos = 0;
    (function () {
        var j;
        for (j = array.length; j > pos; j--) {
            if (array[j] < array[j - 1]) {
                swap (array, j, j - 1);
            }
        }
        pos++;
        if (pos < array.length) {
            setTimeout(arguments.callee, 500);
        } else {
            onComplete(array);
        }
    })();
}
var array = [3,1,4,2];
bubbleSort(array, function (v) { console.log(v)} );
function chunk(array, process, context) {
    var items = array.concat();
    setTimeout(function () {
        var item = items.shift();
        process.call(context, item);
        if (items.length > 0) {
            setTimeout(arguments.callee, 100);
        }
    }, 100);
}

var obj = { items : [] };
var items = [ 1,2,3 ];
chunk(items, function (value) {
    this.items.push( value * 10);
}, obj);
console.log(obj.items);

出典

技術情報 / JavaScript / 選択範囲 / テキストを選択状態にする

テキストを選択状態にする

<html>
<body>
<span>abcdefg</span>
<script type="text/javascript">
var span = document.getElementsByTagName('span')[0]; 

if (window.getSelection) {
  var range = document.createRange();
  range.setStart(span.firstChild,2);
  range.setEnd(span.firstChild,5);
  var sel = getSelection();
  sel.removeAllRanges();
  sel.addRange(range);
} else {
  var range = document.selection.createRange();
  range.moveToElementText(span);
  range.collapse();
  range.moveStart("character", 2);
  range.moveEnd("character", 3);
  range.select();
}
</script>
</body>
</html>
  • IE8/Fx3.5で確認。
  • Fxで、range.setStart()/setEnd()の代りにrange.selectNodeContents()を使っても良い。その場合、SPANタグ内のテキスト全部が選択される。

IEでmoveToPoint()を使う場合

var range = document.body.createTextRange();
range.moveToPoint(window.event.x, window.event.y);
range.expand("WORD");
range.select();

目的の要素に、例えばabcdefgのようにイベントにファンクションを登録し、上のコードを実行出来るようにする。すると、abcdefgが選択状態になる。

参考