postnote
 kensaku-5 

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>【ドロップダウン検索】contenteditable と scrollIntoView</title>
<style>
body { font-family: sans-serif; padding: 1em; }
.container { display: flex; flex-direction: column; gap: 10px; }
.controls { display: flex; align-items: center; gap: 10px; }

.editable-area {
width: 500px;
height: 200px;
border: 1px solid #767676;
padding: 8px;
overflow: auto;
font-size: 16px;
line-height: 1.5;
background-color: white;
white-space: pre-wrap;
}
.highlight {
background-color: #ff0;
border-radius: 3px;
}
#searchCounter {
font-size: 0.9em;
color: #333;
min-width: 50px;
}
/* ▼▼▼ ドロップダウンのスタイル ▼▼▼ */
select {
padding: 5px;
font-size: 1em;
}
</style>
</head>
<body>

<h1>【ドロップダウン検索】編集エリア内検索 & ジャンプ機能</h1>

<div class="container">
<div class="controls">
<select id="searchSelect" onchange="findNext(true)">
<option value="" disabled selected>検索語を選択...</option>
<option value="JavaScript">JavaScript</option>
<option value="キーワード">キーワード</option>
<option value="機能">機能</option>
<option value="テキストエリア">テキストエリア</option>
</select>
<input type="button" value="次を検索" onclick="findNext(false)">
<span id="searchCounter"></span>
</div>
<div id="editor" class="editable-area" contenteditable="true"></div>
</div>

<script>
const editor = document.getElementById('editor');
// ▼▼▼ 取得対象を searchInput から searchSelect に変更 ▼▼▼
const searchSelect = document.getElementById('searchSelect');
const counterElement = document.getElementById('searchCounter');

const sampleText = "このテキストエリアは、長文の入力が可能です。\nJavaScriptを使うことで、様々な便利機能を追加できます。例えば、このように検索{タンを押すと、指定したキーワードの場所までカーソルがジャンプします。\nもう一度「次を検索」{タンを押すと、次の「キーワード」に移動します。JavaScriptの可能性は無限大です。\nぜひ、このサンプルを改造して、あなただけの機能を作ってみてください。\n最後のキーワードはこちらです。\n\n";
editor.innerText = sampleText.repeat(5);

let currentMatches = [];
let currentIndex = -1;

// ▼▼▼ isNewSearch 引数を追加して、新しい検索かどうかを判定 ▼▼▼
function findNext(isNewSearch = false) {
// ▼▼▼ キーワードの取得元を searchSelect.value に変更 ▼▼▼
const keyword = searchSelect.value;
if (!keyword) {
// ドロップダウンが選択されていない場合は何もしない
return;
}

// 新しい検索の場合 (onchangeイベント) or 検索キーワードが変わった場合に再検索
if (isNewSearch || editor.dataset.lastKeyword !== keyword) {
removeHighlights();
editor.dataset.lastKeyword = keyword;

const regex = new RegExp(keyword, 'gi');
editor.innerHTML = editor.innerText.replace(regex, `<span class="highlight">${keyword}</span>`);

currentMatches = Array.from(editor.querySelectorAll('.highlight'));
currentIndex = -1;
}

if (currentMatches.length === 0) {
if (isNewSearch) { // 新規検索時のみアラート
alert('キーワードが見つかりませんでした。');
}
counterElement.textContent = '(0 / 0)';
return;
}

currentIndex = (currentIndex + 1) % currentMatches.length;
counterElement.textContent = `(${currentIndex + 1} / ${currentMatches.length})`;
const currentElement = currentMatches[currentIndex];

currentElement.scrollIntoView({
behavior: 'smooth',
block: 'center'
});

currentMatches.forEach(el => el.style.backgroundColor = '#ff0');
currentElement.style.backgroundColor = '#ffa500';
}

function removeHighlights() {
if (editor.dataset.lastKeyword) {
editor.innerHTML = editor.innerText;
counterElement.textContent = '';
}
}
</script>

</body>
</html>


使用変数

backgroundColor
charset
class
contenteditable
counterElement
currentElement
currentIndex
currentMatches
editor
el
findNext -------( Function )
id
innerHTML
innerText
isNewSearch
keyword
lang
lastKeyword
length
onchange
onclick
regex
removeHighlights -------( Function )
sampleText
searchSelect
textContent
type
value