2022年1月22日 (土)

DOCTYPE HTML その後

DBアプリの各ページに <!DOCTYPE HTML>  を入れたのですが、動かなくなったのは style.width でけではありませんでした。

画面のスクロール位置を得るのに

document.FORM_NAME.scrollLeft,  document.FORM_NAME.scrollTop

を使っていたのですが、これらの値が常に0になってしまいました。

これ、元はIEの仕様らしいのですが、Firefox でも動いていたので気にしていなかったのですが、標準仕様ではなかったようです。

window.scrollX , window.scrollY に変更したらOKでした。

位置の復元は 

document.FORM_NAME.scrollLeft,  document.FORM_NAME.scrollTop に値を代入していたのですが、これもNG。 window.scrollTo(x,y); としたらOKでした。

Windows を買えばIEが付いてくるし、

IEを前提にしたサイトが増えると Firefox としても対応せざるを得ない・・・ので対応しただけだったようです。Html にはこんなのが沢山ありそうで、次は何が発覚するかな??

いろんなブラウザ屋さんが「独自に拡張」したのがデファクトスタンダードになってしまい、仕様は後追いなので、混乱の極みですね。何とかしてほしいけど、ブラウザもサイトも「出回ってしまった」のでどうしようもないでしょうね。

2022年1月19日 (水)

ESL-63復活と改造プロジェクト(12)

長らく止まっていた「ESL-63復活と改造プロジェクト」ですが、どうにか作業ができそうな状態になりました。DBいじりの方が面白い以外に「仕上がりの姿」が見えなくなってしまったからです。どういう事かと言うと

・発音エレメントは強固なフレームに固定する。

・フレームは外筐に「ゆるく」固定する。

のは変わっていないのですが、その構造をどうするか。が問題だったのです。考えているうちに、どんどん複雑になってしまいました。で、一旦リセット。考え直していたら、逆にどんどん単純な構造になってきました。単純になるのは方向性が正しい場合が多いんですよね。

それをベースに「お絵描き」(図面を書くこと)をやっています。ラフではあるけど、縮尺を決めて絵をかいています。何とかいけそう。

改造案にいくつか追加しました。

・エレメントのプラスチック枠に固定電極(紙エポキシ基板)を接着するのですが、これが剥がれると放電が始まってしまいます。オリジナルが放電を始めたのも、コレが原因と思われます。そこでプラスチック枠と固定電極の穴を細い糸で縛り付けようかと思っています。

・固定電極に鉛シート(0.3ミリ厚)を貼り付ける。もちろん音が通る穴を開けなくてはなりませんが、薄い鉛シートなので簡単に開ける事ができます。問題は数が多い事(数万カ所)。やれやれ。でも、やる予定です。と言うのは、

以前プラ枠の高音域での共振音を消そうとして油粘土でデッドニングした時、高音域のクセがとれると同時に低音域がぐっと良くなったからです。枠が重くなった、振動膜と固定電極、枠の質量比が大きくなったからでしょう。ならば、固定電極自体も重くしてやれ。固定電極のデッドニングにもなるしね。

振動膜の厚さは不明ですが、10ミクロンと仮定して、固定電極の厚さは0.5ミリ。比重が同じならば質量比は50倍程度。これに0.3ミリ圧の鉛(比重11くらい)を貼れば質量比は400程度になります。期待できそう。

・ついでに、プラ枠のデッドニング材は鉛シートに変更。

それにしても・・・当面の作業はプラ枠の古い接着剤落しと固定電極の洗浄。長く面白くない作業になりそうです。

ブラウザに警告された

DBの新版作成中なんですが、firefox のブラウザコンソールに見慣れないメッセージが表示されました、それも全ページに対して。アプリ自体は動くのですが、気になります。読んでみたら「このページは後方互換性によって動いている。<!DOCTYPE HTML> を使えだそうです。そこで、各ページの先頭に <!DOCTYPE HTML>  を入れると警告メッセージは出なくなったのですけど、画面のレイアウトが不正になってしまいました。

調べてみると「後方互換で動いているので画面のレイアウトが不正になる可能性がある」らしい。でも <!DOCTYPE HTML> を削除すれば正しいレイアウトになる、何じゃ、コレ。

どうやら JavaScript が function の途中で処理を打ち切っているらしい。(これ、JavaScript の仕様なんですね。エラーが起こると、その時点で処理を打ち切る)

...style="width:100;" などの行に単位「px, %」がないのが原因でした。

旧仕様:単位が記述されていない場合は「px である」として動作する。

新仕様:単位が記述されていない場合はエラーとする。

のようです。そこらじゅう直して動くようになりました。それにしても「単位が記述されていない場合は px であるとみなす」のではどんな不具合が起こるのでしょうか。理解できないけど、動いたから、まっ、いいか。

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 ファイルの予定です、