最近はフロントエンド関連(JavaScript、HTML、CSS)をやってることが多いのですが、「どうやって実装するんだ……?」ということが度々ありました。
そういった内容について自分はどう実現したか、小ネタとして備忘録を残したいと思います。
JavaScript側からtableのスクロールを操作する
通常のtableであれば、普通にテーブル上でスクロールすれば、テーブルをスクロールさせることはできます。
ただ、他のイベントを拾うためなどでテーブルの上に別の要素がか被さっている場合は、上の要素にスクロールイベントが吸われてしまうため、テーブルをスクロールすることができません。
この解決策は、上の要素で拾ったスクロールの量を、テーブル(正確にはスクロール要素)のscrollTopに渡してやることです。
HTML。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<div id="upperLayer"></div> <div id="scrollTableView"> <table id="scrollTable"> <thead> <tr> <th>項目1</th> <th>項目2</th> <th>項目3</th> <th>項目4</th> <th>項目5</th> <th>項目6</th> <th>項目7</th> </tr> </thead> <tbody> <tr> <td>項目1</td> <td>項目2</td> <td>項目3</td> <td>項目4</td> <td>項目5</td> <td>項目6</td> <td>項目7</td> </tr> </tbody> </table> </div> |
テーブルをスクロールさせるためのCSS。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#scrollTableView{ overflow: auto; width: 350px; height: 150px; } table{ text-align: center; margin: 0; border-spacing: 0; } table thead{ position: sticky; top: 0; left: 0; } table tbody{ overflow-y: scroll; } |
JavaScript。
1 2 3 4 |
// 上の要素のスクロールで下のテーブルをスクロール document.getElementById("upperLayer").addEventListener("pointermove", (e)=>{ document.getElementById("scrollTableView").scrollTop -= e.movementY; }); |
座標からtableのインデックスを取得する
上述の件と同様に、テーブル上に別の要素が被さっている場合の話。
直接テーブルでイベントを拾えるならインデックスを取得する方法はいろいろあるのですが、テーブル上でイベントを拾えない場合はどうすればよいのでしょうか。
一つの方法として、クリックされた座標をスクロール量込みで取得し、1行あたりの高さで割ってやるというものがあります。
1 2 3 4 5 6 7 8 9 |
// 上の要素でクリックして下のテーブルのインデックスを取得 document.getElementById("upperLayer").addEventListener("click", (e)=>{ let rect = e.target.getBoundingClientRect(); let y = e.clientY - rect.top + document.getElementById("scrollTableView").scrollTop; let table = document.getElementById("scrollTable"); let index = Math.floor(y / table.tBodies[0].rows[0].offsetHeight) - 1; document.getElementById("yTablePoint").innerText = y; document.getElementById("tableIndex").innerText = index; }); |
e.clientY - rect.top
でテーブル上における座標を取得します(次の項目に図があります)。
ただこの座標はスクロール量が含まれていないので、スクロール要素.scrollTop
の値を足してやります。
あとは行の高さで割り、0始まりのインデックスにするために1を引いて求めることができます。
クリック位置をChart.jsのグラフ上の位置(x, y)に変換する
Chart.jsで描画されたグラフ上をクリックして、クリックした位置の(x, y)を取得します。
こんな感じ(下部に出力している座標は四捨五入した整数値です)。
HTML。
グラフを描画するchartと、クリック位置の座標を表示するlabel。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<div> <canvas id="chart"></canvas> </div> <div id="graphPointLabel"> <div class="graphPoint"> <label>x座標: </label> <label id="xPoint"></label> </div> <div class="graphPoint"> <label>y座標: </label> <label id="yPoint"></label> </div> </div> |
JavaScript。
myChartというのは、Chart.jsのグラフオブジェクトのことです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// グラフエリアをクリックして座標取得 document.getElementById("chart").addEventListener("click", (e)=>{ // グラフ上における座標 let rect = e.target.getBoundingClientRect(); let x = e.clientX - rect.left; let y = e.clientY - rect.top; // 座標変換 let xScale = myChart.scales.x; let yScale = myChart.scales.y; let xValue = xScale.getValueForPixel(x); let yValue = yScale.getValueForPixel(y); // 四捨五入 座標表示 document.getElementById("xPoint").innerText = Math.round(xValue); document.getElementById("yPoint").innerText = Math.round(yValue); // クリック位置に点を移動 if(myChart){ myChart.destroy(); } drawGraph(xValue, yValue); }); |
まずはグラフ上におけるクリック位置の座標を取得します。
左上を(0, 0)とする座標で、Window上の座標から、グラフのWindowの端からのサイズを引くことで求まります。
図解するとこう。
その後は、chart.scalesとgetValueForPixelによってグラフ上の(x, y)に変換してやります。
以前の任意の座標上に文字列を出力する時にも使用したものです。あれはグラフの(x, y)を画面上の座標に変換するものだったので、今回とは逆ですね。
テーブルで行単位の処理を実装する
ある行のある要素のイベントによって、同じ行の要素に対して何か操作をするテーブルを作成したいとします。
例えば、「×ボタンを押すと、同じ行のテキストがクリアされるテーブル」。
ボタンとテキストにそれぞれidを振っておいて、document.getElementById()でイベント取得や値操作をすることもできますが、数が多いと管理や処理を追加することも大変ですし、動的に個数が変化させる場合はidを割り当てておくことも、イベントをセットすることも、そもそも厳しいです。
いろいろな方法があるとは思いますが、今回は以下の方針で実装しました。
・JavaScriptで行ごとの要素を作成してTableにappendChild()する
・要素作成時にEventを追加する
→ 要素の特定にidが不要
JavaScript。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// 行単位での処理 let editTable = document.getElementById("editTable"); for(let i=0;i<6;i++){ let row = editTable.insertRow(-1); row.insertCell(0).appendChild(document.createTextNode(i+1)); let textBox = document.createElement("input"); textBox.setAttribute("type", "text"); textBox.setAttribute("class", "editTextBox"); textBox.setAttribute("value", `テスト${i+1}`); let deleteButton = document.createElement("button"); deleteButton.setAttribute("type", "button") deleteButton.innerText = "×"; let box = document.createElement("div"); box.setAttribute("class", "editBox"); box.appendChild(textBox); box.appendChild(deleteButton) row.insertCell(1).appendChild(box); deleteButton.addEventListener("click", function(){ textBox.value = ""; }); } |
まず行の要素を作成します。document.createElement()
で要素を作り、appendChild()
でセットしています。
要素の作成とセットの後、要素に対するイベントを追加します。
今回は×ボタン(deleteButton)が押されたときにテキストボックス(textBox)の内容を削除するイベントです。この行における×ボタンもテキストボックスも要素作成時の変数名で識別できるため、idで区別する必要がありません。
こうすることで行ごとの処理を作成することができます!
ただ今回は同じテーブル内で完結しているので良いのですが、全く他のイベントからテーブルの値を参照したい場合はidが必要なこともあります。
その場合は、”文字列”+ i などで連番のidをsetAttributeで付加が必要です。
任意のボタンの押下でselectの選択を開く
JavaScriptではなくHTMLとCSSなのですが、JavaScriptではできなかった話として書いておきます。
ドロップダウンを表示するselectタグですが、普通に使用した場合の表示は
こんな感じになると思います。
これを現在選択中の内容を表示しているパーツは表示せず、任意のボタンが押されたらドロップダウンを表示するようにします。
ようはこんなイメージ。
selectのドロップダウン表示イベントはJavaScriptから操作できない
いろいろ調べたのですが、どうも「selectのドロップダウン表示イベントはJavaScriptから操作できない」らしいです。そのため、任意のボタンクリックイベント内でselectのドロップダウン表示イベントを発火させるという実装ができません。
じゃあどうするかというと、
・ドロップダウンを表示させるためにはselectをクリックさせる
・ボタンを押してドロップダウン表示させているように見せるため、ボタンの上にボタンと同じサイズの透明selectを重ねる
という形で実現します。
HTML。
クリックでドロップダウンを表示させたい任意のボタンと、実際に表示するドロップダウンです。
1 2 3 4 5 6 7 8 9 |
<div class="item" id="selectPos"> <button id="openSelectButton">項目を選択する</button> <select id="select"> <option>HTML</option> <option>CSS</option> <option>JavaScript</option> </select> <label id="selectContent">選択項目:-</label> </div> |
CSSです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#selectPos{ position: relative; } #openSelectButton{ width: 30%; margin: 10px; } #select{ -webkit-appearance: none;/* ベンダープレフィックス(Google Chrome、Safari用) */ -moz-appearance: none; /* ベンダープレフィックス(Firefox用) */ appearance: none; position: absolute; left: 0; top: 61px; height: 26px; background: transparent; border: none; } #select:focus{ outline: none; } |
まずボタンに重ねるため、サイズをボタンと合わせ、position: absolute;
とtop
やleft
によって調整します。
そして見た目を消すために、border: none;
とbackground: transparent;
、加えてクリック時のfocus表示も消すために、:focus
で、outline: none;
を設定します。
これでselectを透明にできてそうですが、まだ右端にある開閉ボタン・下三角が消えていません。そこで以下のCSSを適用します。
1 2 3 |
-webkit-appearance: none;/* ベンダープレフィックス(Google Chrome、Safari用) */ -moz-appearance: none; /* ベンダープレフィックス(Firefox用) */ appearance: none; |
こうすることで、任意のボタンが押されたらドロップダウンを表示するという処理が実現できます。
まとめ
githubのコードはこちら。
githubのコードはこんな感じの全部盛りでHTML、CSS、JavaScriptを実装しています。
よれけば触ってみていただけると嬉しいです。
今後も何かあれば小ネタ集ということで随時上げていきたいと思います!
コメント