javascript について質問です




1.Google Map を表示します
 http://maps.google.co.jp/

2.URL 欄に次のように打ちこみます
javascript: (function(){setTimeout(function(){alert('timeout')}, 5000)})()

3.次の操作を行います
キーボードで地図移動⇒アラートが出る⇒キーを離す⇒アラートを消す⇒地図はとまる

このとき、Google Map は KeyDown や KeyUp でしかキーの状態が取得できないと思うのですが
どうして移動をとめることができるのでしょうか

alert を補足するイベントがあるのでしょうか
それとも、キーの状態をタイマーでチェックしているのでしょうか
(キーイベント以外で状態を補足できるのでしょうか?)

教えてください

回答の条件
  • 1人2回まで
  • 登録:
  • 終了:2008/09/10 14:33:09
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

回答1件)

id:m_nagase No.1

回答回数58ベストアンサー獲得回数8

ポイント60pt

javascriptはマルチスレッドではないので、alertのモーダルダイアログで(モーダルではない処理系もあるが) 他の処理が止まるのは当然だと思うのですが。

おそらく、キーを押し続けて地図がスクロール処理としては

0) キーのUp Down を捕らえて、キーの状態を保存するだけのハンドラある
1) 矢印キーの押下を検知してスクロール処理を開始
2) 地図をスクロールしてsetTimeoutでリピートする
3) 2のタイムアウトハンドラでキーの状態をチェックし、押されていれば 2へ、キーが押されていなければスクロール停止

こんな感じの処理だと思います。

そこでスクロール途中にモーダルダイアログが表示されるため、ダイアログ表示中は 2 と 3 の処理が止まるのだと思います。

そして処理が止まっているうちにキーが離されるため、スクロールの処理が継続しないんじゃないかと思います。

ソースを読めば分ると思いますが、難読化されているので読もうという気になれませんね。

id:you_got

質問の補足をありがとうございます。

私もそこまでは想像していました。

>3) 2のタイムアウトハンドラでキーの状態をチェックし、押されていれば 2へ、キーが押されていなければスクロール停止

ここで、キーの状態をチェックする方法はありますでしょうか?

KeyUp, KeyDown ではそのイベントを起こしたキーが何か、という情報はくるのですが

「今何が押されている」という情報は取得できません

→ を押したときは →

→ をそのままにして ↓ を押したときは ↓ だけきます

ご存知でしたらその部分を詳しく教えていただけるとたすかります

2008/09/10 08:55:08
  • id:you_got
    書き忘れました
    対象ブラウザは IE 6 です
  • id:tukihatu
    やってみましたが…えっとどういう意味ですか?
    キーボード押しっぱなしで地図を右に移動させていて、alertが出た段階で移動は止まっちゃったんですが。IE6で。
  • id:you_got
    alert が出た段階でとまるのは
    m_nagase さんの回答のとおり正常な動きです

    alert が出た状態で キーを離すと KeyUp イベントが起こらないため
    alert が消えた後に キーが押されたままと認識されて移動が再開してしまいそうですが
    Google Map では止まりっぱなしになります

    わかりにくい質問ですみません


  • id:you_got
    簡易化したサンプルです
    IEで確認しました

    <HTML>
    <HEAD>
    <script language="JavaScript">
    <!--

    window.onload = function(){

    var $ = function(id){return document.getElementById(id)};

    document.attachEvent("onkeydown", function(e){
    $('onkeydown').innerHTML = e.keyCode;
    //alert('');
    });
    document.attachEvent("onkeyup", function(e){
    $('onkeyup').innerHTML = e.keyCode;
    });
    }

    </script>
    </HEAD>

    <BODY>
    <div id='onkeydown'></div>
    <div id='onkeyup'></div>
    </BODY>
    </HTML>

    コメントアウトをはずして alert を 出した場合は、onkeyupが取得できません
    また、イベントで取得できるキーは イベントを起こしたキーのみで
    「現在押下中のキーをすべて取得する」といったことはできません

    「現在押下中のキーをすべて取得する」ができれば、タイマーで監視できるのですが。。
  • id:sheile
    Google Mapのソースを読んだわけではないですが、ちょっと思いついたので。

    おそらくyou_gotさんが想定している物は
    1. keydownイベントで「押されてるフラグ」をON
    2. setInterval/setTimeoutなどで定期的に呼ばれている関数から「押されてるフラグ」を見て、
    押されていれば移動
    3. keyupイベントで「押されてるフラグ」をOFF
    といったロジックかと思います。

    しかし、キーを押しっぱなしにした場合、onkeydownは連続して送られてきます。
    ということは、
    1. keydownイベントで押された方向に移動
    だけで、実現できるのではないかと。
    これであれば、alert中にkeyupが取れなかったからといって問題は起こりません。
  • id:you_got
    なるほど!
    それはうまい手ですね

    と、やってみましたが
    2つのキー同時押しに対応できませんでした。。
    (→↓ で斜め移動)

    A と B が同時に押下されている、という状態を表現するには

    今のところ

    ・フラグ(しかし、KeyUp が取得できない)
    ・なんらかの方法で押下中のキーを取得する

    ですね
    うーーーむ
  • id:sheile
    うーむ。確かにGoogle Mapだと同時押しもOKですね・・・
    ためしにこんなのをやってみました。

    1. 5秒後のAlertをセット
    2. →と↓を同時押しで移動
    3. Alertが出現したら→だけを離してAlertを閉じる
    4. 動かない
    5. ↓は押したまま、→を押すと右にだけ動き出す。

    4で下に動かなかったこと、5で右下に動かなかったことを考えると
    ・なんらかの方法で押下中のキーを取得する
    というわけでも無いのかなぁ・・・
  • id:you_got
    sheile さんありがとうございます
    おかげでわかりました

    1.同時押しのために
     「フラグで管理」

    2.アラートが出た場合は処理終了
     「一定の間隔以内に KeyDown が発生しない場合は、処理をキャンセル」

    この2つめがわかっていませんでした
    Google Map は同時押しだろうがなんだろうが
    アラートが出ると処理が止まるんですね!

    まとめると次のとおりです

    本当にありがとうございました

    <HTML>
    <HEAD>
    <script language="JavaScript">
    <!--

    window.onload = function(){

    //
    // メソッド定義
    //

    var $ = function(id){return document.getElementById(id)};
    var KeyState = [];

    // 表示を更新
    var displayKeyState = function()
    {
    $('nowKey').innerHTML = KeyState.join();
    }

    // キークリア
    var clearAll = function()
    {
    KeyState = [];
    displayKeyState();
    }

    // キークリアタイマ
    var clearTimerHdl = null;
    var setClearTimeout = function()
    {
    if(clearTimerHdl)
    {
    clearTimeout(clearTimerHdl);
    }
    clearTimerHdl = setTimeout(clearAll, 500);
    }

    // キー検索
    var searchKey = function(key)
    {
    for (var n in KeyState)
    {
    if(KeyState[n] == key)
    {
    return true;
    }
    }
    return false;
    }

    //
    // イベント定義
    //

    document.attachEvent("onkeydown", function(e){

    // 500ms以内に再度呼ばれないと、キーをクリアする
    setClearTimeout();

    // 押下中キーだったら、イベントキャンセル
    if(searchKey(e.keyCode)){return;}

    // フラグを追加
    KeyState.push(e.keyCode);

    // 表示を更新
    displayKeyState();
    });

    document.attachEvent("onkeyup", function(e){

    // 押下中キーのアップイベントだったら、フラグを削除
    for (var n in KeyState)
    {
    if(KeyState[n] == e.keyCode)
    {
    KeyState.splice(n, 1);
    }
    }

    // 表示を更新
    displayKeyState();
    });

    // 5秒後にアラートを出す
    setTimeout(function(){alert('timeout')}, 5000);

    }

    </script>
    </HEAD>

    <BODY>
    <div id='nowKey'></div>
    </BODY>
    </HTML>
  • id:m_nagase
    自分でも検証コード書いてみましたが、alert中に発生したイベントは一時停止ではなくキャンセルされるみたいですね。
    直接解決につながった訳でもないのにポイントありがとうございます。勉強になりました。

    <html>
    <head>
    <meta http-equiv="Content-Script-Type" content="text/javascript">
    <title>JavaScript Key Event Test</title>
    <script type="text/javascript">
    var ie=/*@cc_on!@*/false;
    var keymap=new Array();
    var ckud=0;
    var ckpr=0;

    function OnKeyDown(e){
    if(ie){
    keymap[event.keyCode]=1;
    }else{
    keymap[e.keyCode]=1;
    }
    DispKeyUpDown();
    }

    function OnKeyUp(e){
    if(ie){
    keymap[event.keyCode]=0;
    }else{
    keymap[e.keyCode]=0;
    }
    DispKeyUpDown();
    }

    function OnKeyPress(e){
    var ev=e||window.event;
    DispKeyPress(ev.charCode || ev.keyCode);
    }

    function DispKeyUpDown(){
    var el=document.getElementById("keyupdown");
    var html="("+(ckud++)+") ";
    for(var key in keymap){
    if(keymap[key]!=0){
    html=html+key+' ';
    }
    }
    el.innerHTML=html;
    }

    function DispKeyPress(k){
    var el=document.getElementById("keypress");
    el.innerHTML="("+(ckpr++)+") "+k;
    }

    function tout(e){
    alert('Timeout');
    setTimeout(tout,5000);
    }
    </script>

    </head>
    <body>
    <p>KeyUp/KeyDown:<span id="keyupdown"></span><p>
    <p>KeyPress:<span id="keypress"></span><p>
    <script type="text/javascript">
    if(ie){
    document.onkeydown=OnKeyDown;
    document.onkeyup=OnKeyUp;
    document.onkeypress=OnKeyPress;
    }else{
    document.addEventListener("keydown",OnKeyDown,true);
    document.addEventListener("keyup",OnKeyUp,true);
    document.addEventListener("keypress",OnKeyPress,true);
    }
    DispKeyUpDown();
    DispKeyPress();
    setTimeout(tout,5000);
    </script>
    </body>
    </html>
  • id:you_got
    m_nagase さん
    言葉足らずの質問の真意を汲み取り、まとめていただき
    ありがとうございました

    alert でイベントが無視されてしまうのは困りますね
    私も今回のことがあるまで知りませんでした

    検証用コードありがとうございます
    クロスブラウザの対応まで!

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません