ムカデノワラジ

ムカデノワラジ

ガジェット・Webサービス・プログラミング・エンタメについて書く雑記ブログ

MENU

html の onclick でCoffeeScriptの関数がis not definedになって動かなかった話

onclick がuncaught reference errorでうまく動かない事案がJavaScriptからCoffeeScriptに変換していた時に起こった。

<div class='foo' id='hoge'  onclick='func()'> </div>

hamlの場合以下

.foo#hoge{:onclick=> 'func()'}

CoffeeScript

func = ->
  処理内容

上のように、html内にonclick=func() を使いクリックイベント時に関数を実行しようとしたところ「uncaught reference error func() is not defined」 となった。

function fanc(){
  // 処理内容
}

のようにJavaScriptを使って定義した場合はonclickで動いたがCoffeeScriptだとどうもうまくいかない。

・考えた原因1: ruby on railsJavaScriptの相性

ruby on rails を使っていたので、rubyjavascript/CoffeeScriptの相性(turbolinkやjqueryの読み込み)が原因かと思ったが違うようだ。
ruby on railsCoffeeScriptを使う場合、CoffeeScript --> javascriptコンパイルされた後、jsファイルが読み込まれる。
普通のjsファイルは動いていたのでCoffeeScriptコンパイル出来ていないわけではなかった。

・考えた原因2: jquery

次に、CoffeeScript内でjqueryが動いていないのかと思って調べてみると、CoffeeScriptjqueryを使うには
処理の前に

$ ->

を入れる必要があるようだ。試してみたがやはりonclickが動かない。

色々調べてみたところ、これはJavaScriptCoffeeScriptの変数スコープや関数スコープが原因だった。

CoffeeScriptが使われる理由の一つに、JavaScriptグローバル変数・関数の問題を改善することがある。
JavaScriptでは変数の頭にvar を付け忘れるとグローバル変数になってしまいグローバル汚染が起こる。
CoffeeScriptはその問題を解決するために、暗黙裡にスクリプトを無名関数で囲い、スコープを作っている。
つまり先の

func = ->
  //処理内容

コンパイルされたとき

function func(){
 //処理内容
}

ではなく

function(){
 function func(){
  }
}

というふうにコンパイルされていると考えられる。
このとき、JavaScript

function func(){
//処理内容
}

としたときfunc()はグローバルな関数であるのに対し、CoffeeScriptで定義したfunc()はグローバルな関数ではない
しかしonclick='func()'はグローバルな関数func()を探すが、その関数はグローバルなスコープに存在しない。そこでfunc()が即時関数式(IIFE : Immediately Invoked Function Expression )と認識されるがこれも未定義のため、uncaught reference error func() is not defined がおこる。

JavaScriptでは(暗黙裡のうちにグローバル変数・関数が作られ)、ローカル変数を定義するには明示的にvar を書くことを必要にしたのに対し、CoffeeScriptでは逆に、グローバル変数や関数を作る場合には明示的に操作する必要がある。
グローバル変数を作る場合には明示的に@を置く。つまり@globaL_variable = ... と書く。

JavaScriptで書いた処理と等価の処理をCoffeeScriptでグローバルな関数として行いたい場合は

window.func = ->
  処理内容

とする。

あるいは、id hogeに対してon 'click' -> を使うことでも対処可能。
よくよく考えるとJavaScriptの時点で、グローバルな関数を使うという基本がなってないガバガバなコードの書き方が問題でもある。。。。

参考記事
stackoverflow.com
参考記事・東京伊勢海老通信