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

2013年4月16日火曜日

[JavaScript]Pinterestを参考にブックマークレットを作る

ここ数年流行しているPinterest等のキュレーションサイトは、簡単にコンテンツをクリップするために、ブックマークレットを提供していたりするんだけど、自分もPinterestのを参考に作ってみたので、発見したことを書きます。

※最近ではchrome拡張機能とかも提供しているみたいだね。

javascript:void((function(d){
    var e=d.createElement('script');
    e.setAttribute('type','text/javascript');
    e.setAttribute('charset','UTF-8');
    e.setAttribute('src','//assets.pinterest.com/js/pinmarklet.js?r='+Math.random()*99999999);
    d.body.appendChild(e)
})(document));

これが、pinterestのブックマークレットです。(見やすいように改行は入れました。)
ここで入手できます。

これを見ると、開いているサイトのHTMLに、無理矢理自サーバに配置したjavascriptを呼び出すscriptタグを挿入しています。

その呼び出したjavascriptは非常に長いので説明を省略しますが、クリップする画像を選ばせるようなプロンプトを出させたり等、自サイトのjavascriptを書くかのごとく書いています。

ただし、jquery等のライブラリをscriptタグ挿入で呼び出しても、変数、関数の共有ができないので使えない、とのこと。(どこかのサイトに書いてあったのだが、どこかわからなくなってしまった)
なので、生のjavascriptを書く必要があります。

CSSファイルはlinkタグ挿入により、反映することができます。なので、自分のスタイルを反映することができます。

また、このjavascriptから、window.open()等で自サイトのページを開く場合、ポップアップブロックが発生する、という問題がおこります。

この問題への対処はhtmlにonclick属性を使い、自サイトを開くということです。
このページを参照してください。ポップアップブロック回避策

なので、プロンプトはjavascriptのalertやconfirmを使うのではなく、htmlでモーダルっぽいものを作り、そのOKボタンのタグにonclick属性をつけることになります。

一連の流れは以下のようになります。(自分の場合)

1.ブックマークレット起動。→自サーバのjavascript(A.jsとする。)が呼び出される。
2.A.jsにて、自サーバのcssを呼び出すlinkタグを挿入。
3.A.jsにて、モーダルっぽいHTMLを挿入。自サーバを開くボタン(Bボタン)にはonclick属性でwindow.openを紐づけてある。
4.Bボタンを押すと、自サーバの画面が開く。そのリクエストにはそのサイトから抽出したい情報をパラメータとしてつけておく。

ここで一つ注意点、以下のポストでも書いたように、自サーバ側の設定によっては、違うドメインからの遷移により、セッションが切れるようにしている場合がある。(CakePHPではデフォルトで切る。)そうすると、ログインしていたのに、別画面で自サイトを開いたら、再度ログインしなければならなくなってしまう。なので、チェックをはずす設定をする必要があります。
[CakePHP]他のサイトを経由するとセッションが切れてしまう件への対処

[CakePHP]JavaScriptをコントローラーに吐かせる。

ブックマークレットから呼び出されるjavascriptって自サーバのURLをどうしても絶対パスで書かないといけないと思うんだけど、開発環境と本番環境で書き換えなきゃいけなかったりで、面倒臭いので、CakePHPのコントローラーからjavascriptを吐かせるようにした。

[CakePHP] スタイルシートをコントローラーで作ってみる

基本的には上記のサイトを参考にさせて頂いた。

CSSでもJSでもだいたい同じ。

ただ、header('Content-Type: text/css');ってところを当然header('Content-Type: text/javascript');にしなければいけません。

あと、データベースを使用しないモデルになるのでモデル内に

var $useTable = false;

も必要です。これが上記のサイトにのってないのはなぜかな。

2013年4月5日金曜日

[jQuery]infinitescrollが正しくpage番号をincrementしてくれない件への対処

infinitescrollを使用している際、以下のような事象が発生した。

http://【mydomain】/themes/2/page:2がinfinitescrollのオプションnextSelectorに指定されている時、なぜか出力されないアイテムが表示されたり、アイテムがもっとたくさんあるはずなのに、「もうアイテムはありません」というようなメッセージが出てしまうのだ。 i

まず、サーバ側のaccessログをみると、 http://【mydomain】/themes/3/page:2というURLにアクセスしており、404エラーを返していた。なぜ、このようなURLにアクセスしてしまうのか。
infinitescrollのソースをchoromeのデバッグモードで見ていると、以下のことがわかった。
pathという2要素の配列があるのだが、 0番目に'http://【mydomain】/themes/'、1番目に'/page:2'という文字列が入っており、処理としては、このそれぞれの文字列の間にインクリメントされた数字を入れて結合しアクセス先のURLを作っているようだった。つまり、本来は 'http://【mydomain】/themes/2/page:【ページ番号】'と認識してほしいところ、'http://【mydomain】/themes/【ページ番号】/page:2'というように認識してしまったようだ。

infinitescrollのソースをよくみると、URLに/2/というようにスラッシュに直接はさまれた’2’があるとそれをページ番号として認識する作りになっていた。/suzuki2/や/2suzuki/など、他の文字列と一緒であれば、問題なかった。

これを防止するため、最初はinfinitescroll.jsに手を加えようと考えたが、pathはオプションで指定できるということがわかった。なので、以下のようにして指定した。
色々書いているが言いたいことは色が付いている行。

    path=new Array();
    path.push(location.href+'/page:');
    path.push("");
    $("#infiniteItems").infinitescroll({
            navSelector  : '#page-nav',    // selector for the paged navigation
            nextSelector : '#page-nav a',  // selector for the NEXT link (to page 2)
            itemSelector : '.rankedItem',     // selector for all items you'll retrieve
            loading: {
                finishedMsg: 'これ以上はないのじゃ',
                img: $("body").attr("data-ladingImg")
            },
            path:path
        },
        function(newElements){
            var $newElems = $( newElements ).css({ opacity: 0 });
                $newElems.each(function(i){
                    $itemInfo=$(this).children(".itemInfo");
                    $voters=$(this).children(".voters");
                    if($itemInfo.outerHeight()>$voters.outerHeight()){
                        mgnpdgSum=$voters.outerHeight()-$voters.innerHeight();
                        $voters.height($itemInfo.height()-20);
                    }
                });
            $newElems.css({ opacity: 1 });
        }
    );


2013年3月19日火曜日

[jQuery]resizeイベントが2回発動される問題への対策

$(window).resize(function)なんですが、なぜかchromeだと1度のリサイズでイベントが二回発動されていしまいます。firefoxだとおきませんでした。

ここにのっている対処が一番シンプルそうです。実際うまくいきました。

JQuery: How to call RESIZE event only once it's FINISHED resizing?

コードはここにあるのを、発動される関数を自分のもに変えればいいと思います。

http://jsfiddle.net/Zevan/c9UE5/5/

2013年2月28日木曜日

[JavaScript]JSのlive関数のように動的に取得した要素にstyleを反映させるには

AJAXなどで動的に取得したHTML要素にstyleを反映させるにはどうするか。
最初からcssファイルに記述しておけばいいんだけど、値自体を動的に決めたい場合はどうするか。例えば、windowサイズに合わせて何かのサイズを決めたい時とか。

そんなときはhead内に動的にstyle要素を追加すればよいということがわかった。。
このサイトが参考になります。

jQueryのlive関数のように、後から追加されたエレメントに対して事前にスタイルを設定する方法

2013年2月9日土曜日

[JavaScript]画面ロード時にレイアウトがガタッっと一瞬崩れないようにするため

レイアウトに影響を与えるJavaScriptが外部ファイル化されていると、読み込み完了まで時間がかかるので、一瞬ガタっとレイアウトが崩れるように見えることがある。
今回の場合、それは、例えば以下のようなonload時に実行されるレイアウトに影響を与えるコマンドが、外部ファイルにあるためだった。

if($(window).width()>=980){
     $("#itemList").css("padding-top",$(".hero-unit").height()+50+"px");//hero-unitの大きさに合わせてpadding-topを変更
 }
なので、こちらをHTMLファイル内のbodyの閉じタグ直前の方に移した。
すると、ほとんど気にならないほど画面表示が速くなった。

2012年12月20日木曜日

[AJAX]ajaxで取得したレスポンス内のjavascriptはデバッグできないので

Ajaxで取得したHtml等の中に書かれているjavascriptはFirebugなどで
デバッグすることができない。

なので、なるべくソースはもともとあるjavascript(Ajaxではなく最初にそのページを
表示した際に読み込まれるjavascript)内に関数として記述しておき、Ajaxで取得した
javascriptからはその関数を呼び出すだけ、という形にしておけば、デバッグが可能だ。

[JavaScript]htmlのタグIDと同じ名前の変数を作ってはいけない

下記で説明されているとおりとなります。

IE8で「オブジェクトでサポートされていないプロパティまたはメソッドです」と表示される

変数宣言する際にvarをつければエラーは発生しないのだが、
タグIDの方が無効になってしまうので、やはり違う名前をつけるべき。

[JavaScript]普通のfor文とfor-in文の微妙な違い

JavaScriptで配列やオブジェクトを扱う際に、普通のfor文とfor-in文の微妙な違いについて
紹介する。

■一次元配列の場合。
・普通のfor文

定義有り→エラーが発生しない。
list=new Array();
for(i=0;i<list.length;i++){
    alert(list[i]);
}

定義なし→エラーが発生する。
エラー: 'list' は宣言されていません。
for(i=0;i<list.length;i++){
    alert(list[i]);
}

・for-in文

定義あり→エラーが発生しない。
list=new Array();
for(var item in list){
    alert(list[item]);
}

定義なし→エラーが発生する。
エラー: 'list' は宣言されていません。
for(var item in list){
    alert(list[item]);
} 

つまり、上記のように一次元配列においての挙動は同じである。

■多次元配列の場合
・普通のfor文

二次元目までの定義あり→エラーが発生しない。
list=new Array();
list["a"]=new Array();
for(i=0;i<list["a"].length;i++){
    alert(list["a"][i]);
} 

二次元目までの定義なし(一次元目のみ定義)→エラーが発生する。
エラー: 'list.a.length' は Null またはオブジェクトではありません。 
list=new Array();
for(i=0;i<list["a"].length;i++){
    alert(list["a"][i]);
} 

・for-in文

二次元目までの定義あり→エラーが発生しない。
list=new Array();
list["a"]=new Array();
for(var item in list["a"]){
    alert(list["a"][item]);
}

二次元目までの定義なし(一次元目のみ定義)→エラーが発生しない。
list=new Array();
for(var item in list["a"]){
    alert(list["a"][item]);
}

つまり、for-in文で多次元配列を扱う場合は、2次元目の配列を定義していなくても
なぜかエラーが発生しない。

ちなみ三次元にしたところ、

以下のように一次元のみ定義だとエラーが発生。
エラー: 'list.a.b' は Null またはオブジェクトではありません。
list=new Array();
for(var item in list["a"]["b"]){
    alert(list[item]["a"]["b"][item]);
} 

二次元まで定義したらエラーが発生しなくなる。
list=new Array();
list["a"]=new Array();
for(var item in list["a"]["b"]){
    alert(list[item]["a"]["b"]);
}

■結論
①普通のfor文でx次元配列を扱う場合、x次元まで定義する必要がある。
②for-in文でx次元配列を扱う場合は、x-1次元まで定義すればよい。

※①はlengthを参照するのでx次元目まで定義していないとエラーが発生するのはかるが、
  ②でx次元目まで定義していないのにエラーが発生しない理由がわからない。


2012年12月6日木曜日

[jQuery]「jQuery Alert Dialogs」での$(this)の参照が変わる?

検証はしていないので、原因が違ったらすみません。
以下の「jQuery Alert Dialogs」を使っています。

http://h2ham.seesaa.net/article/117267913.html

jAlertやjConfirmの後に$(this)を使うと、想定していたオブジェクトを
指していないようです。
firebugで見たところ、「Window」オブジェクトになっていました。

jAlertやjConfirmの処理の中で$(this)が変わる処理が入っているのでは
ないか、と推測します。

なので、仕方なしに、$(this)で取得したい値をjAlertやjConfirmを使う前に
変数に代入しておくことにします。


2012年11月22日木曜日

[jQuery]コロンを含むセレクタを指定する方法

以下のサイトで説明されているよう、コロンの前に二つのバックスラッシュを
つけてエスケープしなければならない。
なぜ二つかはわからない。

JQueryでコロン付きのセレクターを使う場合

私の場合は既に変数で指定されている文字列を使う必要があったので
下記のようにコロンを置換して実装した。
なお、4行目に試しに置換後の文字列を表示しているが、
なぜか「abc\:1234」とバックスラッシュが一つだけで表示される。
//以下JavaScript部分 
var test ="abc:1234"
test2=test.replace(':',"\\:")
alert(test2);  //「abc\:1234」と表示される。
alert($("#"+test2).val());  //「5678」と表示される。
//以下HTML部分
<input type="hidden" id="abc:1234" value="5678"> 

2012年11月6日火曜日

[Ajax]Ajaxで取得したHTML内のスクリプトを実行するには

このページに書いてあるよう、scriptタグを指定して、evalをかければ実行できた。

Ajax/Ajaxで取ってきたHTMLコンテンツ内の<script>タグを実行させたい

[jQuery]$.ajaxではまった件

$.ajax({
    complete:function (XMLHttpRequest, textStatus) {
        document.getElementById("result").innerHTML=XMLHttpRequest.responseText;
    },
    data:"data[Keywords]="+$("#lastKeywords").val()+"&data[ItemPage]="+$("#nextPage").val();
    type:"post",
    url:    "\/rakuzon\/themes\/itemSearch"
});
このコードが動かなかったんだけど、5行目のセミコロンをカンマにしてあげたら
動いた。(丸一日つぶした。。。)
また、dataには配列として渡してあげたほうが見やすいきがしたので以下のように修正。
$.ajax({
    complete:function (XMLHttpRequest, textStatus) {
        document.getElementById("result").innerHTML=XMLHttpRequest.responseText;
        eval($("#result script").text());
    },
    data:{
        'data[Keywords]':$("#lastKeywords").val(),
        'data[ItemPage]':$("#nextPage").val()
    },
    type:"post",
    url:"\/rakuzon\/themes\/itemSearch"
}); 
以下が参考にしたサイト。

[jQuery]postメソッドとajaxメソッドを使ってPOST送信を行うサンプル
連想配列の使用方法

【追記 2012/11/6】
なお、data:に設定するのは連想配列となる。
事前に連想配列を以下のように用意しておくことも可能だ。
postData={
    'data[Keywords]':$("#lastKeywords").val(),
    'data[ItemPage]':$("#prevPage").val()
};
$.ajax({
    complete:function (XMLHttpRequest, textStatus) {
        $("#result").html(XMLHttpRequest.responseText);
        bindPager();
    },
    data:postData,
    type:"post",
    url:"\/rakuzon\/themes\/itemSearch"
});
しかし、以下のように連想配列の要素ごとに代入していくやり方だと
なぜかうまく$.ajax()が動かなかった。原因不明。
postData['data[Keywords]']=$("#lastKeywords").val();
postData['data[ItemPage]']=$("#prevPage").val();
$.ajax({
    complete:function (XMLHttpRequest, textStatus) {
        $("#result").html(XMLHttpRequest.responseText);
        bindPager();
    },
    data:postData,
    type:"post",
    url:"\/rakuzon\/themes\/itemSearch"
}); 

2012年11月3日土曜日

[JavaScript]処理の途中で強制終了させるには。

exit、returnのようなコマンドで処理を強制終了させることができる
プログラミング言語って結構あるけど、JavaScriptはそういうコマンドが
ないとのこと。

なので、try~cacth構文を使用して代替する。
以下のサイトがわかりやすい。

JavaScript try catchによる例外処理

2012年10月26日金曜日

[JavaAcript]外部ファイル化する際はonloadする必要がある

下記のページにあるよう、JavaScriptを外部ファイル化する際は、
外部化するスクリプト全体をwindwo.onloadで指定しなければならない。
ただ、これをしてしまえば、htmlのほうにJavaScriptを書く必要はない。

JavaScript の基本

2012年10月16日火曜日

[jQuery]bindについてわかったこと

・イベントによって生成された要素にbind()するには。 


生のHTMLに書かれた要素に対してイベント登録するには
単純にbind()すればよい。
だが、イベント発生後にJavaScriptで生成された要素に対してイベント登録するには
要素の作成時にbindしなくてはならない。

$("#submit-1528237246").bind("click", function (event) {
                $("#tags").append("<p><span class=\"tag\">"+$("#tag").val()+"</span> <span class=\"rm\">×</span></p>");
                rmTag();
});

function rmTag(){
    $(".rm").unbind();
    $(".rm").bind({
        click:function(event){
            $(this).parent().remove();
        },
        mouseenter:function(){
            $(this).css("cursor","pointer");
        },
        mouseleave:function(){
            $(this).css("cursor","default");
        }
    });
}
2行目によってクリック時に要素を追加する設定をしている。
ここで追加された要素に対してイベント登録するには、この作成時の時点で
イベント登録を行わなければならない。
なので3行目でイベント登録する関数を呼び出している。

・bindによるイベント登録は上書きではなくappend


ちなみに、7行目でunbindしているのは下記のサイトで説明されているよう
bindは上書きではなくappendしていくためだ。

jQueryのイベント時の関数を上書きする

上記のソースのようにイベント発生時にrmクラス全てにイベント登録して
いくと、最初の方にbindされた要素は同じイベント登録が複数回appendされて
しまうため、毎回全登録をunbindしている。

・一つの要素に複数のイベント登録をするには


また、一つの要素に複数のイベント登録をするには8~18行目のようにする。

・"hover"はbind()で使用できない。


 下記のようにhover()という関数を使ってイベント登録をできるが、
これをbind()で実現するには上記のソースのように、mouseenterとmouseleaveに
分けて記述する必要がある。

            $(function(){
                $(".rm").hover(
                    function(){
                        $(this).css("cursor","pointer"); 
                    },
                    function(){
                        $(this).css("cursor","default"); 
                    }
                );
                $(".rm").click(
                    function(){
                        $(this).parent().remove();
                    }
                );
            });


2012年10月10日水曜日

[CakePHP]$js->submit()のoptionsのbeforeの限界と対策

以下のような形でbeforeオプションを使用し、
ajax送信直前に送ろうとしているformの値自体を操作しようと
したのだけど、うまく送れなかった。
おそらく、beforeが完了してから送信の処理が開始されるので
はなく、beforeと同時並行的に送信するデータ自体は取得して
いるからだろう。

echo $js->submit("登録する",array(
    'id'=>'register',
    'url'=>'add',
    'update'=>'#complete'
    'before'=>'
        var fm = document.getElementById("add");
        $("#add").append("<input type=\"hidden\" name=\"data[Theme][explanation]\"/>");
        fm.elements[\'data[Theme][explanation]\'].value=$("#explanation").val();
    '
));

では、どうすればよいか。
beforeで設定するのではなく、別で$js->submitより上に、
onclickのイベント設定をしてしまえばよい。
こうすれば、フォームの値の操作が完了してから、
送信の処理が開始されるようになる。

$js->get("#register")->event('click','
    var fm = document.getElementById("add");
    $("#add").append("<input type=\"hidden\" name=\"data[Theme][explanation]\"/>");
    fm.elements[\'data[Theme][explanation]\'].value=$("#explanation").val();
');

echo $js->submit("登録する",array(
    'id'=>'register',
    'url'=>'add',
    'update'=>'#complete'
)); 
※CakePHPのバージョンはcakephp-cakephp-1.3.15-9-gacd25c3.zip

[PHP]PHPでJavaScriptを書き出す際のクオテーションについて

PHPでjavascriptを吐き出す場合、シングルクオテーションで
囲っている例が多い。もしかしたらダブルでもよいのかもしれないが、
シングルで囲う場合を考える。

以下phpのスクリプトで2~6行目がJavaScriptである。

$js->get("#register")->event('click','
    var fm = document.getElementById("add");
    $("#tags span").each(function(i){
        $("#add").append("<input type=\"hidden\" name=\"data[Tag]["+i+"][content]\"/>" );
        fm.elements[\'data[Tag][\'+i+\'][content]\'].value=$(this).text();
     });
');

javascript全体がシングルでかこわれている。
この中でさらにクオテーションを使用したい場合、
以下のサイトから、

PHPで javascriptを書き出すときのクォーテーションの使い方

「PHPとしての最初のクォティションと最後のクォティションはそのままで、
内側の同種クォティションはエスケープが基本」

らしいです。

なので、私も

・ダブルはそのまま使う。
・シングルを使いたい場合はエスケープ。

ただし、

javascriptが吐き出す文字列にダブルクオテーションを
付与したい場合は、エスケープしないとだめなようだ。

[CakePHP]inputタグのnameをjQueryで指定できない。

CakePHPでajaxを使ってフォームの内容を送信する際に、
javascriptでinputタグのtype=hiddenのvalueを操作したい場合が
あったのですが、その際に対象のinputエレメントを指定するのに
苦労しました。

inputタグにidをつけてしまえば簡単なのですが、
nameというユニークの属性を持っておきながら、
さらにidを付与するのは少し不細工なので、なんとか
nameで指定するやり方を考えました。

jQueryだと通常、
$(*"input[name=XXX]")
といった形で、name属性で指定できる。

だが、CakePHPのhtmlヘルパーで作成されるinputタグは以下のように
"["と"]"を含むので指定することができない。
色々な方法でエスケープを試みたができなかった。
<input type="text” name=”data[Model][field]“/> 
なので、以下のサイトを参考にした。

cakePHPの Formヘルパーで作られるdata[Model][Field]をJavascript処理する
[CakePHP] 配列のname属性をJavaScriptで参照する方法
var fm = document.getElementById("add");
//中略
fm.elements[\'data[Item][\'+i+\'][code]\'].value=itemLst[i];
上記のような形でnameを変数で動的に変動させることもできるのだ。
上記の例では、formエレメントをidで取得してきているが、
nameをつけている場合には、以下のように書くことができる。
(formのnameが"add"である場合)
document.add.elements[\'data[Item][\'+i+\'][code]\'].value=itemLst[i]; 
※ここでのバックスラッシュはWindwosでいういわゆるYENマークね。
※CakePHPのバージョンはcakephp-cakephp-1.3.15-9-gacd25c3.zip