RM-BLOG

IT系技術職のおっさんがIT技術とかライブとか日常とか雑多に語るブログです。* 本ブログに書かれている内容は個人の意見・感想であり、特定の組織に属するものではありません。/All opinions are my own.*

【javascript】文字列変数に改行をいれるとイカれる

javascriptの文字列変数にソースコード上でそれとわかるような形で改行いれるとページ全体がなんかおかしなことになる。
少なくとも、その変数使ってるjavascriptは動かなくなるようだ。
まあ普通に考えるとそういうもんだろう。
個人的な感覚でいうと、変数に固定の文字列埋め込むケースではコーディングミス(誤記、というか)以外に考えられらない。
だから普通発生しないはずである。
ただ実際に発生して困ったので備忘録として残す。


 

 


HTMLの具体的な実例で見てみると。

(1)固定値埋め込み:改行なし

 

<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
		<meta http-equiv="Content-Script-Type" content="text/javascript">
		
		<script type="text/javascript">
		<!--
			var strVal = "あいうえお";
			
			function alertOn() {
				alert(strVal);
			}
		// -->
		</script>

		<title>Test Js Alert(No Line)</title>

	</head>
	<body>
		<h1>■改行なしの文字列を表示します。</h1><br/>
		<br/>
		<input type="button" value="ここをクリックするとjs変数を表示します。" onClick="alertOn()">
	</body>
</html>


(2)固定値埋め込み:改行あり

 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
		<meta http-equiv="Content-Script-Type" content="text/javascript">
		
		<script type="text/javascript">
		<!--
			var strVal = "
あいうえお";
			
			function alertOn() {
				alert(strVal);
			}
		// -->
		</script>

		<title>Test Js Alert(No Line)</title>

	</head>
	<body>
		<h1>■改行ありの文字列を表示します。</h1><br/>
		<br/>
		<input type="button" value="ここをクリックするとjs変数を表示します。" onClick="alertOn()">
	</body>
</html>


(3)自ページ内のテキストエリアから設定

 

<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
		<meta http-equiv="Content-Script-Type" content="text/javascript">
		
		<script type="text/javascript">
		<!--
			
			function alertOn() {
				var strVal = document.getElementById("TEXTAREA_FREE").value;
				alert(strVal);
			}
		// -->
		</script>

		<title>Test Js Alert(Free Input)</title>

	</head>
	<body>
		<h1>■自由入力の値を表示します。</h1><br/>
		<br/>
		ここ↓に入力した値を表示します。<br/>
		<textarea width="300" height="200" id="TEXTAREA_FREE"></textarea>
		<br/>
		<input type="button" value="ここをクリックするとjs変数を表示します。" onClick="alertOn()">
	</body>
</html>




それぞれボタンをクリックすると変数値をalertで表示するだけのシンプルなHTMLである。
(1)は言わずもがな、全然問題ない。(普通に"あいうえお"が表示される)
(3)は表示する値をTEXTAREAから取得するため、TEXTAREAへの入力内容次第では当然改行が入ることもあるが、
仮に改行をいれて入力していても問題なくalertで表示できる。(改行もちゃんと表現された状態でalertされる、いわゆる"あいうえお\nかきくけこ"とかと同じ扱いになる)
ただし、(2)はボタンを押しても反応がない。
Internet Explorerの開発ツールによれば、ボタンクリック時に起動しているjavascriptの関数「alertOn()」が未定義で呼び出せない。
関数定義のブロックと変数に値を埋め込んでいる箇所はコード上で異なるが、
意図的に改行をいれてしまったことでjavascript全体がイカれてしまっている。

冒頭で言った”変数に固定の文字列埋め込むケースではコーディングミス(誤記、というか)以外に考えられらない。”というのは
上に挙げた例でいうところの(2)にあたるケースで、
要するにこういう書き方↓は普通は(=意図的には)しないよねという意味である


<script type="text/javascript">
<--
var STR_VAL = "あいうえお ←わざわざここで改行なんかしないだろjk…という意味
かきくけこ";
// -->
</script>





↑までの話ならただのコーディングミスで話が付くのだが、
実際に遭遇した時はjspで変数設定している箇所に原因があった。
簡単な実例として、
「HTMLのTEXTAREAに入力された値をPOSTして、JSPにFORWARDした後、受け取った値をjavascriptでalert表示させる」
ようなものを考える。

(4)入力元HTML

 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
	<title>Test Textarea</title>
	
	<meta HTTP-EQUIV='Cache-Control' CONTENT='no-cache'>
	<meta HTTP-EQUIV='Pragma' CONTENT='no-cache'>
	<meta HTTP-EQUIV='Expires' CONTENT='-1'>

</head>
<body>
	<h1>■最初の画面です。</h1><br/>
	<br/>
	<form action="/linecodetest/TestServlet" method="post">
		ここになんか入力↓<br/>
		<textarea width="300" height="30" name="textarea_test" id="textarea_test">
		</textarea>
		<br/>
		<input type="submit" value="GO! Submit!!">
	</form>
</body>


(5)間にいるServlet

 

import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.*;
import java.text.*;
import java.net.*;
import javax.mail.internet.*;

public class TestServlet extends HttpServlet {
	
	private static final DateFormat LOG_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
	
	private static final String ENCODING_UTF8 = "UTF-8";
	
	private static final String STR_PARAM_TEXTAREA_TEST = "textarea_test";
	private static final String STR_URL = "/jsp/forward.jsp";
	
	public void doGet(HttpServletRequest req , HttpServletResponse res) throws ServletException,IOException {
		this.doProcess(req,res);
	}
	
	public void doPost(HttpServletRequest req , HttpServletResponse res) throws ServletException,IOException {
		this.doProcess(req,res);
	}

	private void doProcess(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException {
    try {
    	String strVal = request.getParameter(STR_PARAM_TEXTAREA_TEST);
    	
    	if (strVal == null) {
    		throw new ServletException(STR_PARAM_TEXTAREA_TEST + " is null");
    	}

    	printLog("**** Debug Print -START- ****");
    	printLog(STR_PARAM_TEXTAREA_TEST + " = " + strVal);
    	printLog("**** Debug Print -E N D- ****");
    	
    	HttpSession hs = request.getSession();
    	
    	hs.setAttribute(STR_PARAM_TEXTAREA_TEST,strVal);
    	RequestDispatcher rd = request.getRequestDispatcher(STR_URL);
    	rd.forward(request,response);
		} catch(ServletException e) {
			throw e;
    	}
   }
	
	private static void printLog(Object msg) {
		StringBuilder sb = new StringBuilder();
		sb.append(LOG_FORMAT.format(new Date()));
		sb.append(" ");
		if (msg != null) {
			sb.append(msg.toString());
		}
		
		System.out.println(sb.toString());
	}

}


(6)forward先JSP

 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
	<title>Test forward</title>
	
	<meta HTTP-EQUIV='Cache-Control' CONTENT='no-cache'>
	<meta HTTP-EQUIV='Pragma' CONTENT='no-cache'>
	<meta HTTP-EQUIV='Expires' CONTENT='-1'>
	
	<script type="text/javascript">
	<!--
		var REQ_VAL = '<%= session.getAttribute("textarea_test") %>';
		function alertOn() {
			alert("前画面からもらった値は\n" + REQ_VAL);
		}
		
	// -->
	</script>

</head>
<body>
	<h1>■forward先画面です。</h1><br/>
	<br/>
	<input type="button" value="ここを押すと前画面でもらった値を表示します。" onClick="alertOn();">
</body>



(6)はボタンクリックでjavascriptの変数「REQ_VAL」をalertに表示させるが、
この値はHttpSession#getAttributeで受け取った後にHTMLのソースコードとして展開されるため、
HttpSession#getAttributeの値次第ではここで改行が起き、変数の設定箇所が2行に渡るコードになり、(2)と同じ状態になる。
結果的にjavascriptが動作しない…という現象が起きる。

ことこのケースでいえば、
HttpSession#getAttributeで取得できる値を”入力する(設定する)側”は(4)で、それを表示するのが(6)という構成なので、
つまり自分で入力して自分でコードをブッ壊しているという間抜けな状況だが
この項目の取得元が「外部システムに依存していてデータの仕様がよくわからない」というようなケースでは結構こういうことも起き得るだろう。
(そもそもデータの仕様を確認せぬまま実装してはいけないけど…)
まあこういう危険性をはらんでる時点で、改行含むかどうかに関わらずこういうコーディングをしてはいけないのだろう。