概要
デバック情報などをブラウザ上に表示する my_console.log()を作りました
使い方
my_console.log("ログを表示する");
my_console.log("背景色を指定してログを表示する", "red");
my_console.log("りんごは\n赤いです", "yellow");
ソースコード
var my_console = {
ログの行数: 0,
/***********************************************************************************************
ログ_上限数は 五百行を基本とする。
たった5000行で、物凄く重くなる。また、ほんの30回くらいUI更新する処理()したら、すぐ、ログ5000行なります。
/***********************************************************************************************/
ログ_上限数: 500,/*これを超えると、古いログを半分削除する*/
hash: "hash_2021mmddhhmmss",/*なんでもいい。とりあえず固有のIDにするためにコーディング時の日時をいれておく。例: "hash_201910111424" */
modalWindow_elem: null,
コンソールテキスト行_div_elem: null,
isログの最後に現在日時を追加する: true,
is永続的な新しいスタイルシートを設定した: false,
ログの行数を表示する場所_span_elem: null,
自動スクロールする_input_elem: null,
isログ_上限数が多い事の警告を出した: false,
createOverlay: function(){
/* コーダーの為の安全装置 */
if( this.hash === null ){
my_alert('エラー。my_consoleを使いたいなら、プロパティのhashになんでもいいので文字列を入れて下さい');
return false;
}
/*安全装置*/
if(this.isログ_上限数が多い事の警告を出した !== true && this.ログ_上限数 > 500){
my_alert('consoleのログ_上限数が 500行より多いです。この警告は初めに1度だけ行います。デバックが終了したら500に戻しましょう。');
this.isログ_上限数が多い事の警告を出した = true;
}
/* ウインドウを生成 */
/* 既に存在するかチェックし、"存在するなら何もしない */
if( this.modalWindow_elem !== null){
return true;
}else{
/* この中では、my_console私用NG。無限ループなる */
// console.log('my_consoleのmodalwindowが無いのでこれから作ります');
};
/* body内の末尾に追加 */
this.modalWindow_elem = document.body.appendChild(document.createElement('div'));
this.modalWindow_elem.id = `my_console_${this.hash}`;
var h2_elem = this.modalWindow_elem.appendChild(document.createElement('h2'));
// h2_elem.innerHTML = 'console windows(クリックすると全面<=>後面)';
var p_elem = this.modalWindow_elem.appendChild(document.createElement('p'));
p_elem.innerHTML = `ログの上限数:${this.ログ_上限数}行 現在の行数:<span id="log_rows_now">${this.ログの行数}</span>`;
/*ちょっと力業だが、thisに入れておく*/
this.ログの行数を表示する場所_span_elem = this.modalWindow_elem.querySelector('span#log_rows_now');
var label_elem = this.modalWindow_elem.appendChild(document.createElement('label'));
label_elem.innerHTML = `<input type="checkbox" name="is_auto_scroll_${this.hash}" checked="checked">自動スクロールする`;
this.自動スクロールする_input_elem = this.modalWindow_elem.querySelector(`input[name=is_auto_scroll_${this.hash}]`);
this.コンソールテキスト行_div_elem = this.modalWindow_elem.appendChild(document.createElement('div'));
this.コンソールテキスト行_div_elem.id = `my_console_text_line_${this.hash}`;
var that = this;
this.modalWindow_elem.addEventListener('click', function(){
/* UIが9999なので、10000にして前にだす */
if(that.modalWindow_elem.style.zIndex === "10000"){
that.modalWindow_elem.style.zIndex = "9998";
}else{
that.modalWindow_elem.style.zIndex = "10000";
}
});
/*ここまでOK*/
this.とあるelemに対してとあるcss_objを適用する(this.modalWindow_elem, {
'width': '360px',
'height': '80%',
'margin-top': '10x',
'padding': '10px',
'font-size': '12px',
'position': 'fixed', /* fixed: これは画面の決まった位置に要素を固定させたいときに使います。 */
'border': '2px solid #D04255',
'background-color': '#FFF',
'background': 'rgba(255, 255, 255, 0.9)',/* 背景を半透明にする */
/* 'display': 'none', */
'z-index': '9999', /* 「オーバーレのHTML要素」よりもスタックレベルを高くする */
'color': '#0000ff',
'text-align': 'left',
'right': 10 + 'px',
'top': 10 + 'px',
"padding": "4px",
"padding-left": "20px",
"padding-bottom": "50px",
});
this.とあるelemに対してとあるcss_objを適用する(h2_elem, {
"font-size": "12px",
});
this.とあるelemに対してとあるcss_objを適用する(p_elem, {
"font-size": "10px",
});
this.とあるelemに対してとあるcss_objを適用する(this.コンソールテキスト行_div_elem, {
"height": "90%",
"overflow": "scroll"
});
h2_elem = void 0;
p_elem = void 0;
label_elem = void 0;
},
コンソールウインドウを非表示にする: function(){
this.modalWindow_elem.hidden = true;
},
コンソールウインドウを表示する: function(){
this.modalWindow_elem.hidden = false;
},
isコンソールウインドウは表示されている: function(){
if(this.modalWindow_elem.hidden === true){
return false;
}else{
return true;
}
},
/* 表示、非表示をトグルスイッチで切り替える */
コンソールウインドウの表示と非表示を切り替えるトグルスイッチ: function(){
this.createOverlay();
if( this.isコンソールウインドウは表示されている() === true ){
this.コンソールウインドウを非表示にする();
}else{
this.コンソールウインドウを表示する();
}
},
log: function(text, 背景のcssカラーコード = 'transparent', isログの最後に現在日時を追加する = this.isログの最後に現在日時を追加する){
this.createOverlay();
/* 改行コードつきの文字を 人間に見やすくするために、置換してみる */
text = text.replace(/\n/g, '<br>');
/* てきとうにトリム */
text = text.trim();
/************************************************************
追加するHTMLを作る。(軽量化の為、DOM操作は極力最小限で)
/************************************************************/
var HTML = '';
/* 文章と日時の親 div */
if(背景のcssカラーコード === 'transparent'){
HTML += '<div class="p_no_oya">';
}else{
HTML += '<div class="p_no_oya" style="background:'+背景のcssカラーコード+'">';
}
/* 文章 */
HTML += '<p class="my_console_text_'+ this.hash + '">' + text + '</p>\n';
/* 日時 */
if(isログの最後に現在日時を追加する === true){
HTML += '<p class="my_console_date_'+ this.hash + '"><span>' + this.get現在日時() + '</span></p>\n';
}
/* 文章と日時の親 divの終わり */
HTML += '</div>';
/************************************************************
追加する
/************************************************************/
this.コンソールテキスト行_div_elem.insertAdjacentHTML('beforeend', HTML);
this.ログの行数++;
/************************************************************
永続的な新しいスタイルシートを1度だけ作成する */
/************************************************************/
if( this.is永続的な新しいスタイルシートを設定した !== true ){
var style_elem = document.head.appendChild(document.createElement('style'));
style_elem.id = `my_console_style_${this.hash}`;
style_elem.type = 'text/css';
style_elem.innerHTML = `
div.p_no_oya{
border-bottom: 1px solid #bbb;
}
p.my_console_text_${this.hash}{
margin: 0px;
color: #000000;
background-color: transparent;
}
p.my_console_date_${this.hash}{
margin: 0px;
text-align: right;
background-color: transparent;
}
p.my_console_date_${this.hash} span{
text-align: right;
color: #000000;
background-color: #c4c4c4;
}
`;
style_elem = void 0;
this.is永続的な新しいスタイルシートを設定した = true;
}
/************************************************************
ログの削除
/************************************************************/
if(this.ログの行数 > this.ログ_上限数){
/*************************************************
無限ループ注意
本処理内でmy_console.log()内でmy_console.log()を呼ぶと、再帰的になり簡単に無限ループに突入します。
辞めましょう。
**************************************************/
var 削除する行数 = Math.floor(this.ログ_上限数 / 2);
var TEMP_削除した行数 = 0;
var 削除対象_elem_nodeList = this.コンソールテキスト行_div_elem.querySelectorAll(`div.p_no_oya:nth-child(-n+${削除する行数})`);
for(var i=0, len=削除対象_elem_nodeList.length; i<len; i++){
var 削除対象_elem = 削除対象_elem_nodeList[i];
削除対象_elem.parentNode.removeChild(削除対象_elem);
削除対象_elem = void 0;
}
this.ログの行数 = this.ログの行数 - 削除対象_elem_nodeList.length;
/*********************************************************************
ログを削除した後なので無限ループにはならないとは思いますが、my_console.log()内でmy_console.log()を使う場合は注意すること
*********************************************************************/
my_console.log(`ログが上限LIMIT${this.ログ_上限数}行を超えたので、${削除対象_elem_nodeList.length}行を削除しました。`, 'yellow');
削除対象_elem_nodeList = void 0;
}
/************************************************************
スクロール
/************************************************************/
if(this.自動スクロールする_input_elem.checked === true){
this.下までスクロールする();
}
/************************************************************
ログの行数を表示する
/************************************************************/
this.ログの行数を表示する場所_span_elem.innerText = this.ログの行数;
/************************************************************
new!
ブラウザタイトルに、my_console.logの最終日時を出す
(フリーズしたことを、タスクマネージャーからすぐ発見できるように)
/************************************************************/
var タイトルの先頭に追加する日時_String = this.getDateオブジェクトを日本語表記にして返す_title用(new Date());
var 先頭に日時があるかもしれないtitle = document.title;
/* 2週目以降にtitleにどんどん日時が追加されていくのを防ぐために、日時があるならば削除しておく*/
var 先頭に日時の無いtitle = 先頭に日時があるかもしれないtitle.replace(/[0-9]{2}\/[0-9]{2} [0-9]{2}:[0-9]{2}/g, '');
document.title = タイトルの先頭に追加する日時_String + " " + 先頭に日時の無いtitle;
/************************************************************
GC!!
/************************************************************/
HTML = void 0;
style_elem = void 0;
削除する行数 = void 0;
TEMP_削除した行数 = void 0;
削除対象_elem_nodeList = void 0;
i = void 0;
削除対象_elem = void 0;
},
getDateオブジェクトを日本語表記にして返す_title用: function( Date ){
var date = Date;
var year = date.getFullYear();
var month = date.getMonth() + 1; /* monthは0始まりなので人間用にプラス1する */
var day = date.getDate();
var weeks = new Array('日','月','火','水','木','金','土');
var week = weeks[ date.getDay() ];
var hour = date.getHours();
var min = date.getMinutes();
var sec = date.getSeconds();
if(month < 10) { month = "0" + month; }
if(day < 10) { day = "0" + day; }
if(hour < 10) { hour = "0" + hour; }
if(min < 10) { min = "0" + min; }
if(sec < 10) { sec = "0" + sec; }
var string = `${month}/${day} ${hour}:${min}`;
return string;
},
getゼロ埋め: function(num,length){
return ('0000000000' + num).slice(-length);
},
get現在日時で使う_week_list: ['日','月','火','水','木','金','土'],
get現在日時: function(){
var now = new Date();
// var year = now.getFullYear();
var month = now.getMonth() + 1; /* monthは0始まりなので人間用にプラス1する */
var day = now.getDate();
var week = this.get現在日時で使う_week_list[ now.getDay() ];
var hour = now.getHours();
var min = now.getMinutes();
var sec = now.getSeconds();
var milliseconds = now.getMilliseconds();
month = this.getゼロ埋め(month, 2);
day = this.getゼロ埋め(day, 2);
hour = this.getゼロ埋め(hour, 2);
min = this.getゼロ埋め(min, 2);
sec = this.getゼロ埋め(sec, 2);
milliseconds = this.getゼロ埋め(milliseconds, 3);
now = void 0;
return `${month}/${day}(${week}) ${hour}:${min} ${sec}秒${milliseconds} `;
},
下までスクロールする: function(){
var topからの位置 = this.コンソールテキスト行_div_elem.scrollTop + 99999;
this.コンソールテキスト行_div_elem.scrollTo({
top: topからの位置,
behavior: "instant"
});
topからの位置 = void 0;
},
とあるelemに対してとあるcss_objを適用する: function(elem, css_obj){
for(var css名 in css_obj){
elem.style[css名] = css_obj[css名];
}
elem = void 0;
css_obj = void 0;
return true;
},
};/* end of my_console{} */
コードを改良して頂きました。爆速になりました
改良前と改良後のコードを比較するために、
ログを1万行出力するテストを行いました
▼結果
改良前の実行時間: 60227 msec(約60秒)
改良後の実行時間: 00093 msec(約0.1秒)
▼下記のテストコードを実行しました
var count = 0;
var start_Date = new Date();
for(let i = 0; i<10000; i++){
count++;
my_console.log(count + " 行目のログです", "yellow");
}
var end_Date = new Date();
my_console.log("▼改良後");
my_console.log(`実行時間: ${end_Date.getTime() - start_Date.getTime()} msec`);
改良前のコードの実行結果
@ss8826 さんから頂いた改良されたコードの実行結果
改良して頂いた事
- my_console.log()を実行する度に毎回 一時的な変数
var HTML = ''
を作っていましたが、それをオブジェクトプロパティとして作っておき、毎回再利用する - DOMにHTMLを追加するinsertAdjacentHTML()処理は、setTimeout()を使って非同期で行う。
感想
びっくりしました
次やる事
- 改良したコードをqiitaに投稿する
- まさか人様に見て頂けるなんて夢にも思っていなかったので、my_console.log()に関係のない自作関数を消したり、自分しか分からないメモやコメントを書きなおす
おまけ
改良して頂いたコードで、ログを9万行出力という恐ろしい処理を恐る恐る実行してみると、
たったの913msecで処理が終わりました。
私のコードで9万行出力テストをしたところ、5分待っても処理が終わらなかったため、ブラウザを再起動しました。