行列見出し固定 stickyTable contenteditable fileApi Blob JS
以前に行列見出し固定テーブルを作製 しました。最近は「position:sticky;」を使った行列見出し固定テーブルのサンプルをネットでいろいろ見かけるようになりましたので、自分なりにまとめてみました。
また編集可能にするためcontenteditable属性をつけ、FileApiでSave、Load機能をつけてみました。
Sample
| num | one | two | three | four | five | six | seven | eight | nine | ten |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 001 |
002 |
003 |
004 |
005 |
006 |
007 |
008 |
009 |
010 |
| 2 | 001 |
002 |
003 |
004 |
005 |
006 |
007 |
008 |
009 |
010 |
| 3 | 001 |
002 |
003 |
004 |
005 |
006 |
007 |
008 |
009 |
010 |
| 4 | 001 |
002 |
003 |
004 |
005 |
006 |
007 |
008 |
009 |
010 |
| 5 | 001 |
002 |
003 |
004 |
005 |
006 |
007 |
008 |
009 |
010 |
| 6 | 001 |
002 |
003 |
004 |
005 |
006 |
007 |
008 |
009 |
010 |
| 7 | 001 |
002 |
003 |
004 |
005 |
006 |
007 |
008 |
009 |
010 |
| 8 | 001 |
002 |
003 |
004 |
005 |
006 |
007 |
008 |
009 |
010 |
| 9 | 001 |
002 |
003 |
004 |
005 |
006 |
007 |
008 |
009 |
010 |
| 10 | 001 |
002 |
003 |
004 |
005 |
006 |
007 |
008 |
009 |
010 |
stickyテーブル
まずstickyテーブルですがヘッダー固定が可能となります。弱点はIEとEdgeは非対応です。これは「FixedMidashi」を使います。これで大体のブラウザ対応になります。最初からFixedMidashiだけでも良かったかもしれせん...
また、せっかくの行列見出し固定ですのでCSSでSPのSafariにスクロールを付加しました。
参考 SpcialThanks
CreaterClip様 StickyTableUnreviewed様 StickyTable
Qitta IOSリセット
FixedMidashi
行ホバー:hover/ 編集可能テーブルcontenteditable="true"
なるべく見やすいように行にホバーエフェクトも付加しました。hoverの順番が難しく webdev様を参考にしました。
また編集可能なようにcontenteditable="true"属性もつけてみました。CSV仕様のため、表に半角英数入力規制,崩れ防止のenter入力規制,3桁入力規制を付けました。なぜかIEでtdタグでは反応せずdivタグを入れ子にして反応させました。
FileApi Blob
FileApi/BlobでCSVファイルを保存し読込を可能にしました。CSVですのでゼロパディングしています。ただSafariがFileApiBlobにまだ対応しておらず使えません。そろそろ対応しそうな感じですのでここは非対応としました。
参考 SpcialThanks
FileApiBlobScript
<script src="js/fixed_midashi.js"></script>
Jquery
window.onload = function () {
var userAgent = window.navigator.userAgent.toLowerCase();
if (userAgent.match(/(msie|MSIE)/) || userAgent.match(/(T|t)rident/)) {
FixedMidashi.create();
} else if (userAgent.indexOf('edge') != -1) {
FixedMidashi.create();
} else if (userAgent.indexOf('chrome') != -1) {
/**safari SPとPCで挙動が違う。contenteditableでNG?***/
} else if (userAgent.indexOf('safari') != -1) {
if (userAgent.indexOf('mobile') == -1) {
FixedMidashi.create();
}
}
/********表の制限 日本語オフと3文字制限とEnter回避**/
$(document).ready(function () {
$('.edit').on('DOMSubtreeModified propertychange', function () {
var word = document.activeElement.innerHTML;
var index = $(".edit").index(this);
if (word.match(/[^\x00-\x7Eァ-ン゙゚]+/g)) {
$('.edit').eq(index).text('');
}
if (word.match(/^[a-zA-Z]+$/g)) {
$('.edit').eq(index).text('');
}
if (word.length >= 4 ) {
$('.edit').eq(index).text('');
}
});
});
$('.edit').keydown(function (e) {
if ((e.which === 13) || (e.keyCode === 13) || (e.which == 229) || (e.keyCode == 229)) {
e.keyCode = 0;
return false;
}
});
};
function handlecsv() {
var elements = document.getElementsByClassName("edit");
var len = elements.length;
var text = "";
var z = 0;
var i = 0;
while (i < len) {
switch (z) {
case 9:/*行のカウント-1*/
text = text + ("000" + document.getElementsByClassName("edit")[i].innerHTML.replace(/[^-^0-9^\.]/g, "")).slice(-3) + "\r\n";
z = 0;
break;
default:
text = text + ("000" + document.getElementsByClassName("edit")[i].innerHTML.replace(/[^-^0-9^\.]/g, "")).slice(-3) + ",";
z = z + 1;
break;
}
i = (i + 1) | 0;
}
var content = text;
/**safari SPとPCで挙動が違う。ファイル名不可**
var userAgent = window.navigator.userAgent.toLowerCase();
if (userAgent.indexOf('safari') != -1) {
if (userAgent.indexOf('mobile') == -1) {
window.open('data:attachment/csv;charset=utf-8,' + encodeURI(text));
}
}*/
var blob = new Blob([content], { "type": "text/csv" });
if (window.navigator.msSaveBlob) {
window.navigator.msSaveBlob(blob, "test.csv");
window.navigator.msSaveOrOpenBlob(blob, "test.csv");
} else {
document.getElementById("downloadcsv").href = window.URL.createObjectURL(blob);
}
};
/************loadCSV****************/
function readCsv2Table(filepath, encoding, resultId) {
var reader = new FileReader();
reader.onload = function (e) {
var splitLines = []; //
$(escape(e.target.result).split(/%0D%0A|%0D|%0A/ig)).each(function (y, line) {
if (line == "") return;
splitLines.push(unescape(line));
});
// 文字列ではなくて、メソッド指定なら、引数にCSVデータセットして
// 呼び出す。
if (typeof resultId === "function") {
resultId.apply(null, [splitLines]);
return;
}
var count = 0;
$(splitLines).each(function (y, line) {
var columns = $(line.split(/,/));
columns.each(function (x, o) {
//alert(this);
//alert(count);
$(".edit").eq(count).text(this);
count = count + 1;
});
});
/*イベントon*/
$(document).ready(function () {
$('.edit').on('DOMSubtreeModified propertychange', function () {
var word = document.activeElement.innerHTML;
var index = $(".edit").index(this);
if (word.match(/[^\x00-\x7Eァ-ン゙゚]+/g)) {
$('.edit').eq(index).text('');
}
if (word.match(/^[a-zA-Z]+$/g)) {
$('.edit').eq(index).text('');
}
if (word.length >= 4) {
$('.edit').eq(index).text('');
}
});
});
}; // end of onload descriptions.
// execute read file.
reader.readAsText(filepath, encoding); //shift-jis
}
$(function () {
$("#myButton").click(function (e) {
/*イベントoff*/
$('.edit').off('DOMSubtreeModified propertychange');
var filePath = document.getElementById("myFile").files[0];
if (typeof filePath === "object") {
/*
readCsv2Table(filePath, "utf-8", function(arr) {
$(arr).each(function(i,o) {
console.log("[" + i + "]=>" + o);
});
});
*/
readCsv2Table(filePath, "shift-jis", "result");
}
});
});
css
/*******リセット*************/
label,
button,
input{
background-color: transparent;
border: none;
cursor: pointer;
outline: none;
padding: 0;
appearance: none;
}
/* iOSでのデフォルトスタイルをリセット */
input[type="submit"],
input[type="button"] {
border-radius: 0;
-webkit-box-sizing: content-box;
-webkit-appearance: button;
appearance: button;
border: none;
box-sizing: border-box;
cursor: pointer;
}
input[type="submit"]::-webkit-search-decoration,
input[type="button"]::-webkit-search-decoration {
display: none;
}
input[type="submit"]::focus,
input[type="button"]::focus {
outline-offset: -2px;
}
/********Table****************/
div.scrollable {
width:90%;
max-width:530px;
max-height:228px;
overflow-y: auto;
overflow-x: auto;
/*border-collapse: collapse;/*効かない*/
/*border-spacing: 0;/*効かない*/
table-layout:fixed;/*横幅固定*/
}
th, td {
position:relative;
padding:2px 10px;
text-align:center;
vertical-align:middle;
border:1px solid #CCCCCC;
/*box-sizing:border-box;*/
min-width:80px;
}
.scrollable .sticky {
position: sticky;
position: -webkit-sticky;
}
div.edit{
/*border:1px solid #CCCCCC;*/
max-width:80px;
min-height:20px;
}
.scrollable .sticky.row {
top: 0;
z-index: 2;
background-color: #faebeb;
height: 22px;
}
.scrollable .sticky.col {
left: 0;
background-color: #faebeb;
height: 22px;
z-index: 1;
}
/*safari SPでスクロールを出す。*/
.scrollable{
overflow-y: auto;
overflow-x: auto;
}
.scrollable::-webkit-scrollbar {
height: 10px;
width: 10px
}
.scrollable::-webkit-scrollbar-thumb{
background: #ccc;
border-radius: 5px;
}
.scrollable::-webkit-scrollbar-track-piece {
background: #eee;
}
/*hover*/
table col:nth-child(2n+3){
background: #eee;
}
table:hover tbody td:nth-child(1) {
color: #ccc;
background: #dedede;
}
table:hover tbody tr:hover td {
color: #666;
background: #cef;
}
table:hover tbody tr:hover th.sticky.col{
background: #fdd8d8;
}
table:hover tbody:hover td:hover {
opacity: 1;
color: #fff;
background: #69f;
}
/*****button**********/
input[type=button],
.square_btn{
font-size:1em;/**Label サイズ合わせ**/
font-family: "Meiryo";/**Label サイズ合わせ**/
padding: .2em 0.8em;
text-align: center;
margin:.4em 0em .4em 0em;/*[上][右][下][左]*/
min-width:70px;
background-color:#69f;
border-radius: 0.3em;
color: #fff;
box-shadow: 0 3px 3px rgba(0, 0, 0, .2), inset 0 1px 1px rgba(255, 255, 255, .7);
text-shadow: 0px 1px 0px rgba(0, 0, 0, .4);
text-decoration: none;
cursor: pointer;
display:inline-block;
width: 80px
}
.square_btn:active{
background-color:#cef;
}
htm
<div class="scrollable"> <table _fixedhead="rows:1; cols:1;"> <thead> <colgroup><col><col><col><col><col><col><col><col><col><col><col></colgroup> <tr> <th class="sticky col">num</th> <th class="sticky row">one</th> <th class="sticky row">two</th> <th class="sticky row">three</th> <th class="sticky row">four</th> <th class="sticky row">five</th> <th class="sticky row">six</th> <th class="sticky row">seven</th> <th class="sticky row">eight</th> <th class="sticky row">nine</th> <th class="sticky row">ten</th> </tr> </thead> <tbody> <tr> <th class="sticky col">1</th> <!-- tdにcontenteditable IEでNG --> <td><div class="edit" contenteditable="true">001</div></td> <td><div class="edit" contenteditable="true">002</div></td> <td><div class="edit" contenteditable="true">003</div></td> <td><div class="edit" contenteditable="true">004</div></td> <td><div class="edit" contenteditable="true">005</div></td> <td><div class="edit" contenteditable="true">006</div></td> <td><div class="edit" contenteditable="true">007</div></td> <td><div class="edit" contenteditable="true">008</div></td> <td><div class="edit" contenteditable="true">009</div></td> <td><div class="edit" contenteditable="true">010</div></td> </tr> <tr> <th class="sticky col">2</th> <td><div class="edit" contenteditable="true">001</div></td> <td><div class="edit" contenteditable="true">002</div></td> <td><div class="edit" contenteditable="true">003</div></td> <td><div class="edit" contenteditable="true">004</div></td> <td><div class="edit" contenteditable="true">005</div></td> <td><div class="edit" contenteditable="true">006</div></td> <td><div class="edit" contenteditable="true">007</div></td> <td><div class="edit" contenteditable="true">008</div></td> <td><div class="edit" contenteditable="true">009</div></td> <td><div class="edit" contenteditable="true">010</div></td> </tr> <tr> <th class="sticky col">3</th> <td><div class="edit" contenteditable="true">001</div></td> <td><div class="edit" contenteditable="true">002</div></td> <td><div class="edit" contenteditable="true">003</div></td> <td><div class="edit" contenteditable="true">004</div></td> <td><div class="edit" contenteditable="true">005</div></td> <td><div class="edit" contenteditable="true">006</div></td> <td><div class="edit" contenteditable="true">007</div></td> <td><div class="edit" contenteditable="true">008</div></td> <td><div class="edit" contenteditable="true">009</div></td> <td><div class="edit" contenteditable="true">010</div></td> </tr> <tr> <th class="sticky col">4</th> <td><div class="edit" contenteditable="true">001</div></td> <td><div class="edit" contenteditable="true">002</div></td> <td><div class="edit" contenteditable="true">003</div></td> <td><div class="edit" contenteditable="true">004</div></td> <td><div class="edit" contenteditable="true">005</div></td> <td><div class="edit" contenteditable="true">006</div></td> <td><div class="edit" contenteditable="true">007</div></td> <td><div class="edit" contenteditable="true">008</div></td> <td><div class="edit" contenteditable="true">009</div></td> <td><div class="edit" contenteditable="true">010</div></td> </tr> <tr> <th class="sticky col">5</th> <td><div class="edit" contenteditable="true">001</div></td> <td><div class="edit" contenteditable="true">002</div></td> <td><div class="edit" contenteditable="true">003</div></td> <td><div class="edit" contenteditable="true">004</div></td> <td><div class="edit" contenteditable="true">005</div></td> <td><div class="edit" contenteditable="true">006</div></td> <td><div class="edit" contenteditable="true">007</div></td> <td><div class="edit" contenteditable="true">008</div></td> <td><div class="edit" contenteditable="true">009</div></td> <td><div class="edit" contenteditable="true">010</div></td> </tr> <tr> <th class="sticky col">6</th> <td><div class="edit" contenteditable="true">001</div></td> <td><div class="edit" contenteditable="true">002</div></td> <td><div class="edit" contenteditable="true">003</div></td> <td><div class="edit" contenteditable="true">004</div></td> <td><div class="edit" contenteditable="true">005</div></td> <td><div class="edit" contenteditable="true">006</div></td> <td><div class="edit" contenteditable="true">007</div></td> <td><div class="edit" contenteditable="true">008</div></td> <td><div class="edit" contenteditable="true">009</div></td> <td><div class="edit" contenteditable="true">010</div></td> </tr> <tr> <th class="sticky col">7</th> <td><div class="edit" contenteditable="true">001</div></td> <td><div class="edit" contenteditable="true">002</div></td> <td><div class="edit" contenteditable="true">003</div></td> <td><div class="edit" contenteditable="true">004</div></td> <td><div class="edit" contenteditable="true">005</div></td> <td><div class="edit" contenteditable="true">006</div></td> <td><div class="edit" contenteditable="true">007</div></td> <td><div class="edit" contenteditable="true">008</div></td> <td><div class="edit" contenteditable="true">009</div></td> <td><div class="edit" contenteditable="true">010</div></td> </tr> <tr> <th class="sticky col">8</th> <td><div class="edit" contenteditable="true">001</div></td> <td><div class="edit" contenteditable="true">002</div></td> <td><div class="edit" contenteditable="true">003</div></td> <td><div class="edit" contenteditable="true">004</div></td> <td><div class="edit" contenteditable="true">005</div></td> <td><div class="edit" contenteditable="true">006</div></td> <td><div class="edit" contenteditable="true">007</div></td> <td><div class="edit" contenteditable="true">008</div></td> <td><div class="edit" contenteditable="true">009</div></td> <td><div class="edit" contenteditable="true">010</div></td> </tr> <tr> <th class="sticky col">9</th> <td><div class="edit" contenteditable="true">001</div></td> <td><div class="edit" contenteditable="true">002</div></td> <td><div class="edit" contenteditable="true">003</div></td> <td><div class="edit" contenteditable="true">004</div></td> <td><div class="edit" contenteditable="true">005</div></td> <td><div class="edit" contenteditable="true">006</div></td> <td><div class="edit" contenteditable="true">007</div></td> <td><div class="edit" contenteditable="true">008</div></td> <td><div class="edit" contenteditable="true">009</div></td> <td><div class="edit" contenteditable="true">010</div></td> </tr> <tr> <th class="sticky col">10</th> <td><div class="edit" contenteditable="true">001</div></td> <td><div class="edit" contenteditable="true">002</div></td> <td><div class="edit" contenteditable="true">003</div></td> <td><div class="edit" contenteditable="true">004</div></td> <td><div class="edit" contenteditable="true">005</div></td> <td><div class="edit" contenteditable="true">006</div></td> <td><div class="edit" contenteditable="true">007</div></td> <td><div class="edit" contenteditable="true">008</div></td> <td><div class="edit" contenteditable="true">009</div></td> <td><div class="edit" contenteditable="true">010</div></td> </tr> </tbody> </table> </div> <label class="square_btn" for="myFile" >OPEN</label> <input type="file" style="display:none" id="myFile" /> <input type="button" class="square_btn" id="myButton" value="LOAD" /> <a id="downloadcsv" class="square_btn" href="#" download="test.csv" onclick="handlecsv()">SAVE</a>
