alert()もどきのalertX()で、数秒後に自動消去するプラグイン例

# はじめに
プログラミングしていると、よく、alert()が数秒後には、自動消去してくれたらいいと思う場面がよくあります。毎回、OKボタンを押さなければならないというのが面倒です。そこで、サンプルをつくりました。これはプラグイン形式ですが、自分のwebページであれば、ページのスクリプトでも移植可能です。
プラグインにしているのは、stampTubeプラグインで、すべてのwebページでも動作テストしたかったからです。プラグインであれば、yahooページでも、アメバページでも動作確認できます。
このstampTubeプラグインは、yoytubeの動画を再生するプラグインです。
では、このサンプルがどう動作するのかについて、以降で、サンプルで説明します。
その前に、プラグインをダウンロードするリンクを以下に示します。

# ダウンロードリンク
[stampTubeプラグイン](https://favorite.tecoyan.net/slim/zip/stampTubeプラグイン.zip)
インストール方法
ここをクリックして、パソコンの任意のフォルダーへzipファイルを保存します。そこで、zipファイルを任意のフォルダーへ解凍します。chromeの設定で拡張機能->拡張機能を管理->パッケージ化されていない拡張機能を読み込むボタンをクリックして、解凍したフォルダーを指定してフォルダーの選択を押すとインストールできます。






# 動作サンプル

![stampTube.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198006/c08f224c-a540-75b5-c3c8-25a4fa4796b5.png)
この画面は、yahoo!JAPANページで、ctrlキー+clickすると表示されます。(stampTubeプラグインがインストールされていること)
画面の右上にpop画面が表示されます。
ここで、alert()もどきのalertX()をテストするため、ランダムボタンを改良しています。このボタンをクリックすると、popメッセージがalert()のようなイメージで表示されます。自動消去して、タイマーで数秒後に次のalertX()を表示しています。
![stampTube1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198006/50f90494-7b3b-b2f8-7c2f-1872b1e42672.png)
薄緑色のpop表示がaletX()で表示したメッセージです。このメッセージが数秒後に自動消去します。続けて次のメッセージも表示され、数秒後に消去します。
alertX()の自動消去のイメージがつかめると思います。
このように、alert()のようなメッセージを自動消去するメソッドです。このメソッドはwindow.alertX()で呼び出しています。alertX()はwindowに追加したものです。

# stampTubeプラグインのコード
このコードはサンプルのコードですが、youtuve動画は動作します。(コードはダーティです。)
参照するコードはalertX部分のところを見てください。

``` content.js
/******************************************************************************
*表題：　　　　　stampTubeプラグイン
*プラグイン：   このプラグインは、すべてのタブで動作する小型のyoutubeプレーヤーです。
*                   iframeでサイトのurlをロードし再生します。
*                   url=https://favorite.tecoyan.net/slim/index_radio_simple.php
*このドメインであれば、すべてのWebサイトのタブで動作します。same origin制約で許可されています。                   
*                                                         
*ファイル名：   content.js
*説明：           すべてのタブで動作するcontent.jsファイルです。このスクリプトでbody要素に
*                   iframeプレーヤーをアペンドします。
*                   検索結果を保存します。
*                   リストキーと本体データ(サムネイルとタイトルの配列)
*                   あらかじめ、jquery.jsをロードしています。
*ロード元:      C:\拡張プラグイン\stampTube
*サイズ：　　　　
******************************************************************************/
//このファイルで使用している変数のみにする
let yahoo_search;
let params;
let tab_sw;
let p_element;
let subdomain;                  //
//グローバルオブジェクト変数
let other;
//
other = {

//
    main: function () {
                window.alertX=function(msg,n){
                //すでにあればアペンドしない。
                if($("#apop").length===0){
                        let html = "<div id='apop' style='padding:5px;z-index:999999;width:200px;height:100px;background-color:lightgreen;position:fixed;top:10%;left:10%;'>"+msg+"</div>";
                        $("body").append(html);
                }
                $("#apop").css("display","block");
                  $("#apop").draggable();
                setTimeout(()=>{
                  
                    
                    $("#apop").css("display","none");

                },n);
        };

        alertX("<br>このブラウザーには、<br>「stampTubeプラグイン」が<br>インストールされています。(8秒後、消去)",8000);

        
            /********************************************************************************
             *iframeのsrcに指定しているURLのphpファイルでheader("Access-Control-Allow-Origin: *");を
             *指定しているため、このhtmlページはすべてのサイトで動作可能
             *
             *********************************************************************************/
            //プレーヤー要素の追加
            //alert("youtube_radio要素をアペンドします。");
            let html ="<!-- youtubeラジオ　-->\n\
<div id='youtube_radio' title='この枠はドラッグすることができます。' style='background-color:lightyellow;z-index:999999;border: 10px solid lawngreen; width: 270px;height: 465px; position: fixed;zoom: 1.0;top:42px;right:0px;'>\n\
<img style='zoom:0.5;' src='https://favorite.tecoyan.net/slim/images/radio_small.png'><span  style='zoom:0.9;position:absolute;top:0px;right:10px;font-size:10px;'>TinyYoutubePlayerPlusプラグインv1.0</span>\n\
<iframe class='iframe_radio' style='position:relative;top:0px;border:solid 2px orange; width:250px;height:440px;' src='https://favorite.tecoyan.net/slim/index_stampTube.php?vid=YNQT68uHpyg&title=%E4%BB%8A%E6%97%A5%E3%81%AE%E6%97%A5%E3%81%AF%E3%81%95%E3%82%88%E3%81%86%E3%81%AA%E3%82%89%E3%80%80%E6%A3%AE%E5%B1%B1%E8%89%AF%E5%AD%90%E3%80%801967&url=&db_id=3120'></iframe>\n\
</div>";


            $("body").append(html);
            $("#youtube_radio").css("display","none");
// ユーザーエージェントを取得
var userAgent = navigator.userAgent.toLowerCase();

// スマホかどうかを判定
var isSmartphone = /iphone|ipod|android.*mobile|windows.*phone|blackberry.*mobile/.test(userAgent);

// PCかどうかを判定
var isPC = !isSmartphone;

// スマホの場合の処理
if (isSmartphone) {
    console.log("このデバイスはスマホです");
    // ここにスマホの場合の処理を記述
}

// PCの場合の処理
if (isPC) {
    console.log("このデバイスはPCです");
    // ここにPCの場合の処理を記述
}            
            
            
            //
            document.getElementById("youtube_radio").onpointermove = function(event){
                      if(event.buttons){
                          this.style.left     = this.offsetLeft + event.movementX + 'px';
                          this.style.top      = this.offsetTop  + event.movementY + 'px';
                          this.style.position = 'absolute';
                          this.draggable      = false;
                          this.setPointerCapture(event.pointerId);
                      }
                      //
                      $("#youtube_radio").css("position","fixed");

            };
    }
};
/*******************************************************************************
 * アメバ編集のyoutubeボタンでリストを保存
 * 
 *********************************************************************************/
//検索結果を取得します。
//$(".js-video-item")
$(".js-accessoryTab-button").on('click',(e)=>{
    let query_key;
//document.getElementsByClassName("js-accessoryTab-button").addEventListener('click', (e) => {
    //alert("bodyでクリックしました。");
    let aaa = document.getElementsByClassName("js-video-item");
    if (aaa.length !== 0) {
        //ここで、リストデータを生成して保存
        //val.innerHTML　この中の<imgタグのsrcを取得　
        //val.innereText  タイトル
        let src;
        let title;
        let data = [];
        let aa = {};
        let thumb;
        $.each(aaa, (i, val) => {
            //<imgタグを抽出
            src = val.innerHTML.match(/<img[\s\S].*\/div><div/)[0].replace(/<\/div><div/, "");
            title = src.split("alt=")[1].replace(/>/, "");
            title = title.replace(/"/g, "");
            thumb = src.replace(/alt=.*>/, ">");
            //srcからサムネイルとタイトルを分けて
            aa = {"サムネイル": thumb, "タイトル": title};
            data.push(aa);
            //                            console.log(src);
            //                            dd.push(src);

        });
        //dd(配列データ)を保存します。サーバへ
        //このリストをサーバーへ保存
        //<imgタグのhtmlデータ
        query_key = $("#js-searchBox-youtube")[0].value;
        var params = {
            query_key: query_key,
            data: data

        };
        var resp = $.ajax({
            url: 'https://favorite.tecoyan.net/slim/save_ameba_youtube_list.php',
            type: 'POST',
            dataType: 'html',
            data: params,
            cache: true,
            async: false
        }).responseText;

        //
        alert("リストデータを" + resp);
    }


});

$("body").on('click',(e)=>{
    if(e.ctrlKey===true){
    
        if(window.confirm("youtubeプレーヤで再生しますか?")){
             //alert("プレーヤーを開きます。");
             //ラジオプラグインを利用
             $("#youtube_radio").css("display","block");
             $("#youtube_radio").draggable();

        }else{// 「キャンセル」時の処理開始
            $("#youtube_radio").css("display","none");
            //ここで再生を停止
            //プラグインからiframeのプレーヤーへ再生停止要求を出すには。
            //テスト的に"play_video"メッセージをbackground.jsへ出す
            chrome.runtime.sendMessage({data: 'play_video'}, function (msg) {
                       //応答は? 特になし。     
                            
            });
            return;
        }
        

        
        
    }
    
});
/*EXTENTION IDをセット */
var myid = chrome.runtime.id;
$("#ex_id").html(myid);
tab_sw = tab_case();
//
//この要素は親タブの要素のため、子タブでは取得できず、nullになります。
//content.jsはひとつのファイルで二つのタブの処理を行います。
subdomain = $("#plugin_id").html();
console.log("先頭のsubdomainチェックで　" + subdomain);
/**********************************************************************
メソッド名：    yahooサーチオブジェクト
ステータス ： 機能しています。
内容：          ページで文字列をセレクトしてマウスアップするとyahooサーチで検索します。
**********************************************************************/
yahoo_search = {
        p_element: document.querySelector("body"),                                                                                                                                         
        main: (event)=>{
                if (window.getSelection().toString().length === 0) {
                    return;
                }
                
                if(location.href.indexOf("favorite.tecoyan.net")!==-1&&$("#search_sw")[0].innerText.indexOf("オン")!==-1){
                            //yahoo検索をリクエスト
                            yahoo_search.yahoo(window.getSelection().toString());
                }else if(location.href.indexOf("favorite.tecoyan.net")===-1){
                            yahoo_search.yahoo(window.getSelection().toString());
                }

        },
        yahoo:(k_w)=>{
                var w = null;
                var char_ = "";
                if (document.all) {
                    char_ = document.charset;
                } else {
                    char_ = document.characterSet;
                }
                if (char_ === "utf-8" || char_ === "UTF-8") {
                } else {
                }
                var k_w = k_w.replace(/&nbsp;/g, ' ');
                if (typeof (k_w) !== "undefined") {
                    var gg = 'http://search.yahoo.co.jp/search?ei=' + char_ + '&p=' + k_w;
                    if (w === undefined || w === null) {
                        w = window.open(gg, '_blank');
                        w.focus();
                    } else {
                        w.close();
                        w = window.open(gg, '_blank');
                        w.focus();
                    }
                    window.getSelection().empty();
                }
        }

};
/*このイベントは、文字列を選択すると実行するリスナー*/
yahoo_search.p_element.addEventListener('mouseup', yahoo_search.main, true);

/********************************************************************************
*タブ処理へ分岐  
*                   slim_content.jsは、タブ毎に処理空間を持つ
*                   タブ毎の処理を分岐　タブ毎にオブジェクト定義　その中のmain()メソッドをコールしている
*                   tab_swで分岐
*                   ここで、#temp_pluginをチェックして、オンオフにより、
********************************************************************************/
switch (tab_sw) {
    case "other":
            other.main();     
            break;

    case "youtube":
            yahoo_search.main();
            break;
}
//******************************************************************************
//スイッチ変数
//タブ処理分岐
//******************************************************************************
function tab_case() {
    //プレーヤータブが複数ある場合、favoriteにする
    if (location.href.indexOf("results?search_query=") !== -1) {
           return "youtube";
    }else  {
           //その他
           return "other";
    }
}
/*****************************************************************************************
 * このリスナーは、タブがアメバ編集のときのみ可能
 * bodyでそりにそのしてもそのタブがアメバ編集タブでなければ、エラーになるため、事前にチェックしておく
 * locationをチェック
 ******************************************************************************************/
document.getElementsByTagName("body")[0].addEventListener('click', (e) => {
                   //アメバ編集か?
                   if(location.href.indexOf("entry/srventryupdateinput.do?") !== -1){
                            //
                            //alert("bodyでクリックしました。");
                            let aaa = document.getElementsByClassName("cke_wysiwyg_frame")[0].contentDocument.getElementsByTagName("iframe");
                             //このsrcにリピート属性を追加
                             let sw = 0;
                             $.each(aaa,(i,val)=>{
                                     if(val.src.indexOf("/embed/")!==-1){
                                             //?があれば
                                             if(val.src.indexOf("/embed/?")!==-1){
                                                      //val.src = val.src.replace(/embed/,"embed\/?autoplay=1&amp;loop=1&amp;playlist=");
                                                      return true;
                                             }else{
                                                     //let src = val.src;    
                                                     //リピート追加
                                                     //?autoplay=1&amp;loop=1&amp;playlist=
                                                     val.src = val.src.replace(/embed\//,"embed\/?autoplay=1&amp;loop=1&amp;playlist=");
                                                     sw = 1;
                                                     return true;
                                             }
                                     }
                             });
                             //
                             if(sw === 1){
                                     alert("リピート機能を追加しました。");
                             }else{

                             }
                   }
});
``` 

# stampTube.js(長いため、省略します)
ランダムボタンのイベントリスナーは、このコードの中にあります。
このスクリプトファイルは、pop画面の中にiframeタグで読み込むためのhtmlファイルの中で定義しています。そのhtmlファイルで、youtubeプレーヤーとリストをロードしています。これらは、今回のalertX()メソッドには無関係ですが、サンプルとして利用したためです。
``` stampTube.js
//一部抜粋
        window.alertX=function(msg,n){
                //すでにあればアペンドしない。

                //setTimeout(()=>{
                        $("#apop").remove();
                        if($("#apop").length===0){
                                let html = "<div id='apop' style='padding:5px;z-index:999999;width:200px;height:100px;background-color:lightgreen;position:fixed;top:10%;left:10%;'>"+msg+"</div>";
                                $("body").append(html);
                        }
                        $("#apop").css("display","block");
                        
                $("#apop").draggable();
                    setTimeout(()=>{
                    $("#apop").css("display","none");
                },n);

                //},n);
        };
```

``` stampTube.js
//一部抜粋
 $("#random").on('click',()=>{
        //alert("ランダム再生");
        glistobj.get_lists("ランダムリスト");
        /*****************************************************
        *Promiseテスト 
        *このパターンをすぐに忘れるため。
        *プロミスオブジェクト(promise3)のコンストラクターを定義して、 
        *****************************************************/
        const promise3 = new Promise((resolve, reject) => {
            alertX("同期処理でalertX()を表示",5000);
            //5秒後にresolveしている。  
            setTimeout(resolve, 6000, 'foo');
            console.log("promise3");
         });
         //コンストラクターを実行して、同期処理(resolve)で'foo'valueを返すので、
         //それをalert()で表示している。
         promise3.then((value)=>{
                     const promise4 = new Promise((resolve, reject) => {
                         
            alertX("次の同期処理でalertX()を表示",5000);
            //5後にresolveしている。  
            setTimeout(resolve, 6000, value);
            console.log("promise4");
         }).then(()=>{
             //alert("これは、promise4の"+value+"です。")
         });
             
             //2秒後に結果が返る
             //'foo'を表示
             //alert(value);
         });
         return false;
    });

```



# alert()自体には、自動消去の機能はないようです。
alert()は、簡単でちょっとしたデバッグには、よく使いますが、あっちこっちに入れすぎると都度のOKボタンを押すのが面倒になります。
ネットでも自動消去の質問が多く出ています。やはり、皆さんも同じなのかと思います。
これは、pop表示する自作のalert()もどきのメソッドを作っておくといいかもしれません。今回のサンプルも10ステップ程度です。

    
# あとがき
まだ、作成してテスト中ですので、使い勝手はいまいちかもしれません。
時間があれば、手を加えて行きたいと思います。