« 2021年10月 | メイン | 2022年1月 »

2021年11月

2021年11月26日 (金)

Select Option の代替品(10)

Window 幅を直前のページから受け取り、それを基準にしていたのですが、ウインドウの横幅を大きくしたり小さくしたり・・があるんですよね。幅を縮小するとページ作成を再実行しようが何をしようがうまくいかない。当たり前なんだけど、こういうのって直面するまで気づかないのは「よくある事」で(なに、お前が不注意なだけだって??)。

で、どうするかと言うと、ページ起動時に window 幅と「サーバー側にある」幅を示すパラメータ値を取得して実際の幅を決める方法です。似たような小細工は「音源の再生」の場面でもやったのですが、面倒なので誤魔化そうとしていたんです。

どうやったかと言うと、Jsp, Java  プログラムが JavaScript から参照したいサーバー側のパラメータを JavaScript のグローバル変数として書き出しておいて、JavaScript  はそれを参照する、というだけです。片方向だけどパラメータを受け渡す事ができますね。やれやれ。余計な手間がかかってしまった。(手を抜くとロクなことないぞ・・・分かっているんですけどね・・・)

2021年11月19日 (金)

Select Option の代替品(9)

MySelect の幅、%単位での指定は、うまくいかないので諦めました。親要素の幅が分かればいいんですけど、サーバー側からはクライアント上のブラウザの内にある情報なんか見えない。要素の幅をサーバーへ送信するページとかを作れば出来るはずですが、とっても面倒。結局%指定はあきらめて、「ウインドウ幅の1/20」を基準とした幅の単位を定義して%単位の代わりにしました。

ここで必用なのはウインドウ幅なんですが、これもクライアント上のブラウザ内の情報なので簡単には取得できません。そこで、直前のページの幅を基準として使う事にしました。これならば各ページが window.innerWidth の値を送信~受信して幅を算出すれば良い。なんとかなりました。(送受信するパラメータが、また1つ増えてしまった)

おまけ的な機能、背景色の指定を作っていたのですが、Html, CSS, JavaScript に遊ばれてしまいました。背景色の指定には bgColoe=...,、 style backgroundColor:...、element.style.backgroundColoe、element.style.background=... などがあり、同じ意味、目的なのに相手によって使い分けなくてはいけない。うっかり element.style.background-color などと書くとエラーになる。「-」が演算子と解釈されるみたい。

style="background-color:'red' はNGで、style="background-color:red と書かなくてはならない。一方、element.style.background=red と書くとエラー(そんな変数はない)で、element.style.background='red' と書かなくてはならない。

Html、CSS、JavaScript は統一的に設計~仕様を決定ではなくて、デファクトスタンダードを寄せ集めたように思えるので、仕方ないか。それにして何とかならんのか。と言って、今から仕様を追加しても古い仕様を捨てるわけにもいかない。せいぜい「古い仕様なので推奨しない」と警告するくらいしか出来そうにありませんね。

我慢して使うしかなさそうです。

2021年11月15日 (月)

Select Option の代替品(8)

onChange のハンドラをどうやって起動するかで苦戦していましたが、結局

通常の <Select> タグの onChange 記述するスクリプトを「そのまま」文字列として受け取り、eval() で起動することにしました。

<Select ... onChange="onChanheFunction(param1, param2,...)" であれば、

文字列「"onChanheFunction(param1, param2,...)" 」を受取り、

eval("onChanheFunction(param1, param2,...)" ) とするわけです。

param に document,MyForm.TextBox.Value のようにタグなどが記述されていても、きちんと評価して実行してくれます。

MDN などによると、eval() にはセキュリティー上のリスクがある上、負荷が重いので【使うな】となっています。確かに不特定の文字列を評価して実行するならば、たとえばユーザーが text box などに入力した文字列を実行するならば「とっても危険」でしょう。でも、私のシステムの場合は、本来は <Select onChange > に記述するものを eval() で実行しているだけです。これで eval() が危険と言うならば、onChange に何か書くと危険という事になってしまいますよね。

悪意のあるユーザーが Jsp, Java ファイルを改変して実行するならば、あるいは、「ページのソース」で取得したものを改変して実行可能ならば eval() なんか関係なしで「とっても危険」ですけどね。

ということで、eval() を使う事にしました。

これをやっていて分かった(分かった気になっている)こと。

・function を呼び出す時のパラメータの評価は、function を呼び出す前に(呼び出す処理の一部として)行われるようだ。

function testFunc(aaa){

  console.log(aaa);

}

testFunc(document.MyForm.MyTextBox.value);

などとして、myTextBox に入力された値を表示しようとした時、「document.MyForm.MyTextBox.value」は function testFunc が呼び出される前に(testFunc(document.MyForm.MyTextBox.value); を認識してから testFunc の実行が始まるまでの間に)評価されるらしい。つまり、function の一部として引数の評価をしているわけではないらしい。

function は arguments を参照する事ができますが、arguments には引数リストに記述していない物も含まれています。という事は、function 実行開始前に評価されている、という事でしょうね。function 実行開始後に評価されるならば、eval()  などとしなくても実行できるでしょうね。

さてさて、今ハマっているのは、MySelect の幅の指定なんです。幅をピクセル単位で指定するのは問題なくいのですが、% で指定するのに手こずっています。親要素の幅の「○%」なんですが、サーバー側では親要素が何であるかは認識できても、それの幅(クライアント側で動作しているブラウザが作成した画面内の「要素の幅」なんか取得のしようがありません(当たり前だけど)。さてさて、どうしようか。

%での指定が出来ないのは不便なんだけど、クライアント環境での幅(ピクセル数)なんかサーバープログラマは想定できませんよね。

現状では、各ページ起動時に window 幅を取得して、幅によってPC版、スマホ版を切替えています。必用に応じて同じページのPC版/スマホ版を呼び出すだけなんですけど、切替える時に取得した windoe.clientWidth などをサーバーに送信して、それを基準に何か出来ないか、などと考えています。

幸いな事に「CD・LP管理システム」では画面幅を20等分して <TD> などの幅を決めているので、何とかなりそうですね。

という所で、今日はこれでおしまい。一杯やっちゃったから。

の内容「aaa」

2021年11月12日 (金)

Select Option の代替品(7)

<Select> <Option> の代替品を作ったのは、CD、LPデータ管理データベースのスマホ版に使用するためでした。実際に組み込もうとして見直したら、いくつかの問題が見つかりました。
(1)「初期状態で選択されているオプションがない場合は、最初のオプションを選択状態とする」が未作成だった。
(2)選択肢表示ウインドウの幅が固定である。オプションリストの幅を決めるには、選択肢キャプションの長さ(文字数ではなく、画面上でのピクセル数が必用なんですが、それを調べるのに時間がかかってしまいました。以下の方法で取得できました。 <canvas id=\"canvas\" width=\"0\" height=\"0\" style=\"visibility:hidden;position:absolute;\"></canvas>canvas はグラフィックやアニメーションのための要素だそうですが、使った事はなかった。
var canvas = document.getElementById('canvas'); var context = canvas.getContext('2d'); var maxOptionLength = 0; metrics = context.measureText(optionText); if(maxOptionLength < metrics.width){ maxOptionLength = metrics.width; }としてキャプションの最大長さを取得しました。
・オプションリストに使える幅(可能な最大幅)を得る。画面幅から適当な余白を差し引いた幅。・キャプションの最大長さが可能な最大幅よりも小さい場合は、キャプションの最大長さをオプションリストの幅とする。・そうでない場合は、長いキャプションの末尾を切り捨て、省略記号「…」とする。これを実現するために、<li style に「overflow:hidden;white-space:nowrap; text-overflow:ellipsis;」を付け加えました。キャプションが長すぎるものにだけ付加すればいいのだけど、面倒なので全部に付加しました。後は左右の位置を適当に決めれば良い。MySelect 左端から右側の余地が十分な場合は MySelect の左端に揃えて、そうでない場合は画面右側に適度な余白がある位置としました。
(3)onChange などのイベントハンドラを起動できない。記述してなかったので起動できるわけはなかったのですが・・・。ちょっと苦戦しましたが、何とかなりました。とりあえず必用なのは onChange なので、それだけを実装しました。本来の <Select> はイベント change を投げるのでしょうが、MySelect では指定された function を実行するようにしました。理由は mySelect は Html のタグではなく、私の応用では Java のメソッドで書き出すようになっているからです。苦戦した理由は、change を検出できるのは function setValue() 内なのですが、その場所に直接に直接呼び出すべき function を記述するのはまずいからです。そんな事をすると汎用性なしになってしまいますからね。MySelect を書き出すメソッドに change で起動すべき function と、その function に渡すパラメータを引数として渡すように、それを etValue へ引数として渡すようにしました。手こずったのは、setValue() に引数で渡す function と function のパラメータでした。setValue() へのパラメータを記述する部分で呼び出すべき function などを「'」(シングルクォート)で囲んでいたのが原因でした。受け取った setValue() は文字列だと解釈していたのですね。確かに、文字列は function ではない。「'」で囲むのをやめたらOKでした。次に問題になったのは、onChange function に渡すパラメータでした。パラメータの数は MySelect を書き出す Java のメソッドが決めるのではなく、そのメソッドを呼び出すもの(私のアプリでは別の Java メソッド)が決めるんです。調べているうちに、JavaScript に arguments というのがある事が分かりました。やれやれ、何とかなりました。

後は冗長な部分を直すとかですね。

2021年11月 5日 (金)

Select Option の代替品(6)

(5)でUpした html をコピペ~保存して Firefox で試したところ、動作しました。

今分かっている問題点や改善したい点など

・クリックする場所によってはオプションリストが閉じない場合がある。

アドレスバーやメニューバーなどをクリックしても閉じません。どんなイベントが発生するのかが分からないので、ハンドラを登録できないからです。調べてみても(今のところ)分かっていません。まぁ、そんな場所で発生したイベントの処理が【重要な】アプリは殆どないと思えるので、情報が少ないのです。

実際に動かしてみても、さほどの問題ではないので、放置しています。

・オプションリストの幅

選択肢のキャプションに長いものがあった場合、現状では折り返して表示されます。

文字列は折り返されるのですが、背景色が「選択色」になるのは1行だけ。そのように作ったわけではなく、「やってみたらそうなった」だけなんですが、あまりかっこよくない。

文字列の最大長さは (2 ** 32)-1 だったような記憶がある。何にしても極端に長い文字列があり得るので、何らかの制限を付ける方がいいかな。選択肢に画面全体を使っても表示できないものを記述するとも思えないけどね。

リストの表示幅を拡げる、背景色を変える範囲を何とかする、省略記号(…)を使うなどが考えられますが、それは「実用品」として使う時に考えよう。

さて、ずいぶん長い間作業が止まっていた「ESL-63改造」を再開するかな。。。

Select Option の代替品(5)

<!--

・サンプルの Html ファイル

(1)適当なテキストエディタにコピペして適当なフォルダに保存し、

(2)ブラウザからファイルとして開いてください。

Firefox ならば、【ファイル】【ファイルを開く】とすれば開くことができます。Chtome, Edge にはそのようなメニュー等はないようです。アドレスバーにファイルの URL を直接入力すれば開く事ができます。入力する URL は「file:///C:/ ファイルのパス名」などとします。

【送信】ボタンをクリックするとパラメータを送出し、自分自身を呼び出します。通常、送信先のURL を指定してから送信しますが、このテストページではURLは指定していない(自分自身なので)ので、保存する時のファイル名はどのような名前でもかまいませんが、拡張子は「.html」とするのが無難でしょう。ブラウザが「html ファイルではない」と誤解するとまずいので。
以下のサンプルは

・コメントは削除してあります。ブログ入力ソフトが「通常の文書」として処理する過程で余分なスペースや、時には改行などを削除したりするので、必用な行の先頭に「//」があるかに見える場合があるからです。

・私の環境では動作していますが、動作を保証するものではありません。念のため。

・動かなかったり、変な動きをした場合には動作環境を明示してコメントを頂けると幸いです。もっとも、私が同じような環境でテストできるか、は別問題ですが。

-->

<html><Head> <meta contentType="text/html"; charset="utf-8"; pageEncoding="utf-8"> <Title>&lt;select&gt;&lt;option&gt; の代替品のテスト</Title></Head>
<Body onLoad="onLoadFunction()"><Form name="TEST" method="get"><p>&lt;select&gt;&lt;option&gt; の代替品のテスト</p><BR><BR><span style="outline:solid thin;width:150px;" id="MySel0" onClick="dispMyOption('MySel0','MySelT0','MyOpt0','MyUl0','17')"><input type="text" readonly style="cursor:default;outline:none;border:none;width:135px;height:40px;" id="MySelT0" value=""><input type="text" readonly style="cursor:default;outline:none;border:none;text-align:right;width:15px;height:40px;" value="v " class="aH"></span><input type="hidden" name="TestParam" value="">
<div id="MyOpt0" style="display:none;position:fixed;border:1px solid #000;background-color:#fff;"><ul style="list-style:none;padding-left:0;padding-top:0;margin-top:0;padding-bottom:0;margin-bottom:0;overflow-y:auto;" id="MyUl0">
<li onMouseOut="onMouseOutFunction('MySel0',this,'0');" onMouseOver="this.style.background='lightskyblue';" style="text-align:left;padding-left:20;padding-top:10;padding-bottom:10;" onClick="setValue('MySel0','MySelT0','MyOpt0','222','アイルランド','0','TestParam')">アイルランド</li><li onMouseOut="onMouseOutFunction('MySel0',this,'1');" onMouseOver="this.style.background='lightskyblue';" style="text-align:left;padding-left:20;padding-top:10;padding-bottom:10;" onClick="setValue('MySel0','MySelT0','MyOpt0','278','アメリカ','1','TestParam')">アメリカ</li><li onMouseOut="onMouseOutFunction('MySel0',this,'2');" onMouseOver="this.style.background='lightskyblue';" style="text-align:left;padding-left:20;padding-top:10;padding-bottom:10;" onClick="setValue('MySel0','MySelT0','MyOpt0','242','イギリス','2','TestParam')">イギリス</li><li onMouseOut="onMouseOutFunction('MySel0',this,'3');" onMouseOver="this.style.background='lightskyblue';" style="text-align:left;padding-left:20;padding-top:10;padding-bottom:10;" onClick="setValue('MySel0','MySelT0','MyOpt0','223','イタリア','3','TestParam')">イタリア</li><li onMouseOut="onMouseOutFunction('MySel0',this,'4');" onMouseOver="this.style.background='lightskyblue';" style="text-align:left;padding-left:20;padding-top:10;padding-bottom:10;" onClick="setValue('MySel0','MySelT0','MyOpt0','207','オーストリア','4','TestParam')">オーストリア</li><li onMouseOut="onMouseOutFunction('MySel0',this,'5');" onMouseOver="this.style.background='lightskyblue';" style="text-align:left;padding-left:20;padding-top:10;padding-bottom:10;" onClick="setValue('MySel0','MySelT0','MyOpt0','230','オランダ','5','TestParam')">オランダ</li><li onMouseOut="onMouseOutFunction('MySel0',this,'6');" onMouseOver="this.style.background='lightskyblue';" style="text-align:left;padding-left:20;padding-top:10;padding-bottom:10;" onClick="setValue('MySel0','MySelT0','MyOpt0','218','ギリシャ','6','TestParam')">ギリシャ</li><li onMouseOut="onMouseOutFunction('MySel0',this,'7');" onMouseOver="this.style.background='lightskyblue';" style="text-align:left;padding-left:20;padding-top:10;padding-bottom:10;" onClick="setValue('MySel0','MySelT0','MyOpt0','238','スペイン','7','TestParam')">スペイン</li><li onMouseOut="onMouseOutFunction('MySel0',this,'8');" onMouseOver="this.style.background='lightskyblue';" style="text-align:left;padding-left:20;padding-top:10;padding-bottom:10;" onClick="setValue('MySel0','MySelT0','MyOpt0','212','チェコ','8','TestParam')">チェコ</li><li onMouseOut="onMouseOutFunction('MySel0',this,'9');" onMouseOver="this.style.background='lightskyblue';" style="text-align:left;padding-left:20;padding-top:10;padding-bottom:10;" onClick="setValue('MySel0','MySelT0','MyOpt0','213','デンマーク','9','TestParam')">デンマーク</li><li onMouseOut="onMouseOutFunction('MySel0',this,'10');" onMouseOver="this.style.background='lightskyblue';" style="text-align:left;padding-left:20;padding-top:10;padding-bottom:10;" onClick="setValue('MySel0','MySelT0','MyOpt0','217','ドイツ','10','TestParam')">ドイツ</li><li onMouseOut="onMouseOutFunction('MySel0',this,'11');"  onMouseOver="this.style.background='lightskyblue';" style="text-align:left;padding-left:20;padding-top:10;padding-bottom:10;" onClick="setValue('MySel0','MySelT0','MyOpt0','113','日本','11','TestParam')">日本</li><li onMouseOut="onMouseOutFunction('MySel0',this,'12');" onMouseOver="this.style.background='lightskyblue';" style="text-align:left;padding-left:20;padding-top:10;padding-bottom:10;" onClick="setValue('MySel0','MySelT0','MyOpt0','231','ノルウェー','12','TestParam')">ノルウェー</li><li onMouseOut="onMouseOutFunction('MySel0',this,'13');" onMouseOver="this.style.background='lightskyblue';" style="text-align:left;padding-left:20;padding-top:10;padding-bottom:10;" onClick="setValue('MySel0','MySelT0','MyOpt0','220','ハンガリー','13','TestParam')">ハンガリー</li><li onMouseOut="onMouseOutFunction('MySel0',this,'14');" onMouseOver="this.style.background='lightskyblue';" style="text-align:left;padding-left:20;padding-top:10;padding-bottom:10;" onClick="setValue('MySel0','MySelT0','MyOpt0','216','フランス','14','TestParam')">フランス</li><li onMouseOut="onMouseOutFunction('MySel0',this,'15');" onMouseOver="this.style.background='lightskyblue';" style="text-align:left;padding-left:20;padding-top:10;padding-bottom:10;" onClick="setValue('MySel0','MySelT0','MyOpt0','208','ベルギー','15','TestParam')">ベルギー</li><li onMouseOut="onMouseOutFunction('MySel0',this,'16');" onMouseOver="this.style.background='lightskyblue';" style="text-align:left;padding-left:20;padding-top:10;padding-bottom:10;" onClick="setValue('MySel0','MySelT0','MyOpt0','232','ポーランド','16','TestParam')">ポーランド</li></ul></div><BR><BR><input type="button" style=\"-webkit-appearance:none;height:40px;\" value="送信" onClick="reload()"></Form><script language="JavaScript">var selectedCode = '';var codeIDList = [ [222, 'アイルランド'], [278, 'アメリカ'], [242, 'イギリス'], [223, 'イタリア'], [207, 'オーストリア'], [230, 'オランダ'], [218, 'ギリシャ'], [238, 'スペイン'], [212, 'チェコ'], [213, 'デンマーク'], [217, 'ドイツ'], [113, '日本'], [231, 'ノルウェー'], [220, 'ハンガリー'], [216, 'フランス'], [208, 'ベルギー'], [232, 'ポーランド'] ];var initialSelection = -1;
function onLoadFunction(){ document.addEventListener('auxclick', closeMyOption, {capture:true}); document.addEventListener('scroll', closeMyOption, {capture:true}); document.addEventListener('keydown', closeMyOption, {capture:true}); document.addEventListener('mousedown', mouseDownHandler, {capture:true}); var urlStr = document.location.search; initialSelection = -1; if((urlStr != null) && (urlStr.length != 0)){ var eqPos = document.location.search.indexOf('='); selectedCode = urlStr.substring(eqPos + 1); if(selectedCode != ''){ for(var i=0; i<codeIDList.length; i++){ if(codeIDList[i][0] == selectedCode){ initialSelection = i; var mySelectElement = document.getElementById('MySelT0'); mySelectElement.value = codeIDList[i][1]; break; } } } }}
var activeMySelect=null;var activeMyOption=null;var selectedItems=null;var normalHeight=20;
function dispMyOption(mySelectID, mySelectTextID, myOptionID, myUlID, numOptions){ var myOptionElement = document.getElementById(myOptionID); var ulElement = document.getElementById(myUlID); if(activeMyOption != null){ myOptionElement.style.display='none'; activeMyOption=null; activeMySelect=null; return; } var selectedNo = -2; if(selectedItems == null){ selectedItems = new Array(0); } for(var i=0; i<selectedItems.length; i++){ if(selectedItems[i][0] == mySelectID){ selectedNo = selectedItems[i][1]; break; } } if(selectedNo == -2){ selectedNo = initialSelection; var workArray = new Array(2); workArray[0] = mySelectID; workArray[1] = selectedNo; selectedItems.push(workArray); } var optionRows = ulElement.children;
for(var i=0; i<optionRows.length; i++){ optionRows[i].style.backgroundColor=''; } if(selectedNo != -1){ var selectedRow = optionRows[selectedNo]; selectedRow.style.backgroundColor='aquamarine'; } var mySelectTextElement = document.getElementById(mySelectTextID); var mySelectTextRect = mySelectTextElement.getBoundingClientRect(); var mySelectElement = document.getElementById(mySelectID); var mySelectRect = mySelectElement.getBoundingClientRect(); var mySelectTop = mySelectTextRect.top; var mySelectBottom = mySelectTextRect.bottom; var mySelectLeft = mySelectRect.left; var mySelectWidth = mySelectRect.width; var myOptionLiHeight=normalHeight + 20; var myOptionTotlaHeight=myOptionLiHeight * numOptions; var windowHeight=window.innerHeight; var upperMatgin=mySelectTop; var lowerMargin=windowHeight-mySelectBottom; var dispAbove=false; var numRows = 0; if((upperMatgin < myOptionTotlaHeight) && (lowerMargin < myOptionTotlaHeight)){ dispAbove = lowerMargin < upperMatgin; var margine; if(dispAbove){ margin = upperMatgin; }else{ margin = lowerMargin; } numRows = Math.floor(margin / myOptionLiHeight); myOptionTotlaHeight = myOptionLiHeight * numRows; }else{ dispAbove = lowerMargin < myOptionTotlaHeight; } ulElement.style.height = myOptionTotlaHeight; ulElement.style.width = mySelectWidth; if(dispAbove){ myOptionElement.style.top = mySelectTop - myOptionTotlaHeight; }else{ myOptionElement.style.top = mySelectBottom; } myOptionElement.style.left = mySelectLeft; var listElement=document.getElementById(myOptionID); listElement.style.display='block'; for(var i=0; i<optionRows.length; i++){ optionRows[i].style.height=normalHeight; } var scrollRows = 0; if(selectedNo != 0){ var maxScrollRows = numOptions - numRows; var scrollOffset = Math.floor(numRows / 2); scrollRows = selectedNo - scrollOffset; if(scrollRows < 0){ scrollRows = 0; }else{ if(maxScrollRows < scrollRows){ scrollRows = maxScrollRows; } } } ulElement.scrollTop=scrollRows * myOptionLiHeight; activeMyOption=myOptionID; activeMySelect=mySelectID;}
function mouseDownHandler(e){ if(activeMyOption == null){ return; } var mySelector='#' + activeMySelect; if(e.target.closest(mySelector) != null){ return; } closeMyOption(e);}
function closeMyOption(e){ if(activeMyOption == null){ return; } var eventType=e.toString(); if(eventType.indexOf('KeyboardEvent') != -1){ var keyCode=e.key; if((keyCode != 'Escape') &&(keyCode != 'OS') &&(keyCode != 'F10') &&(keyCode != 'Tab') && (keyCode != 'Enter')){ return; } } var targetObjType = e.target.toString(); var posEllement=targetObjType.indexOf('Element'); var closeIt=false; if(posEllement == -1){ closeIt = true; }else{ var mySelector='#' + activeMyOption; closeIt = e.target.closest(mySelector) == null; } if(!closeIt){ return; } var myOptionElement=document.getElementById(activeMyOption); myOptionElement.style.display='none'; activeMyOption=null; activeMySelect=null;}
function setValue(mySelectID, mySelectTextID, myOptionID, codeID, caption, rowNo, htmlParamName){ var mySelectElement = document.getElementById(mySelectTextID); var myOptionElement=document.getElementById(myOptionID); mySelectElement.value=caption; var hiddenElements = document.getElementsByName(htmlParamName); hiddenElements[0].value=codeID; for(var i=0; i<selectedItems.length; i++){ if(selectedItems[i][0] == mySelectID){ selectedItems[i][1] = rowNo; break; } } myOptionElement.style.display='none'; activeMyOption=null; activeMySelect=null;}
function onMouseOutFunction(MySelID, liElement, rowNo){ for(var i=0; i<selectedItems.length; i++){ if(MySelID == selectedItems[i][0]){ if(rowNo == selectedItems[i][1]){ liElement.style.background='aquamarine'; }else{ liElement.style.background=''; } return; } }}function reload(){ document.TEST.submit();}</script></BODY></HTML>

Select Option の代替品(4)

・JavaScript その2


/* マウスボタンが押下された時のイベントハンドラ

オプションリストが非表示ならば何もしない。表示中であっても MySelect 上で押下された場合は何もしない。

そうでなければ、オプションリストを非表示とする。

マウスイベントは mouseDown --> mouseUp --> click の順に発生するので、MySelect 上で押下された時にオプションリストを閉じると、直後に発生するMySelect onClick で再び開いてしまいます。

これを避けるために、ここでは非表示とはせず、MySelect click のハンドラでオプションリストが開いていたら閉じる、としました。

function mouseDownHandler(e){

 if(activeMyOption == null){ // オプションリストが開いていない時は

  return;  // 何もしない。

 }

 var mySelector='#' + activeMySelect;

 if(e.target.closest(mySelector) != null){ // MySelect 上で押下

  return;  // された場合も、何もしない。

 }

 closeMyOption(e);  // オプションリストを閉じる。

}


// オプションリストを閉じる

function closeMyOption(e){

 if(activeMyOption == null){  // オプションリストが開いていない場合は

  return;  // 何もしない

 }

 var eventType=e.toString();  // イベント種別を得る

  if(eventType.indexOf('KeyboardEvent') != -1){ // キーボードイベントならば

   var keyCode=e.key;  // 押されたキーのコードを得る。

// 閉じるのは【エスケープキー】【Windows キー】【F10 キー】【Enter キー】そする。

   if((keyCode != 'Escape') &&(keyCode != 'OS') &&(keyCode != 'F10') &&(keyCode != 'Tab') && (keyCode != 'Enter')){

    return;  // いずれのキーでもないので、これで終わり。

   }

  }

/* イベント発生源を得る。どのエレメントの上でもない位置で押下される場合があるから。エレメント上で押下された場合は、発生源が 'XxxElement' となるので、これで判断する。エレメントでない発生源に対して e.target.closest() を行うとエラーとなる

*/

  var targetObjType = e.target.toString(); // 発生源を得る

  var posEllement=targetObjType.indexOf('Element'); // 文字列 'Element' の位置を得る。

  var closeIt=false; // true:オプションリストを閉じる

  if(posEllement == -1){  // 発生源が 'xxxElement' でない場合は

   closeIt = true;  // オプションリストを閉じる

  }else{  // 発生源が xxxElement の場合は

// オプションリストの上で押下された場合は閉じてはならない。オプションリスト上かを調べる。

   var mySelector='#' + activeMyOption;

   closeIt = e.target.closest(mySelector) == null;  // オプションリスト上ではない

  }

  if(!closeIt){  // 「閉じるべき」でないならば

   return;  // 何もしない。

  }

//  オプションリストのエレメントを得て非表示とする。

  var myOptionElement=document.getElementById(activeMyOption);

  myOptionElement.style.display='none';

  activeMyOption=null; activeMySelect=null;

}


// オプションリストの行がクリックされた。必用な値を MySelect にセットしてオプションリストを閉じる

function setValue(mySelectID, mySelectTextID, myOptionID, codeID, caption, rowNo, htmlParamName){

 var mySelectElement = document.getElementById(mySelectTextID);  // セット対象の mySelect

 var myOptionElement=document.getElementById(myOptionID); // 閉じるべきリストの id

 mySelectElement.value=caption;  // MySelect にキャプションを表示

 var hiddenElements = document.getElementsByName(htmlParamName);   // hidden を取り出し

 hiddenElements[0].value=codeID;  // value をセットする。

 for(var i=0; i<selectedItems.length; i++){  // MySelect ごとの「現在の選択行」を書き換える。

  if(selectedItems[i][0] == mySelectID){

   selectedItems[i][1] = rowNo;

   break;

  }

 }

 myOptionElement.style.display='none';  // オプションリストを非表示とする。  

 activeMyOption=null;

 activeMySelect=null;

}


/*  <li> の行からマウスが外れた時の処理

 既に選択されている行と、今マウスが乗っている行とでは背景色を変えている。 マウスが去った時、背景色を「選択されていない」色にするか「すでに選択されている」色にするかを判断する必要がある。*/

function onMouseOutFunction(MySelID, liElement, rowNo){

 for(var i=0; i<selectedItems.length; i++){

  if(MySelID == selectedItems[i][0]){ // この MySelect の現在の選択行を取り出し

   if(rowNo == selectedItems[i][1]){ // 選択された行ならば

    liElement.style.background='aquamarine';  // 「選択されている」色に

   }else{  // そうでなければ

    liElement.style.background='';  // 背景色なしにセットする。

   }

   return;

  }

 }

}


// 【送信】ボタンがクリックされた時の処理

function reload(){

 document.TEST.submit();  // 単に送信する。

}

</script>


次回はサンプルの Html ファイルの予定です、

2021年11月 4日 (木)

何か変だ・・・続報

問題の記事は「<Select><Option> の代替品(1)」といったタイトルだったのですが、タイトル文字列の中に html のタグがあったのが問題だったようです。

タイトルを「Select Option の代替品(1)」のようにに修正すると正常な表示になりました。

何か変だ

今日、「<Select><Option> の代替品」の(1)から(3)まで、3つの記事を投稿したのですが、表示が変です。しばらく状況を見る事にします。

連投したのがまずかったのか???

Select Option の代替品(3)

・<li> について、それから・・・

<li> にはてこずりました。オプションリスト表示の高さを得るために

「リストの高さ = 1行の高さ X 行数」としているので、どうしても「1行の高さ」が必用なんですが、


・<li> の高さが取れない。undefined が返ってきます。かなり粘ってみたのですがダメ。高さが取れないので、他の物の高さから「<li> の高さ = 40px」として進めたのですが、


・<li> に高さをセットしても反映されない。これもかなりの時間を使ってしまいました。分かったのは

・【表示されていない】状態の <li> の高さなどは取得できない。同様に

・【表示されていない】状態の<li> に高さ等をセットしても反映されない。


色々いじっていたら、表示されていれば高さなどを取得できるし、値をセットすれば反映されるという事が分かりました。高さは何とかなったのですが・・・次に引っかかったのは・・・


・valign:middle がきかない。何が何でも上寄せになってしまいます。これにも時間を使ってしまいましたが、高さ:20 とし、padding-top=10; padding-botton:10 とすると、それらしい見かけになりました。
他のエレメントはそれなりに動くのだけれど <li> は違うんですね。これ、<li> の仕様なのかバグなのか分かりませんが・・・


短いけど、今回はこれだけ。の予定だったんですけど早速、テスト用 Html のバグが発覚しました。MySelect の外枠(outline)が【横倒しした「こけし」】みたいな形になってしまう事があるんです。Firefix だとOKなんですが、Chrome, Edge では「こけし」になってしまいます。調べてみたら、MySelect の2つの <input type=text. の間の改行が原因でした。Html ファイル上では単なる改行なんですが、この改行コードが「標準高さの空白文字」と解釈されていたようです。border などを表示させると、2つの text の間に空白がありました。標準よりも高い2つの textbox に挟まれて標準高さの空白文字、となっていたと思われます。これの outline を表示していたので、標準高さの空白の部分が「首」になっていたんですね。ところで、【Firefix だとOKなんですが、Chrome, Edge では「こけし」になってしまいます。】どっちが本来の仕様なんでしょうね。ひょっとすると仕様はないのかも知れませんね。HTMLって、ブラウザの裁量にまかされている部分がかなりあるようなので。結局、この改行を削除してOKとなりました。読みにくいけど、しょうがない。