HTML
<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="charPalette.css"> </head> <body> <input id="charInput" type="text"> <input id="charButton" onclick="charPaletteExec()" type="submit" value="Query"> <span id="charNavi"></span> <table id="charPalette"></table> <div id="charNotes"> (説明文)Unicodeコードポイントor文字から検索して、…… </div> <script src="charPalette.js"></script> </body></html>
昨日は記事中に直接
JavaScript
/* coding: utf-8 */ // when Query button clicked function charPaletteExec() { var que = document.getElementById('charInput').value.split(/ *, */), btn = document.getElementById('charButton'), pre = btn.getAttribute('lastExec'), now = (new Date).getTime(); // prevent unintended pressing twice if (pre && (now - pre) < 500) return; btn.setAttribute('lastExec', (new Date).getTime()); getChar(que[0], que[1]); var h = document.getElementById('charNotes'); while (h.hasChildNodes()) h.removeChild(h.firstChild); } // main process function getChar(input, nrow, move) { // for surrogate pairs // c.f. http://webdev.seesaa.net/article/33141943.html // http://teppeis.hatenablog.com/entry/2014/01/surrogate-pair-in-javascript if (typeof input === 'string') { if (input.match(/^x[0-9a-f]+$/i)) { input = '0' + input; } else if (input.match(/^u\+[0-9a-f]+$/i)) { input = '0x' + input.slice(2); } } var num = parseInt(input), cp10, sgp; var cp10, sgp; // decimal code point, flag of surrogate pairs if (!input) { cp10 = Math.round(Math.random() * 0xD7FF); } else if (!isNaN(num)) { cp10 = num; sgp = Math.pow(2, 16) < cp10 ? true : false; } else { var x = input.charCodeAt(0), y = 1 < input.length ? input.charCodeAt(1) : 0, sgp = 0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF; cp10 = sgp ? (x - 0xD800) * 0x400 + y - 0xDC00 + 0x10000 : x; } if (!nrow) { nrow = 10; } else if (nrow > 50) { nrow = 50; } // create table, magnified character and controls setTable(cp10, nrow, sgp, move); addControls(cp10, nrow); } // create palette function setTable(cp10, nrow, sgp, move) { var tbl = document.getElementById('charPalette'), tr = document.createElement('tr'), td = document.createElement('td'); clearPalette(tbl); var r = tr.cloneNode(); for (var j = -1; j < 16; j++) { var c = td.cloneNode(), n = j.toString(16).toUpperCase(); if (j >= 0) c.appendChild(document.createTextNode(n)); r.appendChild(c); } tbl.appendChild(r); var cp10start = Math.floor(cp10 / 16) * 16; tbl.setAttribute('start', cp10start); if (!move) { tbl.setAttribute('searched', cp10); charEnlarge(cp10); } var searched = parseInt(tbl.getAttribute('searched')); for (var i = 0; i < nrow; i++) { var r = tr.cloneNode(), c = td.cloneNode(), n = cp10start + i * 16, n16 = 'U+' + n.toString(16).slice(0, -1).toUpperCase(); c.appendChild(document.createTextNode(n16)); r.appendChild(c); var tds = []; for (var j = 0; j < 16; j++) { var n10 = n + j, param = []; if (n10 === searched) param.push('class="clicked"'); param.push('nrow="' + i + '"'); param.push('ncol="' + j + '"'); param.push('onclick="clickCell(this)"'); var h = '<td ' + param.join(' ') + '>'; if (sgp) { h += '&#x' + n10.toString(16) + ';'; } else { h += String.fromCharCode(n10); } h += '</td>'; tds.push(h); } r.innerHTML += tds.join(''); // use innerHTML to set "onclick" when loaded with location.search tbl.appendChild(r); } } // when a character clicked function clickCell(cell) { if (!cell) return; // just in case cell.className = 'clicked'; var start = parseInt(cell.parentNode.parentNode.getAttribute('start')), nrow = cell.getAttribute('nrow'), ncol = cell.getAttribute('ncol'), cp10 = start + parseInt(nrow * 16) + parseInt(ncol); charEnlarge(cp10); } // add magnified character function charEnlarge(cp10) { // check existence of the same character var ext = document.getElementsByClassName('enlargeLabel'), cp10ext; if (typeof ext === 'object' && ext.length > 0) { for (var i = 0, len = ext.length; i < len; i++) { cp10ext = parseInt(ext[i].childNodes[2].nodeValue.slice(2, -1)); if (cp10 === cp10ext) { // remove the same character var obj = ext[i].parentNode; obj.parentNode.removeChild(obj); break; } } } var nts = document.getElementById('charNotes'), enl = document.createElement('div'), cls = document.createElement('div'), hex = cp10.toString(16).toUpperCase(); enl.className = 'enlarge'; enl.innerHTML = '<div class="enlargeChar">&#x' + hex + ';</div>' + '<div class="enlargeLabel">&#x' + hex + ';<br>&#' + cp10 + ';' + '<div class="enlargeClear"' + 'onclick="enlargeClose(this)">×</div>'; // use innerHTML to set "onclick" when loaded with location.search var clrFloat = document.getElementById('clearEnlarge'), p = nts.parentNode; if (!clrFloat) { clrFloat = document.createElement('div'); clrFloat.id = 'clearEnlarge'; p.insertBefore(clrFloat, nts); } // insert next to palette p.insertBefore(enl, document.getElementById('charPalette').nextSibling); } // remove magnified character function enlargeClose(obj) { var p = obj.parentNode.parentNode; if (!p || p.className !== 'enlarge') return; // just in case p.parentNode.removeChild(p); } function clearPalette(div) { while (div.hasChildNodes()) div.removeChild(div.firstChild); var mv = document.getElementById('charNavi'); if (mv) while (mv.hasChildNodes()) mv.removeChild(mv.firstChild); } function addControls(cp10, nrow) { var nav = document.getElementById('charNavi'), lnk = document.createElement('button'), obj = { '▲▲': -nrow, '▲': -1, '▼': 1, '▼▼': nrow * 1 }, min = Math.pow(2, 5), max = Math.pow(2, 16) + Math.pow(2, 20) - Math.pow(2, 11); for (var k in obj) { var target = cp10 + obj[k] * 16; if (target < min || max < target) continue; var h = '<button onclick="getChar(' + target + ', ' + nrow + ', true)">' + k + '</button>'; nav.innerHTML += h; } } // added process (function () { // when query string added if (location.search) { try { var que = decodeURIComponent(location.search.slice(1)); } catch (e) { console.log('unexpected error in parsing location.search'); } document.getElementById('charInput').value = que; charPaletteExec(); return; } })();
長いのでフレーム内スクロールで表示。大半はインターフェイス回りで、基本は
- 指定されたコードポイント
or 文字から、テーブル左上の起点コードポイントを決める - 起点から横に
16 字、縦に 10 行(または指定された行数)ずつ、文字を並べる
だけです。1.
CSS
#charInput { padding: 5px; } #charButton { margin-left: 0.25em; margin-right: 1em; } #charPalette { border-collapse: collapse; } #charPalette td { border: solid 1px lightblue; text-align: center; padding: 5px 5px 4px; } #charPalette tr:first-child td:first-child { border-style: none; } #charPalette tr:first-child, tr td:first-child { color: gray; font-family: monospace; } #charPalette tr:nth-child(n+2) td:nth-child(n+2) { cursor: pointer; font-size: 1.25em; } #charPalette .clicked { background: lightblue; } #charNavi { white-space: nowrap; } #charNavi button { font-size: 0.7em; margin-right: 0.5em; } .enlarge { border: solid lightblue 1px; float: left; margin-bottom: 10px; margin-right: 10px; padding: 0 10px 3px; position: relative; white-space: nowrap; } .enlargeClear { background: lightblue; cursor: pointer; color: white; font-size: 1.5em; position: absolute; right: 0; top: 0; } .enlargeChar { font-size: 4em; margin-right: 10px; } .enlargeLabel { font-family: monospace; } #clearEnlarge { clear: both; } #charNotes { text-align: left; }
こちらも長いのでフレーム内スクロールで。内容はパレット(テーブル)や拡大文字などのスタイル設定。一部で
使用例
以上の
↓ 昨日と同様、サロゲートペアの絵文字の一つ

↓ 実際どのフォントが使われたか

ローカルで