【JavaScript】入力フォームを使ったHTMLページをスッキリした見た目にする


コードだけをみたい人は最後に全体を載せてますのでそちらへ。

それでは本題の前に、
まずは普通にhtmlでselect要素1つ、input-text要素1つを設置します。

```HTML
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>text and select form</title>
</head>
<body>
	<select>
     	<option value="2021">2021</option>
	     <option value="2022">2022</option>
 	    <option value="2023">2023</option>
  	</select>年
 	<br><br>
	<input type="text">
</body>
</html>
```

上のHTMLをブラウザで見るとselect要素とinput-text要素が表示されます。

これらを多用して「入力ページ」を作るのはいいのですが、
そのページを印刷してそのまま利用するわけにはいきません。

フォームを使ったページを印刷すると、
行がズレる、フォント・大きさ・ベースラインが合わない、見た目のイメージが変わる、
といったことが起こるからです。

HTMLの入力フォームは印刷してみるとWYSIWYGじゃないんです。

そこで、
「入力フォームを使ったHTMLページをJavaScriptでスッキリした見た目にする」
ということを考えました。

目指すのは、
文字列クリック　→　入力フォームが出現　→　フォーム入力終了　→　フォーム消滅　→　文字列に戻る
です。

まずは、HTMLを書き換えます。

```HTML
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>text and select form</title>
</head>
<body>
	<!--spanで囲みます-->
	<span>2021</span>年
 	<br><br>
	<!--spanで囲みます-->
	<!--text入力変更前の文を適当に-->
	<span>textを書き換えます</span>
</body>
</html>
```

上のHTMLをブラウザで見ると「2021年」「textを書き換えます」がそれぞれ文字列で現れています。

次に、Javascriptで扱えるようにspanタグに「id」と「onclick」イベントハンドラを配置します。
「onclick」で起動する関数は後ほど配置します。

```HTML
<html>
<head>
<meta charset="UTF-8">
<title>text and select form</title>
</head>
<body>
	<!--「id」と「onclick」を配置します-->
	<span id="year" onclick="">2021</span>年
 	<br><br>
	<!--「id」と「onclick」を配置します-->
	<span id="line" onclick="">textを書き換えます</span>
</body>
</html>
```

次に、Javascriptの本体を書き込むscriptタグを、今回はわかりやすいようにspanタグの直下にそれぞれ配置します。

```HTML
<html>
<head>
<meta charset="UTF-8">
<title>text and select form</title>
</head>
<body>
	<span id="year" onclick="">2021</span>年
	<!--scriptタグを配置します-->
 	<script>
 	</script>
<br><br>
	<span id="line" onclick="">textを書き換えます</span>
	<!--scriptタグを配置します-->
 	<script>
 	</script>
</body>
</html>
```

続いて、最初のscriptタグにspan id="year"用のJavaScriptを書きます。


```HTML
    <span id="year" onclick="">2021</span>年
    <script>
        let spanYearTag=document.getElementById("year");
        let optionsArray=[2021,2022,2023];//select要素のoptionsの配列です

        /*span id="year"のonclickで起動する関数です*/
        function toSelect(){
            let keepNen=Number(spanYearTag.innerHTML); //span id="year"の内容を(一応数値化して)変数に格納

            /*select要素を構築します*/
            let selectElement='<select id="year-select" onchange="fromSelect()">';//idとonchangeを書きます
            for(let i=0; i<optionsArray.length; i++){//optionsArray配列からselect要素のoptionsを書きます
                selectElement+='<option value="'+optionsArray[i]+'">'+optionsArray[i]+'</option>';
            }         
            selectElement+='</select>';
            
            /*span id="year"にselect要素を配置します*/
            spanYearTag.innerHTML=selectElement;//ここでselect要素が実体化します
            
            /*表示されていた「年」と同じoptionsをselectedにします*/
            for(let i=0; i<optionsArray.length; i++){//
                if(keepNen==optionsArray[i]){
                    document.getElementById("year-select").options[i].selected=true;
                    break;
                }
            }
            
            /*span id="year"のonclickハンドラを無効にします*/
            spanYearTag.onclick=null;//spanタグ内にselectタグあるので、selectタグへのクリックが上位spanタグに影響しないようにするためです
        }

        /*select id="year-select"のonchangeで起動する関数です*/
        function fromSelect(){//
            let selectTag=document.getElementById("year-select");
            let selectNen=selectTag.value;//selectで選択されたvalueを格納
            
            /*span id="year"に選択された年を配置します*/
            spanYearTag.innerHTML=selectNen;//ここでselect要素と選択された「年」が入れ替わります

            /*span id="year"にonclickを新たに配置します*/
            spanYearTag.onclick=new Function("toSelect()");//nullにしたものを復活です
        }
    </script>
```

上記のJavaSciptのtoSelect関数をspan id="year"のonclickに配置します。

```HTML
<html>
<head>
<meta charset="UTF-8">
<title>text and select form</title>
</head>
<body>
	<!--toSelect関数をspan id="year"のonclickに設置します-->
	<span id="year" onclick="toSelect()">2021</span>年
 	<script>
 		/*--略--*/
	</script>
<br><br>
	<span id="line" onclick="">textを書き換えます</span>
 	<script>
 	</script>
</body>
</html>
```

次に、二つ目のscriptタグにspan id="line"用のJavaScriptを書きます。

```HTML
<script>
        let spanInputTag=document.getElementById("line");

        /*span id="line"のonclickで起動する関数です*/
        function toText(){
            let keepLine=spanInputTag.innerHTML; //span id="line"の内容を変数に格納。

            /*text要素を構築します*/
            let textElement='<input type="text" id="line-text" value="'+keepLine+'"  onblur="fromText()">';//idとonchangeを書きます valueに変更前の文字列を設定します
            
            /*span id="line"にtext要素を配置します*/
            spanInputTag.innerHTML=textElement;//ここでtext要素が実体化します

            /*text要素にフォーカスします*/
            document.getElementById("line-text").focus();
            document.getElementById("line-text").select();
            /*span id="line"のonclickハンドラを無効にします*/
            spanInputTag.onclick=null;//spanタグ内にselectタグあるので、selectタグへのクリックが上位spanタグに影響しないようにするためです
        }	

        /*input text id="line-text"のonblurで起動する関数です*/
        function fromText(){//
            let textTag=document.getElementById("line-text");
            let getLine=textTag.value;//textで入力されたvalueを格納
            
            /*span id="line"に入力された文字列を配置します*/
            spanInputTag.innerHTML=getLine;//ここでtext要素と入力された文字列が入れ替わります

            /*span id="line"にonclickを新たに配置します*/
            spanInputTag.onclick=new Function("toText()");//nullにしたものを復活させます
        }
    </script>
```

上記のJavaSciptのtoText()関数をspan id="line"のonclickに設置します。

```HTML
<html>
<head>
<meta charset="UTF-8">
<title>text and select form</title>
</head>
<body>
	<span id="year" onclick="toSelect()">2021</span>年
 	<script>
 		/*--略--*/
	</script>
<br><br>
	<!--toText関数をspan id="line"のonclickに設置します-->
	<span id="line" onclick="toText()">textを書き換えます</span>
 	<script>
		/*--略--*/
 	</script>
</body>
</html>
```

HTMLとJavaScript全体です。

```HTML
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>text and select form</title>
</head>
<body>
	<span id="year" onclick="toSelect()">2021</span>年
    <script>
        let spanYearTag=document.getElementById("year");
        let optionsArray=[2021,2022,2023];//select要素のoptionsの配列です

        /*span id="year"のonclickで起動する関数です*/
        function toSelect(){
            let keepNen=Number(spanYearTag.innerHTML); //span id="year"の内容を(一応数値化して)変数に格納

            /*select要素を構築します*/
            let selectElement='<select id="year-select" onchange="fromSelect()">';//idとonchangeを書きます
            for(let i=0; i<optionsArray.length; i++){//optionsArray配列からselect要素のoptionsを書きます
                selectElement+='<option value="'+optionsArray[i]+'">'+optionsArray[i]+'</option>';
            }         
            selectElement+='</select>';
            
            /*span id="year"にselect要素を配置します*/
            spanYearTag.innerHTML=selectElement;//ここでselect要素が出現します
            
            /*表示されていた「年」と同じoptionsをselectedにします*/
            for(let i=0; i<optionsArray.length; i++){//
                if(keepNen==optionsArray[i]){
                    document.getElementById("year-select").options[i].selected=true;
                    break;
                }
            }
            
            /*span id="year"のonclickハンドラを無効にします*/
            spanYearTag.onclick=null;//spanタグ内にselectタグがあるので、selectタグへのクリックが上位のspanタグに影響しないようにしています
        }

        /*select id="year-select"のonchangeで起動する関数です*/
        function fromSelect(){//
            let selectTag=document.getElementById("year-select");
            let selectNen=selectTag.value;//selectで選択されたvalueを変数に格納
            
            /*span id="year"に選択された年を配置します*/
            spanYearTag.innerHTML=selectNen;//ここでselect要素と選択された「年」が入れ替わります

            /*span id="year"にonclickを新たに配置します*/
            spanYearTag.onclick=new Function("toSelect()");//nullにしたものを復活です
        }
    </script>
<br><br>
	<span id="line" onclick="toText()">textを書き換えます</span>
<script>
        let spanInputTag=document.getElementById("line");

        /*span id="line"のonclickで起動する関数です*/
        function toText(){
            let keepLine=spanInputTag.innerHTML; //span id="line"の内容を変数に格納。

            /*text要素を構築します*/
            let textElement='<input type="text" id="line-text" value="'+keepLine+'"  onblur="fromText()">';//idとonchangeを書きます valueに変更前の文字列を設定します
            
            /*span id="line"にtext要素を配置します*/
            spanInputTag.innerHTML=textElement;//ここでtext要素が出現します

            /*text要素にフォーカスします*/
            document.getElementById("line-text").focus();
            document.getElementById("line-text").select();
            /*span id="line"のonclickハンドラを無効にします*/
            spanInputTag.onclick=null;//spanタグ内にselectタグあるので、selectタグへのクリックが上位spanタグに影響しないようにしています
        }	

        /*input text id="line-text"のonblurで起動する関数です*/
        function fromText(){//
            let textTag=document.getElementById("line-text");
            let getLine=textTag.value;//textで入力されたvalueを格納
            
            /*span id="line"に入力された文字列を配置します*/
            spanInputTag.innerHTML=getLine;//ここでtext要素と入力された文字列が入れ替わります

            /*span id="line"にonclickを新たに配置します*/
            spanInputTag.onclick=new Function("toText()");//nullにしたものを復活させます
        }
    </script>
</body>
</html>
```

さて、input-text要素に関してですが、
上記のJavaScriptでは、onblurで入力フォームが消えるようにしていますが、
onkeydownでEnterキーをトリガーにすることもできます。

更に上記のinput-text要素は、
value=""で入力フォームが消えてしまった場合の回避措置がありません。
value=""で消えてしまうとクリックすべき文字列がなくなりますので、次はもうクリックできません。
実際に使う場合は、value=""消えようとする時にアラート出すなりの回避措置が必要です。