1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

JavaScriptのconsole.logを作ってみた

Last updated at Posted at 2023-04-07

概要

デバック情報などをブラウザ上に表示する my_console.log()を作りました

使い方

my_console.log("ログを表示する");
my_console.log("背景色を指定してログを表示する", "red");
my_console.log("りんごは\n赤いです", "yellow");

55.PNG

ソースコード

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.ログ_上限数}行&nbsp;現在の行数:<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`);

改良前のコードの実行結果

改良前.PNG

@ss8826 さんから頂いた改良されたコードの実行結果

改良後.PNG

改良して頂いた事

  • my_console.log()を実行する度に毎回 一時的な変数var HTML = '' を作っていましたが、それをオブジェクトプロパティとして作っておき、毎回再利用する
  • DOMにHTMLを追加するinsertAdjacentHTML()処理は、setTimeout()を使って非同期で行う。

感想

びっくりしました

次やる事

  • 改良したコードをqiitaに投稿する
  • まさか人様に見て頂けるなんて夢にも思っていなかったので、my_console.log()に関係のない自作関数を消したり、自分しか分からないメモやコメントを書きなおす

おまけ

改良して頂いたコードで、ログを9万行出力という恐ろしい処理を恐る恐る実行してみると、
たったの913msecで処理が終わりました。
99.PNG

私のコードで9万行出力テストをしたところ、5分待っても処理が終わらなかったため、ブラウザを再起動しました。

1
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?