No.16
今は便利な名前変換機能(単語変換機能)スクリプトを配布してくださっている方がいらっしゃいます。
私が自分のサイトに実装した当時、求めている機能が微妙になかったため「作るっきゃねえ!」の精神で自作しました。
デモ
こちらのデモページから動作確認ができます。
仕様
①名字・名前・名字ひらがな・名前ひらがなを変換
②名字と名前はユニークなものを前提としている
③ひらがなは[RB:吃 > ども]りなどで利用
④小説ページごとにデフォルト名が異なっていても対応可能
⑤Cookieを利用
⑥変換フォームのプレースホルダーには変換した名前を入れておく。
ユニークなものとは、重複しない独特なものを意味しています。本文で使われない単語のことです。
例えば、名前が「空」だと「青空の下に広がる海。」など本文に「空」の文字が入っていると仕様上その文字も変換されてしまいます。
基本私が小説を書くときは名前変換を前提としておらず、名前を決めて執筆しています。
htmlに持っていくときに名前の部分にタグ付けがいちいち面倒だな……と思い、JavaScriptの文字列変換を利用して書き換える方向にしました。
ただひらがなはなかなか難しかったため、ひらがなだけタグをつけています。
(私の小説ではそこまで多用しなかったので妥協しました)
ファイル構成
index.html
contents.html
css/style.css
js.name-change.js
今回はjQueryを使いません。
Cookie処理はかわりにjs-cookieを利用しました。
JavaScriptの読み込み
名前変換のフォームがあるページと、名前変換処理をするページで読み込みます。
<!-- js-cookie -->
<script src="https://cdn.jsdelivr.net/npm/js-cookie@3.0.5/dist/js.cookie.min.js"></script>
<!-- script -->
<script src="js/name-change.js"></script>
js-cookieのCDNはこちらからコピペしています。
Cookie操作をしやすいように利用しています。
名前変換フォーム
今回index.htmlに書いてる名前変換のフォームです。
なお、デザインに関するクラスは省いています。
<form name="d_name">
<input type="text" placeholder="名字" name="lastname">
<input type="text" placeholder="名前" name="firstname"><br>
<input type="text" placeholder="みょうじ" name="lastname_hira">
<input type="text" placeholder="なまえ" name="firstname_hira"><br>
<button type="button" onclick="setName()">変換</button>
<button type="button" onclick="resetName()">リセット</button>
</form>
<div id="setName"></div>
onclickの処理はjs/name-change.jsファイルに記述します。
<div id="setName"></div>は変換ボタンやリセットボタンを押した後の結果を表示するためのものです。
style.css
上記<div id="setName"></div>の設定です。
#setName {
display: none;
/* 下記はデザイン用 */
margin: 15px 0;
padding: 15px;
border: 1px solid transparent;
border-radius: 3px;
margin: 0 0 10px;
padding: 8px;
}
#setName.change,#setName.reset {
display: block;
}
#setName.change::after {
content: "名前を変換しました。";
}
#setName.reset::after {
content: "名前をリセットしました。";
}
/* 下記はデザイン用 */
#setName.change,#setName.reset {
border-color: #9ad4de;
background-color: rgba(154, 212, 222, 0.25);
}
名前変換ありの本文
記述はこのようにします。
<div id="d-name-default" data-last="デフォルト名字" data-first="デフォルト名前" data-last-hira="でふぉるとみょうじ" data-first-hira="でふぉるとなまえ"></div>
<div id="d-name-nvl">
<!-- 本文 -- >
</div>
一つずつ解説していきます。
<div id="d-name-default" data-last="デフォルト名字" data-first="デフォルト名前" data-last-hira="でふぉるとみょうじ" data-first-hira="でふぉるとなまえ"></div>
タグはdivにしていますがなんでもいいです。こちらは
<script src="js/name-change.js"></script>
より上に記述してあればどこに記述しても構いません。今回は本文の前に記述しています。
id="d-name-default"でJavaScriptから取得できるようにします。
data-last=""はデフォルトの名字を入れます。省略可能です。
data-first=""はデフォルトの名前を入れます。省略可能です。
data-last-hira=""はデフォルトの名字のひらがな(読み方)を入れます。省略可能です。
data-first-hira=""はデフォルトの名前のひらがな(読み方)を入れます。省略可能です。
この省略可能は、本文で使用しない場合に記述しなくていいようにするものです。
id="d-name-nvl"の中に小説本文を流し込みます。これは上記で設定したデフォルト名と変換フォームで設定した名前を置き換えるためのものです。このidがないと変換されないので注意です。
ひらがなのタグ
名字や名前のひらがな部分にはタグを追加します。
デモ版の場合はこのようにしてます。
「俺は<em>愛上陵</em>!”<em><span class="last-hira">あいうえ</span></em><em><span class="first-hira">おか</span></em>”って読むんだぜ!珍しいだろ?」<br>
──<em><span class="last-hira">あ……いう……</span></em>──<br>
「ん?いまなにか聞こえたか?」<br>
──<em><span class="last-hira">あい……うえ……</span></em><em><span class="first-hira">お……か</span></em>──<br>
「やっぱ誰かに呼ばれてる!」
名字のひらがなを
<span class="last-hira"></span>
名前のひらがなを
<span class="first-hira"></span>
で囲みます。
このとき間に三点リーダ(…)や句読点が入っても大丈夫です。
もしひらがなの部分に別のタグ(強調タグやルビタグなど)を使いたい場合は、spanタグの外側に設置します。
name-change.js
処理はこちらです。
/* 名前設定 */
function setName() {
const formEl = document.forms.d_name;
let change = false;
change = setItem(formEl.lastname,'lastname');
change = setItem(formEl.firstname,'firstname');
change = setItem(formEl.lastname_hira,'lastname_hira');
change = setItem(formEl.firstname_hira,'firstname_hira');
if (!change) { return; }
document.getElementById("setName").classList.remove("reset");
document.getElementById("setName").classList.add("change");
/* 同じページ内に変換箇所がある場合 */
changeName();
}
/* 項目別 */
function setItem(elName,cookieId){
const name = elName ? elName.value : '';
if(name === null || name === '') return false;
Cookies.set(cookieId, name);
return true;
}
/* 名前変換時にテキストエリアを変換した名前に保持 */
function keepName() {
const formEl = document.forms.d_name;
if (!formEl) { return; }
keepItem(formEl.lastname,'lastname');
keepItem(formEl.firstname,'firstname');
keepItem(formEl.lastname_hira,'lastname_hira');
keepItem(formEl.firstname_hira,'firstname_hira');
document.getElementById("setName").classList.remove("reset");
document.getElementById("setName").classList.remove("change");
}
/* 項目別 */
function keepItem(elName,cookieId){
let input = elName ?? null;
if(input === null) return;
const name = Cookies.get(cookieId) ?? input.placeholder;
if(input.placeholder !== name){
input.value = name;
}
}
/* 名前リセット */
function resetName() {
const formEl = document.forms.d_name;
let reset = resetItem(formEl.lastname,'lastname');
reset = resetItem(formEl.firstname,'firstname');
reset = resetItem(formEl.lastname_hira,'lastname_hira');
reset = resetItem(formEl.firstname_hira,'firstname_hira');
if(reset){
ajaxReload(); // 同一ページに変換箇所がある場合
document.getElementById("setName").classList.remove("change");
document.getElementById("setName").classList.add("reset");
}
}
/* 項目別 */
function resetItem(elName,coolieId){
Cookies.remove(coolieId);
if (!elName) return false;
else elName.value = '';
return true;
}
/* 名前変換 */
function changeName() {
let d_name_nvl = document.getElementById('d-name-nvl');
if (d_name_nvl === null) { return; }
let str = changeItem('lastname', 'data-last', d_name_nvl.innerHTML);
str = changeItem('firstname', 'data-first', str);
d_name_nvl.innerHTML = str;
changeHiragana('lastname_hira', 'data-last-hira', 'last-hira');
changeHiragana('firstname_hira', 'data-first-hira', 'first-hira');
}
/* 項目別 */
function changeItem(cookieId,attrName,str){
const data_d = document.getElementById('d-name-default').getAttribute(attrName);
const name = Cookies.get(cookieId);
if(name && data_d) return str.replace(new RegExp(data_d,'g'),name);
return str;
}
/* ひらがな対応 */
function changeHiragana(cookieId,attrName,className){
const data_d = document.getElementById('d-name-default').getAttribute(attrName);
const name = Cookies.get(cookieId);
if(!(name && data_d)) return;
let arr = document.getElementsByClassName(className);
for(i = 0;i<arr.length;i++){
let el = arr[i];
let defHira = data_d.split('');
let nameHira = name.split('');
let str = el.innerText.split('');
let txt ='';
let nameCount = 0;
for (k = 0; k < str.length; k++) {
if(!str[k].match(/^[ぁ-んー ]+$/) || defHira.length <= nameCount){
txt += str[k];
}else{
let replace;
if(nameCount < nameHira.length){
replace = nameHira[nameCount];
txt += str[k].replace(defHira[nameCount], replace);
nameCount++;
}else if(nameHira.length < defHira.length){
// 設定された名前のほうが短ければ以降はつけない
break;
}
}
}
// 不足分追加
if(defHira.length < nameHira.length){
let substr = name.substr(nameCount);
txt += substr;
}
el.innerText = txt;
}
}
/* 同じページに変換がある場合 */
function ajaxReload(){
let d_name_nvl = document.getElementById('d-name-nvl');
if (d_name_nvl) {
let url = location.href + "?date="+new Date().getTime();
let ajax = new XMLHttpRequest;
ajax.open("GET",url,true);
ajax.onload = function(){
var res = ajax.responseText;
var parse = new DOMParser().parseFromString(res,"text/html");
d_name_nvl.innerHTML = parse.getElementById('d-name-nvl').innerHTML;
};
ajax.send(null);
}
}
/* 読み込み直後 */
window.onload = function () {
/* 入力フォームの設定 */
keepName();
/* 名前変換 */
changeName();
};
同一ページに変換箇所がある場合の処理も追記しておきました。
もしない場合はその部分は削除して大丈夫です。
名前を変換する場合は文字を置き換え、リセットする場合は名前変換のあるタグ(d_name_nvl)の中だけをajaxをつかってリロードしています。
一部のみリロードについてはjavascriptでページを一部だけ更新する方法を参考にしました。
ajaxでもどってくる内容はテキストデータなので、htmlにパースして利用しています。テキストのパースは【JavaScript】DOMParserで文字列をHTMLElement・Nodeに変換するを参考にしました。
最後に
実装したのは割と前で記事にしようとしてなかなかできていなかったので、ようやく記事にできて嬉しいです。
「もっと汎用性がほしい!」「もっと使いやすい方がいい!」「全ページ同じにしたい!」と言った場合は、すでに配布されている素晴らしいスクリプトがありますのでそちらを使っていただくといいかもしれないです。
私の場合「名前にいちいちタグしたくない」「デフォルト名が小説によって違う」と自分で用意したほうが良さそうだったので用意してみました。
同じように自作しようとしている方の参考になれば幸いですっ
それと記事に関係ないですが、Twitter(現X)アカウントを作成しました!
Twitter(現X)アカウント てくてく@個人サイト向け情報共有サイト
よければフォローして下さいませ♪
参考
js-cookie
CDN js-cookie
javascriptでページを一部だけ更新する方法
JavaScript】DOMParserで文字列をHTMLElement・Nodeに変換する
DOMParser の parseFromString() メソッドの使い方
私が自分のサイトに実装した当時、求めている機能が微妙になかったため「作るっきゃねえ!」の精神で自作しました。
デモ
こちらのデモページから動作確認ができます。
仕様
①名字・名前・名字ひらがな・名前ひらがなを変換
②名字と名前はユニークなものを前提としている
③ひらがなは[RB:吃 > ども]りなどで利用
④小説ページごとにデフォルト名が異なっていても対応可能
⑤Cookieを利用
⑥変換フォームのプレースホルダーには変換した名前を入れておく。
ユニークなものとは、重複しない独特なものを意味しています。本文で使われない単語のことです。
例えば、名前が「空」だと「青空の下に広がる海。」など本文に「空」の文字が入っていると仕様上その文字も変換されてしまいます。
基本私が小説を書くときは名前変換を前提としておらず、名前を決めて執筆しています。
htmlに持っていくときに名前の部分にタグ付けがいちいち面倒だな……と思い、JavaScriptの文字列変換を利用して書き換える方向にしました。
ただひらがなはなかなか難しかったため、ひらがなだけタグをつけています。
(私の小説ではそこまで多用しなかったので妥協しました)
ファイル構成
index.html
contents.html
css/style.css
js.name-change.js
今回はjQueryを使いません。
Cookie処理はかわりにjs-cookieを利用しました。
JavaScriptの読み込み
名前変換のフォームがあるページと、名前変換処理をするページで読み込みます。
<!-- js-cookie -->
<script src="https://cdn.jsdelivr.net/npm/js-cookie@3.0.5/dist/js.cookie.min.js"></script>
<!-- script -->
<script src="js/name-change.js"></script>
js-cookieのCDNはこちらからコピペしています。
Cookie操作をしやすいように利用しています。
名前変換フォーム
今回index.htmlに書いてる名前変換のフォームです。
なお、デザインに関するクラスは省いています。
<form name="d_name">
<input type="text" placeholder="名字" name="lastname">
<input type="text" placeholder="名前" name="firstname"><br>
<input type="text" placeholder="みょうじ" name="lastname_hira">
<input type="text" placeholder="なまえ" name="firstname_hira"><br>
<button type="button" onclick="setName()">変換</button>
<button type="button" onclick="resetName()">リセット</button>
</form>
<div id="setName"></div>
onclickの処理はjs/name-change.jsファイルに記述します。
<div id="setName"></div>は変換ボタンやリセットボタンを押した後の結果を表示するためのものです。
style.css
上記<div id="setName"></div>の設定です。
#setName {
display: none;
/* 下記はデザイン用 */
margin: 15px 0;
padding: 15px;
border: 1px solid transparent;
border-radius: 3px;
margin: 0 0 10px;
padding: 8px;
}
#setName.change,#setName.reset {
display: block;
}
#setName.change::after {
content: "名前を変換しました。";
}
#setName.reset::after {
content: "名前をリセットしました。";
}
/* 下記はデザイン用 */
#setName.change,#setName.reset {
border-color: #9ad4de;
background-color: rgba(154, 212, 222, 0.25);
}
名前変換ありの本文
記述はこのようにします。
<div id="d-name-default" data-last="デフォルト名字" data-first="デフォルト名前" data-last-hira="でふぉるとみょうじ" data-first-hira="でふぉるとなまえ"></div>
<div id="d-name-nvl">
<!-- 本文 -- >
</div>
一つずつ解説していきます。
<div id="d-name-default" data-last="デフォルト名字" data-first="デフォルト名前" data-last-hira="でふぉるとみょうじ" data-first-hira="でふぉるとなまえ"></div>
タグはdivにしていますがなんでもいいです。こちらは
<script src="js/name-change.js"></script>
より上に記述してあればどこに記述しても構いません。今回は本文の前に記述しています。
id="d-name-default"でJavaScriptから取得できるようにします。
data-last=""はデフォルトの名字を入れます。省略可能です。
data-first=""はデフォルトの名前を入れます。省略可能です。
data-last-hira=""はデフォルトの名字のひらがな(読み方)を入れます。省略可能です。
data-first-hira=""はデフォルトの名前のひらがな(読み方)を入れます。省略可能です。
この省略可能は、本文で使用しない場合に記述しなくていいようにするものです。
id="d-name-nvl"の中に小説本文を流し込みます。これは上記で設定したデフォルト名と変換フォームで設定した名前を置き換えるためのものです。このidがないと変換されないので注意です。
ひらがなのタグ
名字や名前のひらがな部分にはタグを追加します。
デモ版の場合はこのようにしてます。
「俺は<em>愛上陵</em>!”<em><span class="last-hira">あいうえ</span></em><em><span class="first-hira">おか</span></em>”って読むんだぜ!珍しいだろ?」<br>
──<em><span class="last-hira">あ……いう……</span></em>──<br>
「ん?いまなにか聞こえたか?」<br>
──<em><span class="last-hira">あい……うえ……</span></em><em><span class="first-hira">お……か</span></em>──<br>
「やっぱ誰かに呼ばれてる!」
名字のひらがなを
<span class="last-hira"></span>
名前のひらがなを
<span class="first-hira"></span>
で囲みます。
このとき間に三点リーダ(…)や句読点が入っても大丈夫です。
もしひらがなの部分に別のタグ(強調タグやルビタグなど)を使いたい場合は、spanタグの外側に設置します。
name-change.js
処理はこちらです。
/* 名前設定 */
function setName() {
const formEl = document.forms.d_name;
let change = false;
change = setItem(formEl.lastname,'lastname');
change = setItem(formEl.firstname,'firstname');
change = setItem(formEl.lastname_hira,'lastname_hira');
change = setItem(formEl.firstname_hira,'firstname_hira');
if (!change) { return; }
document.getElementById("setName").classList.remove("reset");
document.getElementById("setName").classList.add("change");
/* 同じページ内に変換箇所がある場合 */
changeName();
}
/* 項目別 */
function setItem(elName,cookieId){
const name = elName ? elName.value : '';
if(name === null || name === '') return false;
Cookies.set(cookieId, name);
return true;
}
/* 名前変換時にテキストエリアを変換した名前に保持 */
function keepName() {
const formEl = document.forms.d_name;
if (!formEl) { return; }
keepItem(formEl.lastname,'lastname');
keepItem(formEl.firstname,'firstname');
keepItem(formEl.lastname_hira,'lastname_hira');
keepItem(formEl.firstname_hira,'firstname_hira');
document.getElementById("setName").classList.remove("reset");
document.getElementById("setName").classList.remove("change");
}
/* 項目別 */
function keepItem(elName,cookieId){
let input = elName ?? null;
if(input === null) return;
const name = Cookies.get(cookieId) ?? input.placeholder;
if(input.placeholder !== name){
input.value = name;
}
}
/* 名前リセット */
function resetName() {
const formEl = document.forms.d_name;
let reset = resetItem(formEl.lastname,'lastname');
reset = resetItem(formEl.firstname,'firstname');
reset = resetItem(formEl.lastname_hira,'lastname_hira');
reset = resetItem(formEl.firstname_hira,'firstname_hira');
if(reset){
ajaxReload(); // 同一ページに変換箇所がある場合
document.getElementById("setName").classList.remove("change");
document.getElementById("setName").classList.add("reset");
}
}
/* 項目別 */
function resetItem(elName,coolieId){
Cookies.remove(coolieId);
if (!elName) return false;
else elName.value = '';
return true;
}
/* 名前変換 */
function changeName() {
let d_name_nvl = document.getElementById('d-name-nvl');
if (d_name_nvl === null) { return; }
let str = changeItem('lastname', 'data-last', d_name_nvl.innerHTML);
str = changeItem('firstname', 'data-first', str);
d_name_nvl.innerHTML = str;
changeHiragana('lastname_hira', 'data-last-hira', 'last-hira');
changeHiragana('firstname_hira', 'data-first-hira', 'first-hira');
}
/* 項目別 */
function changeItem(cookieId,attrName,str){
const data_d = document.getElementById('d-name-default').getAttribute(attrName);
const name = Cookies.get(cookieId);
if(name && data_d) return str.replace(new RegExp(data_d,'g'),name);
return str;
}
/* ひらがな対応 */
function changeHiragana(cookieId,attrName,className){
const data_d = document.getElementById('d-name-default').getAttribute(attrName);
const name = Cookies.get(cookieId);
if(!(name && data_d)) return;
let arr = document.getElementsByClassName(className);
for(i = 0;i<arr.length;i++){
let el = arr[i];
let defHira = data_d.split('');
let nameHira = name.split('');
let str = el.innerText.split('');
let txt ='';
let nameCount = 0;
for (k = 0; k < str.length; k++) {
if(!str[k].match(/^[ぁ-んー ]+$/) || defHira.length <= nameCount){
txt += str[k];
}else{
let replace;
if(nameCount < nameHira.length){
replace = nameHira[nameCount];
txt += str[k].replace(defHira[nameCount], replace);
nameCount++;
}else if(nameHira.length < defHira.length){
// 設定された名前のほうが短ければ以降はつけない
break;
}
}
}
// 不足分追加
if(defHira.length < nameHira.length){
let substr = name.substr(nameCount);
txt += substr;
}
el.innerText = txt;
}
}
/* 同じページに変換がある場合 */
function ajaxReload(){
let d_name_nvl = document.getElementById('d-name-nvl');
if (d_name_nvl) {
let url = location.href + "?date="+new Date().getTime();
let ajax = new XMLHttpRequest;
ajax.open("GET",url,true);
ajax.onload = function(){
var res = ajax.responseText;
var parse = new DOMParser().parseFromString(res,"text/html");
d_name_nvl.innerHTML = parse.getElementById('d-name-nvl').innerHTML;
};
ajax.send(null);
}
}
/* 読み込み直後 */
window.onload = function () {
/* 入力フォームの設定 */
keepName();
/* 名前変換 */
changeName();
};
同一ページに変換箇所がある場合の処理も追記しておきました。
もしない場合はその部分は削除して大丈夫です。
名前を変換する場合は文字を置き換え、リセットする場合は名前変換のあるタグ(d_name_nvl)の中だけをajaxをつかってリロードしています。
一部のみリロードについてはjavascriptでページを一部だけ更新する方法を参考にしました。
ajaxでもどってくる内容はテキストデータなので、htmlにパースして利用しています。テキストのパースは【JavaScript】DOMParserで文字列をHTMLElement・Nodeに変換するを参考にしました。
最後に
実装したのは割と前で記事にしようとしてなかなかできていなかったので、ようやく記事にできて嬉しいです。
「もっと汎用性がほしい!」「もっと使いやすい方がいい!」「全ページ同じにしたい!」と言った場合は、すでに配布されている素晴らしいスクリプトがありますのでそちらを使っていただくといいかもしれないです。
私の場合「名前にいちいちタグしたくない」「デフォルト名が小説によって違う」と自分で用意したほうが良さそうだったので用意してみました。
同じように自作しようとしている方の参考になれば幸いですっ
それと記事に関係ないですが、Twitter(現X)アカウントを作成しました!
Twitter(現X)アカウント てくてく@個人サイト向け情報共有サイト
よければフォローして下さいませ♪
参考
js-cookie
CDN js-cookie
javascriptでページを一部だけ更新する方法
JavaScript】DOMParserで文字列をHTMLElement・Nodeに変換する
DOMParser の parseFromString() メソッドの使い方