見出し画像

手軽なオンラインのテキストエディタがないので、作ってみた。

※最新版は ↑ です。

オンラインのテキストエディタを検索しても碌なサイトが出てこないうえに広告だらけ。
ということでコードを書きました。
グーグルサイト(無料)にでもコードを書きこんで試してみてください。
※グーグルサイトへの書き込みが分からない場合には、コメントにお願いします。
※自己責任で。

※2024/10/14 15:41 検索時の表示位置が中央になるように修正しました。

実行画面
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>エディタ</title>
  <style>
    body {
      font-family: Arial, sans-serif;
    }

    .editor-wrapper {
      position: relative;
      width: 800px;
      height: 500px;
      margin: 0 auto;
      border: 1px solid #ccc;
      box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    }

    .line-numbers {
      position: absolute;
      top: 10px;
      left: 0;
      bottom: 0;
      width: 30px;
      text-align: right;
      padding-right: 5px;
      line-height: 1.5;
      font-size: 14px;
      background-color: #f0f0f0;
      color: blue; /* 行番号の色を青に設定 */
      user-select: none;
      pointer-events: none;
      overflow: hidden;
    }

    textarea {
      width: 100%;
      height: 100%;
      padding-left: 40px;
      padding-top: 10px;
      line-height: 1.5;
      font-size: 14px;
      border: none;
      resize: none;
      outline: none;
      white-space: pre;
      font-family: inherit;
      box-sizing: border-box;
    }

    .controls {
      display: flex;
      gap: 10px;
      margin: 10px auto;
      justify-content: center;
    }

    select, input {
      padding: 5px;
    }

    .search-replace {
      display: flex;
      gap: 10px;
      margin-left: 10px; /* 左の余白を追加 */
    }
  </style>
</head>
<body>

  <div class="controls">
    <select id="fontSizeSelect" onchange="changeFontSize()">
      <option value="12">12pt</option>
      <option value="14" selected>14pt</option>
      <option value="16">16pt</option>
      <option value="18">18pt</option>
      <option value="20">20pt</option>
    </select>

    <select id="textColorSelect" onchange="changeTextColor()">
      <option value="black" selected>文字色: 黒</option>
      <option value="red">赤</option>
      <option value="blue">青</option>
      <option value="green">緑</option>
      <option value="orange">オレンジ</option>
    </select>

    <select id="backgroundColorSelect" onchange="changeBackgroundColor()">
      <option value="white" selected>背景色: 白</option>
      <option value="lightgray">薄灰</option>
      <option value="lightyellow">薄黄</option>
      <option value="lightblue">薄青</option>
      <option value="lightgreen">薄緑</option>
    </select>

    <div class="search-replace">
      <input type="text" id="searchBox" placeholder="検索">
      <button onclick="searchText()">検索</button>
      <button onclick="searchNext()">次を検索</button>
      <input type="text" id="replaceBox" placeholder="置換">
      <button onclick="replaceText()">置換</button>
    </div>
  </div>

  <div class="editor-wrapper">
    <div class="line-numbers" id="line-numbers"></div>
    <textarea id="editor" oninput="updateLineNumbers()" onscroll="syncScroll()"></textarea>
  </div>

  <script>
    let lastMatchIndex = -1; // 最後に選択されたマッチのインデックス

    function updateLineNumbers() {
      const editor = document.getElementById("editor");
      const lineNumbers = document.getElementById("line-numbers");
      const lines = editor.value.split("\n").length;
      let lineNumberHtml = '';

      for (let i = 1; i <= lines; i++) {
        lineNumberHtml += i + '<br>';
      }

      lineNumbers.innerHTML = lineNumberHtml;
    }

    function syncScroll() {
      const editor = document.getElementById("editor");
      const lineNumbers = document.getElementById("line-numbers");
      lineNumbers.scrollTop = editor.scrollTop;
    }

    function changeFontSize() {
      const editor = document.getElementById("editor");
      const lineNumbers = document.getElementById("line-numbers");
      const fontSizeSelect = document.getElementById("fontSizeSelect");
      const fontSize = fontSizeSelect.value;

      editor.style.fontSize = fontSize + 'pt';
      lineNumbers.style.fontSize = fontSize + 'pt';
    }

    function changeTextColor() {
      const editor = document.getElementById("editor");
      const textColorSelect = document.getElementById("textColorSelect");
      const textColor = textColorSelect.value;

      editor.style.color = textColor; // テキストの色を変更
    }

    function changeBackgroundColor() {
      const editor = document.getElementById("editor");
      const backgroundColorSelect = document.getElementById("backgroundColorSelect");
      const backgroundColor = backgroundColorSelect.value;

      editor.style.backgroundColor = backgroundColor; // 背景色を変更
    }


function searchText() {
  const editor = document.getElementById("editor");
  const searchTerm = document.getElementById("searchBox").value;

  const editorContent = editor.value;
  const regex = new RegExp(searchTerm, 'gi');  // 'i' フラグで大文字小文字を無視
  const matches = editorContent.match(regex);

  if (matches) {
    lastMatchIndex = editorContent.toLowerCase().indexOf(matches[0].toLowerCase()); // 最初のマッチのインデックスを保存
    editor.setSelectionRange(lastMatchIndex, lastMatchIndex + matches[0].length);
    editor.focus();

    // 検索結果を中央に表示するためのスクロール位置を計算
    const editorHeight = editor.clientHeight;
    const lineHeight = parseInt(window.getComputedStyle(editor).lineHeight, 10);
    const linesFromTop = editor.value.substring(0, lastMatchIndex).split("\n").length - 1; // 上から何行目か
    const scrollPosition = (linesFromTop * lineHeight) - (editorHeight / 2) + (lineHeight / 2);

    // スクロールを中央に移動
    editor.scrollTop = Math.max(scrollPosition, 0);
  }
}

function searchNext() {
  const editor = document.getElementById("editor");
  const searchTerm = document.getElementById("searchBox").value;
  const editorContent = editor.value;

  const selectedText = editor.value.substring(editor.selectionStart, editor.selectionEnd);
  let matchIndex;

  if (selectedText) {
    const regex = new RegExp(selectedText, 'gi');  // 'i' フラグで大文字小文字を無視

    while ((matchIndex = regex.exec(editorContent)) !== null) {
      if (matchIndex.index > lastMatchIndex) {
        lastMatchIndex = matchIndex.index;
        editor.setSelectionRange(lastMatchIndex, lastMatchIndex + matchIndex[0].length);
        editor.focus();

        // 検索結果を中央に表示するためのスクロール位置を計算
        const editorHeight = editor.clientHeight;
        const lineHeight = parseInt(window.getComputedStyle(editor).lineHeight, 10);
        const linesFromTop = editor.value.substring(0, lastMatchIndex).split("\n").length - 1;
        const scrollPosition = (linesFromTop * lineHeight) - (editorHeight / 2) + (lineHeight / 2);

        // スクロールを中央に移動
        editor.scrollTop = Math.max(scrollPosition, 0);
        return;
      }
    }
  } else {
    const regex = new RegExp(searchTerm, 'gi');  // 'i' フラグで大文字小文字を無視
    let match;

    while ((match = regex.exec(editorContent)) !== null) {
      if (match.index > lastMatchIndex) {
        lastMatchIndex = match.index;
        editor.setSelectionRange(lastMatchIndex, lastMatchIndex + match[0].length);
        editor.focus();

        // 検索結果を中央に表示するためのスクロール位置を計算
        const editorHeight = editor.clientHeight;
        const lineHeight = parseInt(window.getComputedStyle(editor).lineHeight, 10);
        const linesFromTop = editor.value.substring(0, lastMatchIndex).split("\n").length - 1;
        const scrollPosition = (linesFromTop * lineHeight) - (editorHeight / 2) + (lineHeight / 2);

        // スクロールを中央に移動
        editor.scrollTop = Math.max(scrollPosition, 0);
        return;
      }
    }
  }
}

 
    function replaceText() {
      const editor = document.getElementById("editor");
      const searchTerm = document.getElementById("searchBox").value;
      const replaceTerm = document.getElementById("replaceBox").value;

      editor.value = editor.value.replace(new RegExp(searchTerm, 'g'), replaceTerm);
      updateLineNumbers();
    }

    // 初期化
    updateLineNumbers();
  </script>

</body>
</html>

この記事が気に入ったらサポートをしてみませんか?