home

 Memo for HTML・サーバー・FTPなど
 11.
HPBでの
バージョンを
HTML4から
HTML5に
変更
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML>
<HEAD>
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<meta http-equiv="Content-Style-Type" content="text/css">
から
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
に変更。(表示できなかった絵文字がHTML5で表示できる)
 12.
「表」の追加
表の挿入希望場所(開始場所)に以下を記載する。
<div align="center">
<table border="0">
<tbody>
<tr>
<td align="center" width="950">

width="950"は600~950まで、適宜。
height="xxx"は通常は記載しない。
 13.
favicon
/HEADの直前に以下を記載する。
画像*.icoは別途freeアプリで作る。*.pngは自作画像で可。
if IEはIEがサポート終了となった為に今後は不要。

<link rel="icon" href="favicon.ico"> (注意・下段QRコードの項参照)
<link rel="apple-touch-icon" href="apple-touch-icon.png">
<!--[if IE]>
<link rel="shortcut icon" href="favicon.ico">
<![endif]-->
</HEAD>

ファビコン生成
https://ao-system.net/favicongenerator/
favicon.ico 16x16 32x32 48x48推奨 
apple-touch-icon.png 180x180推奨
 14.
HomePage
FC2
*
Ninja
*
*
StarS.Free
(Netowl)
*
XREA
*
Xfree
(xdomain)
*
TV松本CATV
*
*
http://web.fc2.com/
*
https://www.ninja.co.jp/hp/
(動画広告が表示。時々障害)
*
https://www.star.ne.jp/free/
(3ケ月毎に要更新。常時動画広告)
****
https://www.value-domain.com/
*
https://www.xfree.ne.jp/
(3ケ月毎に要更新。SIN。)
****
https://www.tvm.ne.jp/
 15.
FTP
https://github.com/ffftp/ffftp 
ffftp-v5.1-x64
フリーソフト(無料)
接続→設定→設定をファイルに保存・復元
 16.
HPB
JustSystem
https://www.justmyshop.com/
HPB20(ホームページビルダー20)
 
 17.
画像処理
SourceNext
https://www.sourcenext.com/ 
フォト消しゴム5
ピタリ四角6
 18.
文字コード
①Google連絡先のExport(csv)形式は、
UTF-8
②Excelの読み込み初期設定は、
Shift-JIS
③メモ帳の保存出力形式は、
ANSIまたはUTF-8が選択可能

①を②でそのまま読み込むと文字化け→
対処方法は③で(csv)を読み込んで
「ANSI」で保存すれば、
②で読み込んでも文字化けしない。
 19.
リンクチェッカー
 http://www.dead-link-checker.com/ja/
デッドリンクチェッカー
(リンクできなくなっているリンクをチェック)
チェックするURLがhttpsでチェックできなかったら
httpに変えてチェックできる場合がある
 20.
HTML
タグリファレンス
 http://www.htmq.com/html/#b

できるだけ「WEBセーフカラー216色」の中で選ぶこと。
HTML5では、<FRAME>は廃止
 21.
カラー画像を
モノクロに
 https://www.peko-step.com/tool/grayscale.html
 22.
JPG画像を
PIXEL値で
サイズ変更
 https://www.iloveimg.com/ja/resize-image/resize-jpg
 (例)
名刺(印刷のWAVE)
600dpiでjpeg作成したもの→
定型91x55mm→pixel変換2150x(縦横比維持値)(1299~1453~)
名刺の場合、
背景が白であれば「塗り足し」は考慮不要。

 23.
pixel-mm-dpi
計算
 https://shimeken.com/print/pixel-mm-dpi
 24.
QRコード 
画像入り
 https://qr.quel.jp/design.php 
QRコードの中央にアイコンや文字を入れることができる。
しかし理論的には同じサイズなら入れないより入れた方が誤り訂正率(読み取り率)が低下する。
印刷などで画像の縮小が予想される場合は入れない方が無難。

このサイトでQRコード(ファビコン入り)を作成する場合は
<link rel="icon" href="favicon.png"> ではなく
<link rel="icon" href="favicon.ico"> とすること。pngでは認識しない場合がある。
ファビコン作成サイトで*.icoを作成する必要あり。

URLを入力してもファビコン入りのQRが生成されない場合がある。
httpsの時はhttpに変えて入力すると生成される場合がある。
ただし読み込みURLはhttpとなるので要注意。
 25.
ビューポート
ビューポート(Viewport)を設定することで、
ウェブページがスマートフォンやタブレットなどの
様々なデバイスで正しく表示されるようになる。
HTMLのheadタグ内に以下のmetaタグを追加(必ずhead内に!)
タグに直接cssを書くインライン方式の方がcssの優先順位が高くなる

<meta name="viewport" content="width=device-width, initial-scale=1.0">

content属性の値を変更することで、ビューポートの設定を調整することができる
width=device-width:デバイスの幅に合わせてビューポートの幅を自動的に設定。
initial-scale=1.0:ページの初期表示時の拡大率を1倍に設定。
例えば、以下のように記述することで、ビューポートの幅を固定して、
初期表示時の拡大率を1.5倍に設定することができる。

<meta name="viewport" content="width=600, initial-scale=1.5">

その他の設定方法については、以下のように指定することができる。
height:ビューポートの高さを指定
minimum-scale:最小の拡大率を指定
maximum-scale:最大の拡大率を指定
user-scalable:ユーザーがページを拡大・縮小できるかどうかを指定
例えば、以下のように記述することで、
ビューポートの高さを固定し、
ユーザーが拡大・縮小できないように設定することができる。

<meta name="viewport" content="width=device-width, height=500, user-scalable=no">


スマホの表示幅をオーバーして表示される問題の解決
外部cssファイルの場合は<style>タグの内側のみ記述で、<style>タグは不要
ただし、ホームページビルダーでは<link rel>でリンクエラーが出る
<link rel="stylesheet" href="sample.css">


<head>
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=3.0,user-scalable=yes">
<style>
@charset "UTF-8";
/* アルファベット文字の折り返しを可能にする追記CSS、*印はユニバーサルセレクタです */
* {
word-break: break-all;
}

/* スマホからPCまで全てのデバイス幅に効くCSS */
table {
max-width:800px;
width:100% !important;
margin:0 auto;
}
img {
max-width:100% !important;
height:auto !important;
}
@media screen and (max-width: 767px) {
table {
max-width:650px;
}
}
</style>
</head>



こちらを使うと、画面幅によって使うCSSを分けることができ、上記の場合だと、
PCやタブレットなどはmax-width:800px;にして、
768px以下のスマホの時はmax-width:650px;になる。
!importantは優先順位を指定。

(以下の例)
カタログの「画面解像度または最高解像度」。注意・pixcelの数値とは違う
実際は1pxの中に2倍または3倍の解像度で描画しますということ。
( )内が実際のピクセル値

各機種の画面解像度
AQUOS wish = 720x1,520 (?x? px)
XPERIA ace Ⅲ = 720x1496 (?x? px)
moto e7 = 720x1600 (?x? px)
arrows M3 = 720x1280 (?x? px)
iPhone 11 = 828x1792 (?x? px)
iPhone 12pro = 1170x2532 (390x844px) 3倍スーパーサンプリング
iPhone SE = 750x1334 (375x667px) 2倍スーパーサンプリング

1ピクセルの中に2倍または3倍の解像度で描画する技術は
「スーパーサンプリング」と呼ばれます。
スーパーサンプリングは、元の画像やグラフィックをより
高い解像度で描画するために使用される方法です。
この技術では、ピクセルごとに複数のサブピクセルを考慮し、
それらの値を組み合わせることでより滑らかな画像を生成します。
スーパーサンプリングは、グラフィックスや画像処理の
分野で広く使用されています。



注意・・・widthはtdで指定する。以下、その例。
<div align="center">
<table border="2" bordercolor="#cc9900">
<tbody>
<tr>
<td align="center" width="800" height="1111"><br>(通常、heightは記載しない)


 26.
XMLサイトマップ

 1.HPBで
編集→ページのSEO設定
サイト→アクセス向上→XMLサイトマップの作成→sitemap.xmlが出来る→サイトにアップロード
サイト→アクセス向上→XMLサイトマップの登録→登録ページを開く→Googleにログイン→
Google Search Console
https://search.google.com/search-console/about?hl=ja
でプロパティを追加(新URLを追加)→
→google*****.htmlが出来る→ダウンロード→ffftpでサイトにアップロード
→サイトマップを送信(http://*****/sitemap.xmlと入力)

 2.Bing Webマスター
http://www.bing.com/toolbox/webmaster または
https://www.bing.com/webmasters/about
BingSiteAuth.xmlが出来る→ダウンロード→ffftpでサイトにアップロード

 27.
水平線に
色を付ける
 水平線を選択し、メニューバーから「編集」→「属性の変更」を選択
「属性」ダイアログボックスが表示されます。

[水平線]タブの[スタイル]ボタンをクリック
「スタイルの設定」ダイアログボックスが表示

[色と背景]タブをクリック
[背景色]で好みの色を選択
※後方互換モードでお使いの場合は、[前景色]で好みの色を選択

[OK]ボタンをクリックし、「スタイルの設定」ダイアログボックスを閉
「属性」ダイアログボックスに戻る

[OK]ボタンをクリックし、「属性」ダイアログボックスを閉じる
水平線に設定した色が反映

 28.
表の枠に
色を付ける
 表を選択

メニューバーから「編集」→「属性の変更」を選択
「属性」ダイアログまたは「レイアウト枠に含まれる表」ダイアログが表示される

[表] タブをクリックし、[背景] にある [色] で任意の色を選択
ここで設定した色が表の枠の色になる

[セル] タブをクリックし、[背景] にある [色] をページの背景色 と同じ色を設定して、
[OK] ボタンをクリック
ここでは、ページの背景と同じ白を設定

表の上で左クリックし、ドラッグを解除

 29.
css
サンプル
1.viewport(スマホ画面)
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=3.0,user-scalable=yes">
<style>
<!--

@charset "UTF-8";
/* 追記CSS・半角アルファベット文字の折り返し可能 */
*{
word-break: break-all;
}

/* スマホからPCまで全てのデバイス幅に効くCSS */
table{
max-width:800px;
width:100% !important;
margin:0 auto;
}
img{
max-width:100% !important;
height:auto !important;
}
@media screen and (max-width: 767px) {
table{
max-width:650px;
}
}
-->
</style>

2.ヘッダー・フッター
* {
margin:0; padding:0;
}
header {
width: 100%;
padding: 15px 0;
margin: 0 auto;
text-align: center;
background-color: #cab64a;
}
header .headline{
font-size: 32px;

}
.nav-list {
text-align: center;
padding: 10px 0;
margin: 0 auto;
}
.nav-list-item {
list-style: none;
display: inline-block;
margin: 0 20px;
}

footer {
width: 100%;
height: 120px;
text-align: center;
padding: 50px 0;
background-color: #cab64a;
}
.footer-text {
color: #fff;
}

3.スクロールバー
/* css */
.scroll-box {
overflow-y:scroll;/* autoでも良い */
height:200px; /* 任意の高さ */

/* 下記は無くても動くかもです */
.scroll-box a { /**/
display:block;
}

(HTML内の記述)
<td>
<ul class="scroll-box">
<li><a href="#">リンク</a></li>
<li><a href="#">リンク</a></li>
<li><a href="#">リンク</a></li>
</ul>
</td>

 30.
リンク
(ジャンプ)
 1.ラベルを付ける場合(例:アという文字に111というラベルを付けた場合)
<a href="#111a">ア</a>
URLは
https://www.sample-url/#111

 2.PDFの場合(例:5ページにジャンプ)はラベルを付ける作業が不要
<a href="sample.pdf#page=5">項目説明文</a>
URLは
https://www.sample-url/sample.pdf#page=5
 31.
画像の
「alt属性」と
「title属性」
 記述例
<img src="sample.jpg" alt="干支" title="うさぎ">
「alt属性」は検索エンジンやユーザーにわかりやすいように簡潔な内容で記述
「title属性」は画像の情報を補足するような文言
詳細は https://seolaboratory.jp/32415/
 32.
同じ内容のページが
複数のURLで存在する場合
 <link rel="canonical" href="https://www.優先される正規URL">
正規URLと副URL両方のページに記載したほうがよい
 33.
検索エンジンから除外
 →→このページを検索対象にしない①
<meta name="robots" content="noindex,follow">
逆に検索する場合は初期値として(影響なし)
<meta name="robots" content="all">は"index,follow" と同じ
詳細は
https://digital-marketing.jp/seo/index-control-meta-robots/

→→全てのリンク先を検索対象にしない②
<meta name="robots" content="index,nofollow">
①+②の場合
<meta name="robots" content="noindex,nofollow">

→→このページをキャッシュ対象にしない③
<meta name="robots" content="index,follow,noarchive">
①+②+③の場合
<meta name="robots" content="noindex,nofollow,noarchive">
 34.
ページのSEO設定
 ページタイトル 文字列
<title>ページタイトル</title>

キーワード 文字列
<meta name="Keywords" content="キーワード">

説明 文字列
<meta name="Description" content="説明">
 35.
サイトのリフレッシュ
 記載例(10秒後に指定サイトに移動)
<meta http-equiv="Refresh" content="10; url=http://rp1998jp.g1.xrea.com/">
 36.
Wikipedia
 Wikipediaの編集(最初から)
https://ja.wikipedia.org/wiki/利用者:YourUsername/○○_下書き

「サンドボックス(下書きページ)」で記事の草案を書く。
Wikipediaにログイン
画面右上の「個人用ツール」から「サンドボックス」を開く

下記のような構成で記事を書き始める:

== 概要 ==
○○ ○○(1900年〜1980年)は、○○県出身の地域活動家。

== 略歴 ==
1900年:○○県○○市に生まれる。

== 業績 ==
・○○福祉会の設立

== 出典 ==
* 『○○市の歴史』、○○市教育委員会、2000年


ウィキ・コモンズ
メインカテゴリー「〇〇」の下にサブカテゴリー「△△」を作る
直接URLから作成する方法
https://commons.wikimedia.org/wiki/Category:〇〇_-_△△
(※半角スペースは「_」に変換されます)
 37.
ビデオファイル挿入
<body>
 <video controls width="380" height="450" poster="aaa(静止画).jpg">
<source src="aaa(動画).mp4" type="video/mp4">
お使いのブラウザは動画タグをサポートしていません。
</video></body>
<video>タグにposter属性を追加する理由:iPhoneでプレビュー画像を正常に表示させる
 38.
「戻る」ボタン
 ********** CSS ***************
<style>
<!--

/* CSSだけで常時表示する「上に戻る」ボタン */
#backToTopCSS {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 100;
display: block;
background-color: #333;
color: #fff;
padding: 10px 15px;
border-radius: 50%;
font-size: 20px;
text-decoration: none;
box-shadow: 0 2px 5px rgba(0,0,0,0.3);
}

#backToTopCSS:hover {
background-color: #555;
}

-->
</style>

********** HTML ***************

<body id="top">
</body>のすぐ上に
<a href="#top" id="backToTopCSS" title="上に戻る">↑</a>
 39.ページが開かれたときに
自動でリフレッシュする
(古いキャッシュを無視)
 <head>
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
</head>
 40.ページ内検索
 2025/08/19
ホームページ
サイト内(ページ内)検索HTML作成手順

①search-demo.htmlをルートに設置(テスト用なので不要・削除してもOK)
内容は yoko8zに有る。
このページの下段にも記述あり

②目的ページの</HEAD>の直前に

<style>
/* 固定表示検索ボックス */
#searchBox {
position: fixed; /* 画面上段に固定 */
top: 0;
left: 0;
width: 100%;
background-color: #fff;
border-bottom: 1px solid #ccc;
padding: 8px;
box-sizing: border-box;
z-index: 9999;
}

/* 入力欄 */
#searchInput {
padding: 8px 12px;
font-size: 16px;
width: 65%; /* 入力欄の幅調整 */
border: 1px solid #666;
border-radius: 6px;
}

/* ボタン */
#searchBox button {
padding: 8px 12px;
font-size: 14px;
margin-left: 4px;
cursor: pointer;
}

/* 本文との重なり防止 */
#content {
margin-top: 70px; /* 検索ボックスの高さに合わせる */
}

/* スマホ向けレスポンシブ調整 */
@media (max-width: 480px) {
#searchInput {
width: 55%;
font-size: 14px;
}
#searchBox button {
font-size: 12px;
padding: 6px 10px;
}
#content {
margin-top: 90px;
}
}
</style>

<style>
/* 🔍 検索ボックス用スタイル */
#searchBox {
display: flex; /* 横並びにする */
flex-wrap: nowrap; /* 折り返さない */
align-items: center; /* ボタンを縦位置中央に */
gap: 5px; /* ボタンとの隙間 */
justify-content: center; /* 中央寄せ */
margin: 10px 0;
}

#searchBox input[type="text"] {
width: 50%; /* 入力欄を半分くらいに */
min-width: 120px; /* スマホでも潰れすぎないように下限幅 */
padding: 5px;
font-size: 14px;
}

#searchBox button {
padding: 5px 10px;
font-size: 14px;
white-space: nowrap; /* ボタン内の文字を折り返さない */
}
</style>


を記述
内容はhttp://hamada777.g1.xrea.com/index2.htmlに有る
以下すべて。


③<body>の下(<table>の上)に下記を記述
<!-- 🔍 固定表示検索ボックス -->
<div id="searchBox">
<input type="text" id="searchInput" placeholder="検索ワードを入力">
<button onclick="searchText()">検索</button>
<button onclick="nextResult()">次へ</button>
<button onclick="prevResult()">前へ</button>
</div>

<!-- ★検索対象を div#content で囲む -->
<div id="content">

このあと検索対象の最後を
</div>
<!-- ★ここまで -->
で閉じる


④</body>の直前に下記を記述
(ホームページビルダーのプレビューでスクリプトエラーになっても問題ない)

<script>
let results = []; // 検索ヒットした要素を保存
let currentIndex = 0; // 現在の位置

function searchText() {
const input = document.getElementById("searchInput").value.trim();
const content = document.getElementById("content");

// 前回のハイライトを解除
content.querySelectorAll("mark").forEach(mark => {
const parent = mark.parentNode;
parent.replaceChild(document.createTextNode(mark.textContent), mark);
parent.normalize();
});

results = [];
currentIndex = 0;

if (input === "") return;

// 正規表現で検索語をハイライト
const regex = new RegExp(input, "gi");
content.innerHTML = content.innerHTML.replace(regex, match => `<mark>${match}</mark>`);

results = Array.from(content.querySelectorAll("mark"));

if (results.length > 0) {
results[0].scrollIntoView({ behavior: "smooth", block: "center" });
results[0].style.backgroundColor = "yellow";
}
}

function nextResult() {
if (results.length === 0) return;
results[currentIndex].style.backgroundColor = "orange";
currentIndex = (currentIndex + 1) % results.length;
results[currentIndex].scrollIntoView({ behavior: "smooth", block: "center" });
results[currentIndex].style.backgroundColor = "yellow";
}

function prevResult() {
if (results.length === 0) return;
results[currentIndex].style.backgroundColor = "orange";
currentIndex = (currentIndex - 1 + results.length) % results.length;
results[currentIndex].scrollIntoView({ behavior: "smooth", block: "center" });
results[currentIndex].style.backgroundColor = "yellow";
}
</script>

<script>
// 検索ボックスの高さを自動計算させる
window.addEventListener("load", function() {
var searchBox = document.getElementById("searchBox");
var content = document.getElementById("content");
if (searchBox && content) {
var h = searchBox.offsetHeight;
content.style.marginTop = h + "px";
}
});
</script>


************************************************************************
①search-demo.html(参考保存のみ)

<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>ページ内テキスト検索デモ(HTML+JSのみ)</title>
<style>
:root { --accent: #0d6efd; }
body { font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Hiragino Kaku Gothic ProN", Meiryo, sans-serif; line-height: 1.7; margin: 0; }
header { position: sticky; top: 0; background: #fff; border-bottom: 1px solid #e5e7eb; z-index: 10; }
.container { max-width: 960px; margin: 0 auto; padding: 16px; }
.toolbar { display: grid; grid-template-columns: 1fr auto auto auto auto; gap: 8px; align-items: center; }
.toolbar input[type="text"] { padding: 10px 12px; border: 1px solid #cbd5e1; border-radius: 10px; }
.toolbar button, .toolbar label { padding: 10px 12px; border-radius: 10px; border: 1px solid #cbd5e1; background: #f8fafc; cursor: pointer; }
.toolbar button.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.toolbar .count { font-variant-numeric: tabular-nums; color: #334155; padding: 0 8px; }
mark.find-hit { background: #fff59d; padding: 0 2px; border-radius: 3px; }
mark.find-current { background: #ffd54f; outline: 2px solid #ffb300; }
.content { padding: 24px 16px 64px; }
.hint { color: #64748b; font-size: 14px; }
.kbd { padding: 1px 6px; border: 1px solid #cbd5e1; border-radius: 6px; background: #fff; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
</style>
</head>
<body>
<header>
<div class="container">
<div class="toolbar" role="search">
<input id="q" type="text" placeholder="このページ内を検索… (例: フェーズドアレイ)" autocomplete="off" />
<button id="btnFind" class="primary" title="検索 (Enter)">検索</button>
<button id="btnPrev" title="前の一致 (Shift+Enter)">前へ</button>
<button id="btnNext" title="次の一致 (Enter)">次へ</button>
<button id="btnClear" title="結果を解除 (Esc)">解除</button>
<div class="count" id="counter" aria-live="polite"></div>
</div>
<div class="hint">Enter=検索/次へ、Shift+Enter=前へ、Esc=解除。 大文字小文字 <label><input type="checkbox" id="optCase" /> 区別する</label>・<label><input type="checkbox" id="optWord" /> 単語全体一致</label></div>
</div>
</header>

<main class="container content">
<!-- ★★ 検索対象はこの#content内だけです。自分のページでは本文をこのdivに入れてください。 -->
<article id="content">
<h1>ページ内テキスト検索デモ</h1>
<p>このサンプルは、サーバー側のプログラムなし(FTPアップロードのみ)で<strong>ページ内のテキストを検索</strong>し、
一致箇所へ自動スクロール&ハイライトします。日本語もOKです。</p>
<p>主なポイント:
<ul>
<li>検索語の全一致を順送り/逆送り(次へ/前へ)</li>
<li>大文字小文字の区別・単語全体一致の切替</li>
<li>Escで解除して元の本文に戻す(元HTMLを一時保存)</li>
<li>検索バーはページ上部に固定(<code>position: sticky</code>)</li>
</ul>
</p>
<h2>ダミー本文</h2>
<p>たとえば「アンテナ」「フェーズドアレイ」「Excel」などで試してみてください。検索結果は
<mark>強調表示</mark>され、現在位置はさらに濃い色で表示されます。</p>
<p>段落が長くてもOKです。複数の一致がある場合、Enterで次の一致へ自動スクロールします。</p>
<p>英語: phased array antenna / array factor / beam steering / FFT.
日本語: 位相制御 / 素子間隔 / 指向性。
</p>
</article>
</main>

<script>
(function(){
const $ = (sel, ctx=document) => ctx.querySelector(sel);
const $$ = (sel, ctx=document) => Array.from(ctx.querySelectorAll(sel));

const box = $('#q');
const btnFind = $('#btnFind');
const btnNext = $('#btnNext');
const btnPrev = $('#btnPrev');
const btnClear = $('#btnClear');
const counter = $('#counter');
const optCase = $('#optCase');
const optWord = $('#optWord');
const content = $('#content');

let hits = []; // Array<HTMLElement mark.find-hit>
let pos = -1; // 現在の一致位置インデックス
let originalHTML = null; // 元のHTML(解除用)

function escapeRegExp(s){ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }

function buildRegex(query){
if(!query) return null;
const flags = optCase.checked ? 'g' : 'gi';
const src = optWord.checked ? `\\b${escapeRegExp(query)}\\b` : escapeRegExp(query);
try { return new RegExp(src, flags); } catch(e){ return null; }
}

function clearHighlights(){
if(originalHTML != null){
content.innerHTML = originalHTML;
} else {
// 念のため:markを外す(初回前はmark存在しない)
$$('.find-hit', content).forEach(m => {
const t = document.createTextNode(m.textContent);
m.replaceWith(t);
});
}
hits = [];
pos = -1;
originalHTML = null;
updateCounter();
}

function updateCounter(){
if(hits.length === 0){ counter.textContent = ''; return; }
counter.textContent = `${pos+1} / ${hits.length}`;
}

function scrollToCurrent(){
if(pos < 0 || pos >= hits.length) return;
hits.forEach(h => h.classList.remove('find-current'));
const el = hits[pos];
el.classList.add('find-current');
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
}

function doFind(direction = 0){
const q = box.value.trim();
if(!q){ clearHighlights(); return; }

// 既存の結果が検索語・オプションと一致しているか簡易チェック
const sameQuery = content.dataset._lastQuery === q &&
content.dataset._lastCase === String(optCase.checked) &&
content.dataset._lastWord === String(optWord.checked);

if(!sameQuery){
// 初回または条件変更:ハイライトを作り直す
clearHighlights();
originalHTML = content.innerHTML; // 元を保存(解除で復元)

const regex = buildRegex(q);
if(!regex){ return; }

// テキストノードを走査してmark化
const walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT, {
acceptNode(node){
// 目に見えるテキストのみ(空白や改行のみは除外)
return /\S/.test(node.nodeValue) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
}
});

const textNodes = [];
while(walker.nextNode()) textNodes.push(walker.currentNode);

textNodes.forEach(node => {
const parent = node.parentNode;
let html = node.nodeValue;
if(!regex.test(html)) return; // 該当なし
// global RegExp は状態を持つので都度リセット
regex.lastIndex = 0;
const parts = [];
let lastIdx = 0;
let m;
while((m = regex.exec(html))){
const before = html.slice(lastIdx, m.index);
const hit = m[0];
parts.push(document.createTextNode(before));
const mark = document.createElement('mark');
mark.className = 'find-hit';
mark.textContent = hit;
parts.push(mark);
hits.push(mark);
lastIdx = m.index + hit.length;
}
parts.push(document.createTextNode(html.slice(lastIdx)));
const frag = document.createDocumentFragment();
parts.forEach(p => frag.appendChild(p));
parent.replaceChild(frag, node);
});

content.dataset._lastQuery = q;
content.dataset._lastCase = String(optCase.checked);
content.dataset._lastWord = String(optWord.checked);

if(hits.length === 0){
updateCounter();
return;
}
pos = 0; // 最初の一致へ
updateCounter();
scrollToCurrent();
return;
}

// 既存の結果で移動
if(hits.length === 0){ updateCounter(); return; }
if(direction === 0){ // Enter=次へ
pos = (pos + 1) % hits.length;
} else if(direction < 0){ // 前へ
pos = (pos - 1 + hits.length) % hits.length;
} else { // 明示的に次へ
pos = (pos + 1) % hits.length;
}
updateCounter();
scrollToCurrent();
}

// ---- イベント
btnFind.addEventListener('click', () => doFind(+1));
btnNext.addEventListener('click', () => doFind(+1));
btnPrev.addEventListener('click', () => doFind(-1));
btnClear.addEventListener('click', clearHighlights);

box.addEventListener('keydown', (e) => {
if(e.key === 'Enter'){
e.preventDefault();
doFind(e.shiftKey ? -1 : +1);
} else if(e.key === 'Escape'){
clearHighlights();
}
});

// ページ全体のショートカット(/ でフォーカス)
document.addEventListener('keydown', (e) => {
if(e.key === '/' && !e.ctrlKey && !e.metaKey && !e.altKey){
e.preventDefault();
box.focus();
box.select();
}
});
})();
</script>
</body>
</html>

**********************************************************************


home




<EOF>

うさぎたちの庭