[C++] 334 BBS閲覧アプリの製作 その20 スクロールボタン配置 JavaScript

[M1 Mac, MacOS Ventura 13.3.1, clang 14.0.3]

HTML右端にスクロールボタンを配置し、最上部、最下部へ移動できるようにしました。ボタンはスクロールしても動きません。

Fl_Buttonで移動させたかったのですが、Fl_WebViewの子要素としてスクロールバーを把握する方法が分からないため、この手法にしました。

ここ数日結構根を詰めて取り組んだので、しばらくのんびりします。

振り返ればプログラミングを始めて3年半が経ちました。進度としてはまずまずだと思います。

    <html lang="ja">
    <HEAD>
    <meta charset="utf-8">
    <STYLE TYPE="text/css">
    A:link {    color: #0000ff;
                text-decoration: none; }
    A:visited { color: #008080;
                text-decoration: none; }
    A:hover {   color: #ff0000;
                text-decoration: underline; }
    body {
        font-family:"Helvetica","ヒラギノ角ゴ";
        margin-left: 20px;
    }

    .tips {
        position: absolute;
        top: 0px;
        left: 100px;
        visibility:hidden;
        background-color: #E0FFFF;
        margin-left:2%;
        padding:1em;
    }

    .rightTop-fixed-button {
        position: fixed;
        top: 0;
        right: 0;
        width: 50px;
        height: 20px;
        /* padding-top: 0px; */
        text-align: center;
        background: #FFFFFF;
        border-top: 4px solid #fff;
    }

    .rightTop-fixed-button button {
        font-size: 16px;
        cursor: pointer;
        vertical-align: middle;
    }

    .rightTop-fixed-button button:hover {
        opacity: 0.2;
    }

    .rightBtm-fixed-button {
        position: fixed;
        bottom: 10px;
        right: 0;
        width: 50px;
        height: 20px;
        /* padding-top: 0px; */
        text-align: center;
        background: #FFFFFF;
        border-top: 4px solid #fff;
    }

    .rightBtm-fixed-button button {
        font-size: 16px;
        cursor: pointer;
        vertical-align: middle;
    }

    .rightBtm-fixed-button button:hover {
        opacity: 0.2;
    }

    </STYLE>
    <script>
        function showPopup(id) {
            if(document.getElementById){
                var element = document.getElementById(id);
                element.style.visibility = (element.style.visibility == 'visible')? "hidden": "visible";

                var rect = element.getBoundingClientRect();
                var y = rect.top;
                console.log("Popup y = " + y);

                element.style.top = -y + 20;
            }
        }

        function moveTop(){
            scrollTo(0,0);
        }

        function moveBottom(){
            let scrollHeight = Math.max(
            document.body.scrollHeight, document.documentElement.scrollHeight,
            document.body.offsetHeight, document.documentElement.offsetHeight,
            document.body.clientHeight)

            scrollTo(0, scrollHeight);
        }
    </script>
    
    
    </HEAD>
    <BODY bgcolor="#ffffff">

    <div class="rightTop-fixed-button">
    <button onclick="moveTop();">|<</button>
    </div>
    <div class="rightBtm-fixed-button">
    <button onclick="moveBottom();">>|</button>
    </div>

<以下略>

[C++] 333 BBS閲覧アプリの製作 その19 レスアンカー先をポップアップ表示 Y座標の取得 JavaScript

[M1 Mac, MacOS Ventura 13.3.1, clang 14.0.3]

前回の設定ではポップアップのTOP位置が絶対座標のゼロだったため、スクロールすると見えなくなってしまいます。

ホバーした時のFl_WebView上端Y座標を基準にTOP設定することで常にGUI上にポップアップが出現するようにしました。

Google Chromeの検証画面でY座標を出力させながら動作確認しました。生成するHTMLはサイズが倍になりましたが、表示速度は体感では変わらずでした。

今回はJavaScript初学者にはキツい内容でした。4時間ほど掛かったでしょうか。ChatGPTはまあまあ貢献してくれました。

Chrome検証画面で動作確認
<html lang="ja">
    <HEAD>
    <meta charset="utf-8">
    <STYLE TYPE="text/css">
    A:link {    color: #0000ff;
                text-decoration: none; }
    A:visited { color: #008080;
                text-decoration: none; }
    A:hover {   color: #ff0000;
                text-decoration: underline; }
    body {
        font-family:"Helvetica","ヒラギノ角ゴ";
        margin-left: 20px;
    }

    .tips {
        position: absolute;
        top: 0px;
        left: 100px;
        visibility:hidden;
        background-color: #E0FFFF;
        margin-left:2%;
        padding:1em;
    }

    </STYLE>
    <script>
        function showPopup(id) {
            if(document.getElementById){
                var element = document.getElementById(id);
                element.style.visibility = (element.style.visibility == 'visible')? "hidden": "visible";

                var rect = element.getBoundingClientRect(); // ホバー時のFl_WebViewを取得する
                var y = rect.top; // 上端のY座標を取得する
                console.log("Popup y = " + y); // Y座標を出力

                element.style.top = -y + 20; // Y座標に-1を掛け20px下へずらした位置をTOPとする          
            }
        }
    </script>
<以下略>
// GUI表示用
<p><font size="2" color="#008080">5 : 投稿者名 <br>
</font><font size="2" color="#000080">2023/07/22(土) 18:48:05.94 ID:xxx<br>
</font><font size="2" color="#000B00"> <span onmouseover = "showPopup(2);" onmouseout = "showPopup(2);">
<a href="../test/read.cgi/xxx/2" rel="noopener noreferrer" target="_blank">>>2</a>
</span> <br> 本文 </font></p>

// ポップアップ用
<p id="5" class="tips"><font size="2" color="#008080">5 : 投稿者名 <br>
</font><font size="2" color="#000080">2023/07/22(土) 18:48:05.94 ID:xxx<br>
</font><font size="2" color="#000B00"> <span onmouseover = "showPopup(2);" onmouseout = "showPopup(2);">
<a href="../test/read.cgi/xxx/2" rel="noopener noreferrer" target="_blank">>>2</a>
</span> <br> 本文 </font></p>

[C++] 332 BBS閲覧アプリの製作 その18 レスアンカー先をポップアップ表示 JavaScript

[M1 Mac, MacOS Ventura 13.3.1, clang 14.0.3]

Fl_WebViewではSTYLEタグやJavaScriptを使えます。

レスアンカーをホバーするとアンカー先の内容をポップアップするようにしました。

現時点ではFl_WebView表示用とポップアップ用を作成する必要があるので単純にHTMLの全体量は倍になります。

まだ検証していないのですが、表示速度に大きな影響があれば改良を検討します。

<html lang="ja">
    <HEAD>
    <meta charset="utf-8">
    <STYLE TYPE="text/css">
    A:link {    color: #0000ff;
                text-decoration: none; }
    A:visited { color: #008080;
                text-decoration: none; }
    A:hover {   color: #ff0000;
                text-decoration: underline; }
    body {
        font-family:"Helvetica","ヒラギノ角ゴ";
        margin-left: 20px;
    }

    .tips {
        position: absolute;
        top: 20px;
        left: 100px;
        visibility:hidden;
        background-color: #E0FFFF;
        margin-left:2%;
        padding:1em;
    }

    </STYLE>
    <script>
        function showPopup(id) {
            if(document.getElementById){
                var element = document.getElementById(id);
                element.style.visibility = (element.style.visibility == 'visible')? "hidden": "visible";
            }
        }
    </script>
<以下略>

[JavaScript] 15 Adobe XDのアイテムデータを取得するプラグイン作成 その3 テキスト出力

[M1 Mac, Monterey 12.6.3]

前回の続きです。

Pythonでは区切り文字付きテキストの方がリストのリテラル(機能のないただの文字列)より扱いやすいため、プラグインの内容を修正しました。

これでPythonスクリプトによりアイテムデータをリテラルのリストとして取得できます。あとは各要素から必要な数値を抽出してJSONに変換します。

JavaScriptの方は一応完成になります。

function myCommand(selection) {
    console.log(selection.items.length + " items are selected");

    let items_list = "";
    var num = 1;
    selection.items.forEach(function(value){
        // console.log(value);
        if (num < selection.items.length){
            items_list += value + ";";
        } else {
            items_list += value;
        }
        num += 1;
    });
    console.log(items_list);
}

module.exports = {
    commands: {
        GetItemXY: myCommand
    }
};
with open("items.txt", "r") as tf:
    items_list = tf.read().replace("\n","").split(';')
    
print(items_list)
print(len(items_list))

# ここからlistをJSONに変換する

[JavaScript] 14 Adobe XDのアイテムデータを取得するプラグイン作成 その2 リスト出力

[M1 Mac, Monterey 12.6.3]

Adobe XDのプラグインからアイテムデータをファイル出力するのは私の技量では難しいため早々にあきらめて、Python用のリストとして開発者コンソールに出力するようにしました。

これをPythonスクリプトにコピー&ペーストしてC++コードを書き換えようという算段です。

JavaScriptはローカル環境とのファイルのやり取りが不得手なところがあるので難しいことをやろうとせず、ここだけ手動でさっさとPythonに渡してしまうのが得策だと考えました。

アイテムデータをPython用リストとして出力
function myCommand(selection) {
    console.log(selection.items.length + " items are selected");

    let items_list = "";
    var num = 1;
    selection.items.forEach(function(value){
        // console.log(value);
        if (num == 1){
            items_list += "[\"" + value + "\",\n";
        } else if (num < selection.items.length){
            items_list += "\"" + value + "\",\n";
        } else {
            items_list += "\"" + value + "\"]\n";
        }
        num += 1;
    });
    console.log(items_list);
}

module.exports = {
    commands: {
        GetItemXY: myCommand
    }
};

[JavaScript] 13 Adobe XDのアイテムデータを取得するプラグイン作成 その1

[M1 Mac, Monterey 12.6.3]

ビデオ編集アプリのGUIデザインにはAdobe XDを使っています。

GUI内アイテムの座標と幅・高さをプラグインで取り出して、C++のコードを書き換えるところまで自動化しようとしています。

とりあえずAdobe XDのプラグイン開発環境を構築し、アイテムのデータ所在を確認するところまでは出来ました。スクリプト自体は簡単に書けたので拍子抜けでした。

これらをJSONファイルとして取り出し、PythonでJSON読込・コード書き換えができれば完成になります。

Adobe XD GUIデザイン
GUI内アイテムデータの出力
function myCommand(selection) {
    console.log(selection.items.length + " items are selected");

    selection.items.forEach(function(value){
        console.log(value);
    });
}

module.exports = {
    commands: {
        GetItemXY: myCommand
    }
};

[JavaScript] 12 submitボタンに動作を追加する

[M1 Mac, Big Sur 11.7.2]

検索ボタンを押すとMySQLへログインして検索するよう設定していますが、同時にストップウォッチが動作するようにしました。

<form method="post" name="formSearch">
    <input id="button2" type="submit" name="button2" value="検索"
    style="color:blue; width:80px; height:40px; font-size:18px; background-color:RGB(211,203,198);
    border:solid; border-color:RGB(30,144,255);">
</form>
<br>

<script language="javascript" type="text/javascript">
    const button2 = document.getElementById('button2');

    button2.addEventListener('click',(e) => {
        startWatch();
        document.cookie="watch=1;";
    });
</script>

<中略>

<span id="elapsedTime"></span>
<br>

<script language="javascript" type="text/javascript">
    function getCookieValue(key) {
        const cookies = document.cookie.split(';');
        for (let cookie of cookies) {
            var cookiesArray = cookie.split('='); 
            if (cookiesArray[0].trim() == key.trim()) { 
                return cookiesArray[1];
            }
        }
        return '';
    }

    function showElapsedTime(time) {
        var now = new Date();
        const eTime = Math.floor((now - time)/1000);
        document.cookie="time=" + eTime + ";";

        var eTimeShow = "検索時間(秒) " + eTime;
        document.getElementById("elapsedTime").innerHTML = eTimeShow;

        return eTimeShow;
    }

    function startWatch(){
        var startTime = new Date();

        setInterval(
            function()
            {showElapsedTime(startTime);}
            ,1000 ) ;
    }

    var watchBool = getCookieValue("watch");
    if (watchBool == 1){
        let time = getCookieValue("time");
        var eTimeShow = "検索時間(秒) " + time;
        console.log(eTimeShow);
        document.getElementById("elapsedTime").innerHTML = eTimeShow;
    }
</script>

[JavaScript] 11 プログレスバー Fetch API

[M1 Mac, Big Sur 11.7.2]

懸案のプログレスバー表示ですが、PHPの簡易webサーバ側(PHPファイルと同じディレクトリ)にMySQL検索の進捗率を格納したテキストファイルを置いて定期的に読み込もうとしたもののデータを更新できず上手くいきませんでした。記録として残しておきます。

以前JavaScriptの学習を断念した原因であるHTTP通信に取り組まざるを得なくなりました。

様々な検証を通して、根本的にMySQL検索中は最新テキストファイルへのアクセスができないことが判明しました。

仕様なのかマシンスペックなのか何が原因なのかは良く分かりません。少なくとも今の環境ではどう手立てを施してもできないのかもしれません。

ここ数日かなりのめり込み愛着もわいてきましたのでJavaScriptと今後決別することはありませんが、この程度の動作も簡単には出来ないとなると使用頻度は低いままに終わりそうです。MySQL検索はターミナルでのPython実行で事足りますから。

直前試行の残データを読み込んだまま更新されない
<progress class="progress" value="0" id="pb">
</progress>
<div id="rate"></div>

<script type="text/javascript">
    async function showRate() {
        let response = await fetch("./files/rate.txt");
        var rateID = document.getElementById('rate')

        if (response.ok) {
            var rate = await response.text();
            rateID.textContent = rate;
            console.log("rate: %s", rate);
        }
        else {
            alert("HTTP-Error: " + response.status);
        }

        document.getElementById("pb").value = rate;
        
    };

    // Fetch APIの使用可否確認
    if(window.fetch ) {
	    console.log("fetch使用可能");
    } else {
	    console.log("fetch使用不可");
    }

    setInterval('showRate()', 1000);
</script>

[JavaScript] 10 cookieの使用 検索時間の記録

[M1 Mac, Big Sur 11.7.2]

MySQLアプリの検索時間をcookieに都度記録し、検索が完了してリロードされた時に最終記録を表示するようにしました。意味がないのですがプログレスバーはいきなり最後だけ100%で表示されます。

PHPもJavaScriptも初心者なので、HTMLを加えた3者が混在するPHPファイルを書くのは骨が折れます。どちらもそれなりに書けるようになるとスクリプト言語はPythonを合わせた3言語を学んだことになります。

Rubyも学んでみたいですが、それにしかできないことでもないと着手しそうにないですね。

MySQL検索終了時
<input id="checkBox" type="checkbox" name="checkBox" value="開始" onClick="startTimer()"/>

<span id="elapsedTime"></span>

<script language="javascript" type="text/javascript">

    function getCookieValue(key) {
        const cookies = document.cookie.split(';');
        for (let cookie of cookies) {
            var cookiesArray = cookie.split('='); 
            if (cookiesArray[0].trim() == key.trim()) { 
                return cookiesArray[1];
            }
        }
        return '';
    }

    function showElapsedTime(time) {
        var now = new Date();
        const eTime = Math.floor((now - time)/1000);
        document.cookie="time=" + eTime + ";";
        console.log(eTime);

        var eTimeShow = "検索時間(秒) " + eTime;
        console.log(eTimeShow);
        document.getElementById("elapsedTime").innerHTML = eTimeShow;

        return eTimeShow;
    }

    function startTimer(){
        var startTime = new Date();

        setInterval(
            function()
            {showElapsedTime(startTime);}
            ,1000 ) ;
    }

    try {
        let checkBox = document.getElementById('checkBox');
        console.log("checkBox: %b", checkBox.checked);

        if (checkBox.checked == false){
            var time = getCookieValue("time");
            var eTimeShow = "検索時間(秒) " + time;
            console.log(eTimeShow);
            document.getElementById("elapsedTime").innerHTML = eTimeShow;
        }
    }
    catch(e){
    }
</script>

[JavaScript] 09 cookieの使用

[M1 Mac, Big Sur 11.7.2]

publicな変数の扱い方が全く分からないので、苦肉の策としてcookieに変数を格納し共有することにしました。

PHP、JavaScript、HTMLが混在するスクリプト内、スクリプト間でどうやって変数の受け渡しをしているのでしょうか。この点においてはC++やJavaよりも難易度が相当高いように思います。

プログレスバーについてもcookieを使って何とかできないか探っていきます。

MySQL検索終了時
<html>
<?php
# MySQLにログイン
$conn = new mysqli('localhost', 'root', 'root');
if (!$conn){
	die("MySQL接続に失敗しました");
} else {
	// print 'MySQL接続に成功しました';
}

$dbs_query = $conn->query('show databases;');
$dbs = $dbs_query->fetch_all(MYSQLI_ASSOC);

$dbs_count = count($dbs);

?>
<head>
	<meta charset="utf-8"/>
	<title>MySQL SEARCHER</title>
	<link rel="shortcut icon" href="favicon.ico">
	
</head>
<body>
データベース、カラム名、検索語を入力してください
<form id="form_submit" action="output.php" method="post">
	<table border="1">
		<tr>
			<td>DB</td>
			<td><input id="input1" type="text" name="db"></td>
		</tr>
	</table>
	<tr>
	<?php
	$num = 0;
	foreach ($dbs as $db) {
		if ($num == 0){
			echo "DB : ";
		}
		echo implode($db);
		if ($num != $dbs_count - 1){
			echo ", ";
		}
		$num = $num + 1;
		
	}
	?>
	</tr>
	<table border="1">
		<tr>
			<td>COL</td>
			<td><input id="input2" type="text" name="col"></td>
		</tr>

	</table>
	<table border="1">
		<tr>
			<td><input id="input3" type="text" name="key"></td>
			<td colspan="2" align="center">
			<input id="button" type="submit" value="検索">
			</td>
		</tr>
	</table>
</form>

<progress class="progress" value="0" id="pb">
</progress>
<br>
<br>

<span id="elapsedTime"></span>
<br>

<span id="clockTime"></span>
<script type="text/javascript">
	function set2fig(num) {
		var ret;
		if( num < 10 ) { ret = "0" + num; }
		else { ret = num; }
	return ret;
	}

	function showTime() {
		var now = new Date();
		var nowhour = set2fig(now.getHours());
		var nowminutes = set2fig(now.getMinutes());
		var nowseconds = set2fig(now.getSeconds());
	
		var clockTime = "現在時刻 " + nowhour + ":" + nowminutes + ":" + nowseconds;
		console.log(clockTime);
		document.getElementById("clockTime").innerHTML = clockTime;
    
    	return clockTime;
  	}
	
	setInterval('showTime()', 1000);
</script>
<br>

<span id="loadCount"></span>
<script language="javascript" type="text/javascript">
	var cnt;        // 読込回数

	function getCookieValue(key) {
		const cookies = document.cookie.split(';');
		for (let cookie of cookies) {
			var cookiesArray = cookie.split('='); 
			if (cookiesArray[0].trim() == key.trim()) { 
				return cookiesArray[1];
			}
		}
		return '';
	}

	// cookie使用可否確認
	if (navigator.cookieEnabled){
		console.log("cookie使用可能");
		
		// 読込回数をHTMLに表示
		if (!document.cookie){
			document.getElementById("loadCount").innerHTML = "1回目の読込";
			document.cookie="counts=1;";  
		}
		else{
			try{
				const cnt_ck = getCookieValue("counts");
				// 読み込み回数をインクリメント
				cnt = parseInt(cnt_ck) + 1;
				document.getElementById("loadCount").innerHTML = cnt + "回目の読込";
				
				//cookieに読込回数書き込み
				document.cookie="counts=" + cnt + ";";
			}
			catch(e){
				document.write("読込回数の取得に失敗しました。");
			}
		}
	}
	else{
		document.write("cookieが使用できません。");
		console.log("cookie使用不可");
	}
		
	if (cnt != 2){
		var now = new Date();
		
		function showElapsedTime() {
			var now2 = new Date();
			const eTime = Math.floor((now2 - now)/1000);
			console.log(eTime);

			// cookieのmaxTimeを都度更新する
			let maxTimeStr = "maxTime=" + eTime + ";";
			document.cookie = maxTimeStr;  

			var eTimeShow = "経過時間(秒) " + eTime;
			console.log(eTimeShow);
			document.getElementById("elapsedTime").innerHTML = eTimeShow;

		return eTimeShow;
		}

		setInterval('showElapsedTime()', 1000);

	} else if (cnt = 2){
		let maxTime_ck = getCookieValue("maxTime");
		var eTimeShow = "経過時間(秒) " + maxTime_ck;
		console.log(eTimeShow);

		// 2回目の読込時は1回目の最大値で固定表示
		document.getElementById("elapsedTime").innerHTML = eTimeShow;
	}

</script>
<br>
<br>

</body>
</html>

参考サイト