変奏現実

パソコンやMMORPGのことなどを思いつくまま・・・記載されている会社名・製品名・システム名などは、各社の商標、または登録商標です。

この画面は、簡易表示です

[JavaScript]Promise

Promiseオブジェクトの作り方には大きく2つある。

  1. new Promise( )
  2. Promise.resolve( )

new Promise( ) は、新たにオブジェクトを作るけど、Promise.resolve( ) の方はstaticなPromiseオブジェクトを使いまわししているだろう。

class Promise {
    constractor () {
    ・・・
    }
    static staticResolve = new Promise().resolve(undefined);
    resolve() {
      if (typeof(this) === "undefined") {
        return Promise.staticResolve;
      ・・・
    }
}

と考えると、Promise.resolve( ) はpromise状態が履行 (fulfilled)に確定しているところから始まるから

Promise.resolve().then( (a)=>{
    /* なんたら */ 
}).then( (b)=>{
   /* かんたら */
}).then( (c)=>{
   /* それから */
}).then( (d)=>{
   /* こうして */
}).then( (e)=>{
   /* こうなった */
}).catch((e)=>{
   /* どうして、そうなった? */
});

と書いても、「なんたら」部の最後まで進めば、直ぐに「かんたら」に突入し、ストレートに「こうなった」まで進んで一気に進むので、完全な同期処理である。

単に、try…catch…finally なんて美しくないという人向けでしかない。

ただ、ドコが壊れているのかイミフなコードを切り分けた状況のエビデンスを作る時には非常に便利である。

実際には<% try { %><% throw(“ココか?”); %> <%} catch(e) {consol.log(e); } %>式に

プローブ針を垂らすけど、エビデンスとしては判りにくいからね。

強いて言えば、「なんたら」「かんたら」「それから」「こうして」「こうなった」の全てにawaitで強制的にワーカースレッドを停止させる箇所が無いと意味が無いのだ。

そんな訳で、非同期で処理したいものを1つづつ、「非同期化」するしかないが、そう難しくはない。

class hoge {
    // 非同期型 同期型と同じ数だけ作成する
    asyncFunc(params) {
    return new Promise((resolve, reject) => {
        try {
            let result = syncFunc(params);
            resolve(result);
        } catch(e) {
            reject(e);
        }
    });
    }
    // 同期型
    syncFunc(params) {
        return foo;
    }
    ・・・ 
}

と非同期呼び出し口を用意するだけ、

しかし、async接頭子のメソッドがいっぱい増えるのが嫌なら

class hoge {
    // 非同期型 1つだけ作って使いまわし。※毎回 new Promise しなくていいのが嬉しい。
    asyncCall(syncFunc,params) {
    return new Promise((resolve, reject) => {
        try {
            let result = syncFunc(params);
            resolve(result);
        } catch(e) {
            reject(e);
        }
    });
    }
    // 同期型
    syncFunc1(params) {
        return foo;
    }
    ・・・ 
    // 同期型
    syncFuncN(params) {
        return foo;
    }
}

と、非同期化メソッドを用意しておくといいだろう。

引き渡すパラメータはthenチェインする前に確定してしまうから、後々トラブりそうなのが難点で、

しかも、下のコードを見て判るように、各処理に引き渡すパラメータは事前に用意することになるけど

paramを変数宣言しておいて使用すれば、各処理が動き出す前に調整は可能だし、何よりデバッグが楽だ。

ちゃんとデバッグが済んでから、試作型に実処理を埋め込めばいいだろう。

とりあえず、

// 非同期処理を準備する
let param1;
let param2;
let param3;
let param4;
let param5;
let promFunc1 = asyncFunc1(param1);
let promFunc2 = asyncFunc2(param2);
let promFunc3 = asyncFunc3(param3);
let promFunc4 = asyncFunc4(param4);
let promFunc5 = asyncFunc5(param5);
//
promFunc1.then(()=>{              /* なんたら param2を最新化 */ 
    promFunc2.then(()=>{          /* かんたら param3を最新化 */
      promFunc3.then(()=>{        /* それから param4を最新化 */
        promFunc4().then(()=>{    /* こうして param5を最新化 */
          promFunc5().then(()=>{  /* こうなった */
});});});});}).catch((e)=>{      /* どうして、そうなった? */
});

とコードすることで完全に非同期になると思う。

でも、thenチェインって云われているものは普通コレではない。

//同期っぽい書いてある非同期処理 ※パラメータは暗黙の了解で繋がっている
promFunc1(a)       /* なんたら 暗黙のparam2を最新化 */
.then(promFunc2)     /* かんたら 暗黙のparam3を最新化 */
.then(promFunc3)     /* それから 暗黙のparam4を最新化 */
.then(promFunc4)     /* こうして 暗黙のparam5を最新化 */
.then(promFunc5)     /* こうなった */
.catch((e)=>{
   /* どうして、そうなった? */
});

あるいは

//同期っぽく書いてある非同期処理
promFunc1(a).then( (b)=>{        /* なんたら */    
    let b = await promFunc2(b);  /* かんたら */
    return b;
}).then( (b)=>{   
    let c = await promFunc3(b);  /* それから */
    return c;
}).then( (c)=>{
    let d = await promFunc4(c);  /* こうして */
    return d;
}).then( (d)=>{
    let e = await promFunc5(d);   /* こうなった */
    return e;
}).catch((e)=>{
   /* どうして、そうなった? */
});

のハズだ。この書き方の最大の問題点はawaitが本当に動作してくれるかどうかだ。

awaitはドコかのPromiseオブジェクトが「履行 (fulfilled): 処理が成功して完了」になるのを待つせいで、

ワーカースレッド内で同時に使用できるのはたったの1回だけ。

ネストできないから、上のコードを参照する処理や各async内でawaitしてるとawaitした途端にエラってしまう。

ので、

awaitを使ってエレガントなコードに酔いしれることができるのは「お一人様」つまり「貴族様仕様」なのだ。

つまり、awaitのaは、「とあるドコかの何か」ではなく「お一人様」の意味なのだ。

※試験には出ないけど重要です。

流石、文化が違うよね。ボクらはネストしてるthenチェーンか綺麗になおしたらデバッグ困難なコードに立ち向かう事になる。

だから、「ボタンをクリック」した直後に一回くらいに限定した方がいい。

サーバーと通信する際に使うと、画面隅に通知機能を搭載した途端にケンカ沙汰になってしまう。

というのが、ググった結果から得られた感想。

本当にこうなっているのかコードしてみると予想が結構ハズれているような気がする。

何せ、数年経てば、仕様書上は全く変わらないのに、全く違う動作になっている世界だけに。

実際、new Promiseのコールバックは今では即実行されてしまうので、

Promiseのthenの設定を全て終えメインスレッドが完了してから動いてくれると思ったら大間違い。

Promise.thenのコードを見つけると即実行するので、全く非同期ではない。

正確には、new Promiseのコールバックの中だけ、resolveかrejectを呼ぶまで非同期になってるダケで

Promise.resolveは、非同期に見える振りをしているだけ。

非同期にしたいなら

//同期っぽい書いてある非同期処理 ※パラメータは暗黙の了解で繋がっている
promFunc1(a)       /* なんたら 暗黙のparam2を最新化 */
.then(promFunc2)     /* かんたら 暗黙のparam3を最新化 */
.then(promFunc3)     /* それから 暗黙のparam4を最新化 */
.then(promFunc4)     /* こうして 暗黙のparam5を最新化 */
.then(promFunc5)     /* こうなった */
.catch((e)=>{
   /* どうして、そうなった? */
});

の書き方以外は意味が無い。

正解を引き当てたると後々面倒なことになるのはいつもの事。(大笑

「非同期な処理」≒「後でやって欲しい処理」を書くには、

各promFuncNの中でsetTimeoutしてresolveする以外に方法は無い。

ps.2021/12/13

サンプル追加。

thenチェインする処理を関数化すると、returnした値が引き継がれないのは痛いね。

thenチェインでreturnした値は、thisにぶら下げて引き渡しているんだろう。

1つの関数の中で閉じ籠って then チェイン して

うまく動いてるけど、ゴチャゴチャしているので

リファクタリングして美しいthenチェインにすると・・・

壊れます。(痛すぎ



[JavaScript]finallyが壊れている様な気がする

イミフなスタックオーバーフローに悩まされた。

static dataDumpNest = [];
static DATADUMPNEXT_MAX = 100;
static dataDump(objectName, obj, pOptions) {
  try {
    ・・・
    Helper.dataDumpNest.push(obj);
    rc = true;
    ・・・
    return true;
    ・・・
    //  再帰呼び出し時はtrueを返す
    if (Helper.dataDumpNest.length > 1) {
      return true;
    }
    //  それ以外は result を結合し返す
    return Helper.result.join("\n");
  } catch (e) {
    throw e;
  } finally {
    Helper.dataDumpNest.pop();
  }
}

finallyで無条件でpopしているので、

重複チェックで弾かれてpushしてない時に

無駄にjoinしてしまうのが気になり

執拗にF10でトレースしていると・・・

finallyブロックの終わりで呼び出し元のメソッドに戻るべきところが、

  1. ソース画面が何行か戻ってしまう。
  2. スタックトレースが妙に長くなっていた。

という摩訶不可解な現象にぶち当たった。

どうやら、return文無しで、finallyブロックの末尾にぶち当たると、ヤバイ事が起きている。

1万個の配列を何度もjoinさせ、しかも結果は捨てているのでブチ切れさせてしまったかもね。(大笑

普通、ドコかのreturn文の後に処理するハズのfinally句なのに。

延々と待たされた挙句にスタックオーバーフローになるソースを

finally句でtryの外の変数rcをreturnする様にしてみた。

static dataDumpNest = [];
static DATADUMPNEXT_MAX = 100;
static dataDump(objectName, obj, pOptions) {
  let rc = false;
  try {
    ・・・
    Helper.dataDumpNest.push(obj);
    rc = true;
    ・・・
    rc = true;
    return rc;
    ・・・
    //  再帰呼び出し時はtrueを返す
    if (Helper.dataDumpNest.length > 1) {
      rc = true;
      return rc;
    }
    //  それ以外は result を結合し返す
    rc = Helper.result.join("\n");
    return rc;
  } catch (e) {
    throw e;
  } finally {
  if (rc != false) {
    Helper.dataDumpNest.pop();
  }
    return rc;
  }
}

と変えたら、即終わったった

setTimeoutでワーカースレッドっぽい状態なんで、

finallyが、Promiseと勘違いしてスタックを遡ってみると配列がいっぱい積んであるので

Array[ Array[1], Array[2] ] あたりが、

new Promise((resolve, reject) {
・・・
}

に、そっくりだったのかもしれない。

しかし、違うと気が付いて、throw new Error(“Don’t mind.”);

して、普通のfinally になるつもりが、ソコのfinallyでまた躓いて、無限ループしてそう。

でも、そのうち治るでしょ?

やっと、本題の文法解析に戻れそう。

ん?三国英雄たちの夜明けがフリーズしやすいのは

もしやコレなのか?



[Visual Studio Code]「フォルダを指定して検索」が動かない

JavaScriptは型が無い。とは云え、全く無視するのは辛いので。マメにJSDocなコメントを入れている。

VisualStudioCodeでは関数や変数の名前をクリックして2回(Ctl+Alt+D)すると、テンプレが書き込まれるので簡単だ。

しかし、コード中に型チェックすると typeof を羅列するより switch case で書きたい時もある。

そんな時はObject.prototype.toString()が、

[Object ${ObjectTypeName}]の様な文字列を返してくれるので、 switch case 風に処理できる。

/**
 * オブジェクトの型名を調べる
 *
 * @param {Any} obj   オブジェクト
 * @return {string}      型名
 */
const getTypeName= (obj) => {
  if (typeof (obj) === "undefined") {
    return "undefined";
  }
  var toString = Object.prototype.toString;
  // [object typeName] のハズなので、typeName部分を抽出してみる
  let typeName = toString.call(obj).match(/^\[\w+\s+(\w+)\]$/);
  return typeName[1];
}

各オブジェクトのtoStringメソッドは、一番OverWriteしまくっているメソッドなので、

ココでは確実な動作を狙って、Object.prototype. toString をCall呼び出しで参照してみた。

でも名前がgetTypeNameなのがイマイチなんでObjectTypeNameにリファクタリングしたら、

他のファイルでUndefinedになってた。

JavScriptだから仕方が無いけど。

置換機能で書き換えようとしたら・・・

(サッパリ

検索すらしない。

ググっても、それっぽい記事は無い。

試しに「検索エディタ」を開いてみた。

赤線で囲んだアイコンをクリック

するとちゃんと検索しだした。

MSCodeを再起動しても、結果は良好。

つまり、裏側でこっそり設定ファイルが出来た様だ。

※この手の自動設定の不具合は、インストーラのテスト同様に見つけにくいものだ。

昔の映画で「xx製は叩けば治る!」というセリフがあったけど、MSCodeも同じらしい。

初回ダメなら「新規xx」とかをぶったたけば、初期設定ができるかも。

しかし、今後はどうなるか予断を許さない。(と思う。



[javascript]パーサ・コンビネータって

Java パーサコンビネータ 超入門というのを見つけた。

使い方を説明してくれるのでとても助かるけど、

JavaScriptでパーサコンビネータのコンセプトを理解する(「正規表現だけに頼ってはいけない」の続き)

の方がパーサ・コンビネータの仕組みを理解でき、且つ、使わなくなったけどJavascriptの便利な手法を思い出させてくれた。

function 関数(...) {
  return [1,2,3,4, ...];
}

と配列等で容易に複数の値を変えすことができるし、そんな関数を

let [a, b, c, d, ...] = 関数(...)

と呼び出すと、すんなり配列中の a, b, c, d, … に値を入れてくれるトコが地味に嬉しい。

いつのまにかいっぱい返す事になる未来が待っていそうな気がするから new classを返すことが多いけど。

また

function token(str) {
  var len = str.length;
  return function(target, position) {
    if (target.substr(position, len) === str) {
      return [true, str, position + len];
    } else {
      return [false, null, position];
    }
  };
}

の様にreturn function {…} することで、関数ジェネレータを簡易に記述できるのは便利、

但し、呼び出す側が、

token('foobar')('foobar', 0); // => [true, 'foobar', 6]を返す

な感じになってしまうとワークスペース内のファイルの関数を全部チェックする開発環境ではなく、

sakuraエディタで チョコ チョコ叩いている場合は、【見た目文法エラー!】なのが難点。

やはり、

const func = token('foobar');
func('foobar', 0); // => [true, 'foobar', 6]を返す

と書いて関数が戻ってくる雰囲気を醸し出した方が無難な気がする。

many(token('hoge'))('hogehoge', 0); // => [true, ['hoge', 'hoge'], 8] を返す

も、token(‘hoge’)部分がmanyな場合は、あーもーな感じになりそうなんで

let parsers = [];
parsers.push(token('hoge1'));
...
parsers.push(token('hogeN'));
let text = 'hoge1hoge2...hogeN';
let myParser =seq(parsers);
myParser(text, 0); // => [true, ['hoge1', ['hoge2', ...['hogeN']...]]], 8] を返す

と1行がいっぱい膨れ上がってしまいそう。

てか、実際に’hoge1hoge2’と直書きすることは滅多に無いから

もっと、もっと膨れ上がる。

先の記事のソースは「あくまで説明しやすく表記」したものなのは明白だけど、

テストコードってこんな風に書いてたりするよね?。(笑

ps.

先の「理解する」のページのコードをまとめてみた。

簡易のテストケースもパーサーの種類ごとに1つダケ書いてみたら、結構長くなってしまった。

作りかけだからFalseしまくってるのはいいとして

Errorオブジェクトから発生原因を判りやすくしてほしいなぁ。

字句解析とか構文解析は、「,」1つの修正だけでも

見えないところがトンデモない結果になり果ててしまいがちなので、テストはガチでやった方がいいからね。

ソースの説明

  • index.html   テスト画面
  • parcom.js パーサの記事からコードをコピペしてチョコっと加工したもの
  • sample.js パーサのテストパターンを起動し、結果を表示するスクリプト
  • testparcom.js  パーサのテストパターン
  • testbase.js    テストパターンを多少楽にするための testparcom.js の基底クラス
  • libs.js オブジェクトのダンプやErrorオブジェクトから情報を引き出したりするD級パーツ群
  • sample.css テスト画面用CSS

※ん-。まだまだな感じがする。

そもそも、各パーサはサブクラス化した方が良いんじゃにのかな?

更にパーサ コンテナ クラスで括った方が・・・。

アカン。どんどん膨れ上がる一方だ。

ps.2021/12/3

BNFっぽい文法”Call = [ Call ] procedureName [ (argumentList) ] ”と書かれたテキストを

解析するコードを作成中。やっと単純な文法をクリアしたものの、2つ目の文法に進まない。

デバッグでハマりすぎ長いログをスクロールするのが大変。

  • 機能を大幅改善
    • ログ末尾のstartとendを検出しリストタグ(UL,OL,LIとか)で手軽にインデント
      • 以前見つけたTreeViewのコードは流用できた
      • FIXTED:cssは全部見直しするハメになった
      • TODO:try…catch…filannyで確実にendを出力させないと右雪崩が発生する
    • TreeView風に[+][ーで折りたためる
      • TODO: +、ー小さすぎた!
      • TODO: endの行に+を配置するのは良くない。
    • trueやfalseを色分け
      • FIXED: あるあるな後付けデグレードと既存バグ発覚
        • objDumpで処理済みのテキストを再度objDumpしてが気が付かなかった
        • replaceで単語にタグ付けする場合は二度漬け禁止のハズが・・・
          • <span> ⇒ &lt;span> 的な出来事
    • FOXED:ログが雪だまり式に増えていた
      • Case2にCase1のログが
      • Case3にCase2とCase1のログが
      • 以下、増殖
        • 各テストCaseでログクリアする
    • TODO:typeof xxx === “undefined”はしてるけど、xxx === null チェックが甘い

コードの見直しが多すぎて、メソッド単体テストが壊滅状態。

修正してみたら、半壊状態。←いまこのへん。

全文法(287行中7行)をクリアするのはまだまだ先の話。

本当に終わりが見えないなぁ(大笑

            //      syntax = [ rule ]  ;
            setSyntax("syntax", many(refSyntax("defsymbol")));
            // defsymbol: { "\n" } IDENTIFIER "=" rule
            //###setSyntax("defsymbol", seq(optEol,  identifire,  asign,  refSyntax("rule")));
            setSyntax("defsymbol", seq(optEolWs, identifire, asign, refSyntax("rule")));
            // rule: list { "|" list }
            setSyntax("rule", seq(refSyntax("list"), repeat(seq(vl, refSyntax("list")))));
            // list: { seq | block | repeat | option }  "\n" { notes }
            // TODO: repeat(...)の中にoption系が挟まると無限ループするので、何か対策案を考える
            setSyntax("list", seq(repeat(choice(refSyntax("seqList"), refSyntax("block"), refSyntax("repeat"), refSyntax("option"))), eol, opt(refSyntax("notes"))));
            // seqList = seq { seqList }
            setSyntax("seqList", seq(refSyntax("seq"), repeat(seq(/*ws,*/ refSyntax("seqList")))));
            // seq = (regExp | IDENTIFIER | STRING | INTEGER | , )
            setSyntax("seq", choice(reExFmt, identifire, string, number, comma));
            // block: "(" rule ")"
            setSyntax("block", seq(lpr, refSyntax("rule"), rpr));
            // repeat: "{" rule "}"
            setSyntax("repeat", seq(lbr, refSyntax("rule"), rbr));
            // option: "[" rule "]"
            setSyntax("option", seq(lbk, refSyntax("rule"), rbk));
            // notes: "#Notes" "Syntax" eol rule "}}" "#Notes" "End" eol
            setSyntax("notes", seq(notes, ws, syntax, eol, refSyntax("syntax"), notes, ws, end, eol));

見直すのはコレだけなのに。

  • ログの大半がホワイトスペースだから何とかしたい
    • パーサから空白を読み飛ばす字句解析結果のを参照する?
    • パーサで作成したtoken(文字と正規表現)を適切な順に並べる
      • 勝手に空白を読み飛ばすので楽なハズ

クリアしたら、

  • 残りのテストを・・・
  • パーサコンテナを作る
  • 各パーサをサブクラス化
  • コードを読む部分を作る
  • 何かの言語に変換する部分を作る

先はまだまだ長い。

ps.2021/12/6

サブクラス化したもののソースは1000行を越えた。何か間違えている様だ。

サブクラス化 の過程で

  • リテラルにはプロパティが付けられない
    • String(“hohegoge”)ではダメ。
    • new String(“hohegoge”) でOK.
    • その辺は throw Error(“hogehoge”);も同じなんだろう。
  • RegExpにgオプションを付けlastIndexプロパティに位置を指定しても文字列の最初から検索する
    • 一度素振り(検索)させ自力でlastIndexを初期化しないとダメ
    • gは空振り(null)すると、とても機嫌が悪い。
  • RegExpのinputプロパティにオリジナルの文字列のコピーが作られる
    • メモリが心配
  • Refer.call を使ってみた
    • 横に長くなる
      • 伸びた部分だけ見れば良いので見やすいと思う人もいるかも
      • 元に戻す
  • super.メソッドみたいな呼び方ができる
    • なんちゃってオーバーライド風に書いてみると、無限ループする

18行1列まで読めた。 行末の _ 結合演算子がムズイ。

ps.2021/12/7

事前にザックリと空白、改行、その他でトークン化したついでにコメント文を空白化。後は _ かな。

全文からワード単位で字句解析するようになってブラウザの動作は軽くなった気がする。

ワード単位で取り出す時に、文末が” _”なら、次の改行を無視すればいいのかな?

ps.2021/12/8

手直しをしたら余計悪化。

0回ループOKなRepeatがネストしたら無限ループしてしまうので、Sequenceで評価がTrueでもpositionが進まない時は、評価をFalseにしたけど、まだループ。

仕方が無いので、catch exceptionで強制停止。300万行とか凄い行数になってたので、1000行までに制限。

やっと途中経過が可視化できた。ログのネストを深く掘り下げると

ParserInfo (ParserInfo)で、字句解析まで見れるけど。文法解析まで見れない。

ps.2021/12/9

手直し。 ダンプの仕方を見直し。

もう、本題を忘れてダンプに力が入る。

ps.2021/12/11

手直。 ダンプの仕方を見直し。  TreeView.markingをPromiseってみた。設定できてるけど、処理が終わったと返ってこない。

ps.2021/12/13

関数のダンプが修正漏れでエラっていたを訂正。



[HTML]表のタイトルロック

1.  position: sticky;  top: 0;

tableタグの上にタイトル行( thエレメント、tdエレメント )をしがみ付かせる方法。

このルールを trエレメントに付けてもあっさりスクロールしてしまう。

th エレメント やtd エレメント に1つづつ付けなくてはいけないので、列が多いと大変だから、

thead.hogeTable th,  thead.hogeTable td {
  position: sticky;
  top: 0;
}

な感じのCSSにすると良いと思う。

但し、この方法では、

  • thやtdの境界線はスクロールしてしまう
  • rowspanを使っている場合に2行目以降はロックしてくれない。

2. position: absolute;  top: nnnnpx;

画面にタイトル行(trエレメント)をネジ止めする方法。

thead.hogeTable tr {
  position: absolute;
  top: 0;
}

テーブルの位置が変わるとcssを修正しないといけないのがメンドイ。

だが、topを指定しないと、勝手にtableエレメントの上に固定してくれるので、これが案外楽かもしれない。

但し、この方法でも、

  • thやtdの境界線はスクロールしてしまう
  • rowspanを使っている場合に2行目以降は位置の微調整が大変。

なのは同じ。

3.javascriptで何とかする方法

ググれば色々と多数存在するので割愛するが、どれもスクロールバーのイベントに合わせて、Theadエレメントの位置を調整するもの。

それよりも、こそ-りと、THead部分をコピって別レイヤに貼り付ける方が楽な気がするのはボクだけだろうか?

テキスト内容で列幅が変わるのが気になるなら、元テーブルのtheadの各thの onresize イベントをフックして書き換えればいいような気がする。


  • カテゴリー:
  • HTML

Windows11 更新あるいはお試し

1.Windows Update

「準備ができたしたーし」

かかか、噛みました的な。

2.手動アップグレード リンク先のページから以下を選択

(1)Windows 11 インストール アシスタント

(2)Windows 11 のインストール メディアを作成する

(3)Windows 11 ディスク イメージ (ISO) をダウンロードする

(4)その他のダウンロード 企業向け

3.お試し方法

Hyper-Vで、手軽にお試しできる。

「新規」より楽そう
ISOファイルダウンロードとかは勝手にやってくれる
できた
何だこれは・・・

とりあえず

を押してみる
もう何がなんだが

やっぱりPCで動かしたいなら



[HTML]table クリックのエコー

HTMLの表をクリックした時に行選択状態にするには、チェックボックスのchecked やリンクのselectedの属性を使う方法もあったが、昔のブラウザならどんな属性も自由に付けられるからtrタグにcheckedするのもいいけど、変な使い方は無視するのが御時世なので、trタグに行選択クラスを加えるのが安心。

その為には、cssとjavascriptを少し書かないといけない。

注意点としては、

  • 表の背景色を一行ごとに変えてる場合はnth-child(…)の方が優先度が高いらしい。
    • 選択状態クラスの背景色に !import して優先度を変える。
  • thやtdで文字や背景の色を指定していると、trで色を変えても無関係で無視されてしまう。

かな。

なので、 ちと面倒だけど、thやtdにも 選択状態クラスを入れれば良い。

但し、javascriptの for .. in は、childNodes の要素の識別子(数値)を含むプロパティ名(文字列)を返してくるので、!isNaNでフィルターしないといけないし。childNodesには行中のスペース等のテキスト要素も含まれているので、classListプロパティがあるものだけ処理しないとエラってしまうから、配列でのfor..inは非推奨でfor..ofがお勧めとなっている。

こんな感じかな。

ちと大変と書いておいて、6行しか増えてないなぁ。

window.onload = function () {
  var tables = document.querySelectorAll("table");
  let addClassElement = null;
  for (var table of tables) {
    table.addEventListener('click', (event) => {
      var tr = event.target.parentElement;
      // 前回設定したものを回復
      if (addClassElement!=null) {
        addClassElement.toggleClassNameWithChilds('selected01');
        addClassElement = null;
      }
      // 行選択クラスをトグる
      addClassElement = tr.toggleClassNameWithChilds('selected01');
    }, false);
  }
}

// 指定クラスをトグる
if(!HTMLElement.prototype.toggleClassNameWithChilds) {
  HTMLElement.prototype.toggleClassNameWithChilds = function(className) {
    var addClassElement = null;
    if (this === undefined || this.classList === undefined) {
      return null;
    }
    if (!this.classList.contains(className)) {
      addClassElement = this;
    }
    this.classList.toggle(className);
    //配下のオブジェクトも反映
    for (var ch of this.childNodes) {
      // 未実装なオブジェクトを除外
      if(ch.toggleClassNameWithChilds !== undefined) {
        ch.toggleClassNameWithChilds(className);
      }
    }
    return addClassElement;
  }
}
tr:nth-child(even) {
  background-color: white;
}

tr:nth-child(odd) {
  background-color: lavender;
}

.selected01 {
  color: white;
  background-color: gray;
}

Element.classListにtoggleWithChildsとかを追加しようとしたけど、classListの生成タイミングが判らず諦めた。


  • カテゴリー:
  • HTML

【HTML】コンテンツとレイアウトの分離

ついつい改行したいからdivタグ、続けて並べるからspanとかつかって、HTMLドキュメントの内容がレイアウトとコンテンツがゴッチャゴチャになる。

当初TABLEタグが嫌われていたのも、音読するとイミフで嫌。これが一因。

極端な例としては、データ(XML)とレイアウト(XSLT)に分離してドヤ顔してるのもあるけど、フォーム風のCSVファイルっぽいXMLなら1ブロック1ページになってそう難しくないけど、各階層のデータブロックの数が可変長、なのに1ページに綺麗に収めたい等々の矛盾する要望が出る世の中なので使い道は無い。

HTMLを多少アレンジしたXHTMLも今更移行なんて無理ってことで黒歴史入りする様だ。

それに、よくよく考えれば、formのselectやoptionやbuttonなんかも音読の必要なんてなさそうで、どうなんかなぁ?


  • カテゴリー:
  • HTML

[HTML]Normalize.css

同じHTMLでもブラウザ毎の属性の設定が違うので、結構画面が荒れてしまう。

しかしcssリセットで全て無かったことにすると、H1とかH5のフォントサイズから考えないといけない。

そんな時に良い感じの初期設定に調整してくれるのがNormalize.css

効果はまぁまぁかな。

sanitize.cssの方が細かい調整がされているらしい。


  • カテゴリー:
  • HTML

「HTML」タブ

これだけのためにjQuery使ってた気がするけど、今ではCSSだけでも作れるっぽい。

というかCSSの書き方としては昔からある

各タブは、チェックボックスとこれに連携するラベルで構成する。

<input id="{タブ名}_tab" type="radio" name="tab_item" checked>
<label class="tab_item" for="{タブ名}_tab">{タブ・タイトル}</label>

チェックボックス 自体は表示する必要は無い。

input[name="tab_item"] {
    display: none;
}

タブことラベルをクリックすると チェックボックス の状態がcheckedに切り替わるので、チェックボックスに対応する兄弟のブロックを表示。

ここは、タブ名が変わったり増えたりするる度に修正しないといけない。

#{タブ名1}_tab:checked~#{タブ名1}_content,
#{タブ名2}_tab:checked~#{タブ名2}_content,
#{タブ名3}_tab:checked~#{タブ名3}_content {
/* 表示 */
    display: block;
/* または上下左右センタリング */
    display: flex;
    justify-content: center;
    align-items: center;
}

cssに正規表現はあるけど、ペアだけ対象にする書き方は無い。

[id$="_tab"]:checked  ~  [id$="_ content] {
どのタブをクリックしても、全部表示しようとする
}

なので 兄弟のブロック は初期非表示。

.tab_content {
    display: none;
    ...
}



top