🎃

jsで要素のtemplate化に成功 全文

2025/03/06に公開

html内でhtml要素のテンプレを記述し、引数込みでreplaceするようなものを作成しました

やりたいこととしては、


    <div class="product-template">
        <div class = "contents_wrap_test">
            <div class = "contents_wrap_test_2">
                <div class="test_wrap">
                    <div class="price"></div>
                    <div class="description">サンプルサンプル</div>
                    <div data-insert="content"></div>
                </div>
            </div>
        </div>
      
      <p>testtest</p>
    </div>

こんなような、構造を記述するために階層がとんでもないようなことになっているものに対して、

  <div class="product-template">
    <div class="price">3,000円</div>
    <div class="info">特別割引中</div>
    <p>商品説明がここに入ります</p>
  </div>

こうゆう感じで引数を指定した関数のようなものを書いておき、これをテンプレで書き換えたいという欲求です。

    <div class="info">特別割引中</div>
    <p>商品説明がここに入ります</p>

は元の要素にはないです。

これらは、
例外処理用のクラス

<div data-insert="content"></div>

に挿入されます。

replaceクラスを作成し、その中にテンプレートを記述します。

 <body>
    <!-- テンプレート定義 -->
<div class="replace">
    <div class="bbb">
        <div class = "contents_wrap_test">
            <div class = "contents_wrap_test_2">
                <div class="test_wrap">
                    <div class="price"></div>
                    <div class="description">サンプルサンプル</div>
                    <div data-insert="content"></div>
                </div>
            </div>
        </div>
      
      <p>testtest</p>
    </div>
    
    <div class="aaa">
        <div class = "contents_wrap_test">
            <div class = "contents_wrap_test_2">
                <div class="test_wrap">
                    <div class="price"></div>
                    <div class="description">サンプルサンプル</div>
                    <div data-insert="content"></div>
                </div>
            </div>
        </div>
      
      <p>testtest</p>
    </div>
  </div>
  
  <!-- 置換対象 -->
  <div class="p_temp bbb">
    <div class="price">3,000円</div>
    <div class="info">特別割引中</div>
    <p>商品説明がここに入ります</p>
  </div>

    <!-- 置換対象 -->
    <div class="p_temp bbb">
        <div class="price">3,000円</div>
        <div class="info">特別割引中</div>
        <p>商品説明がここに入ります</p>
      </div>
  
      <div class = "p_temp aaa"></div>
    <script src = "replace2.js">

    </script>
</body>

replaceクラスは、最後にdisplay:noneされます。

templateを参照したい要素には、p_tempのクラスを追加してください。

例えば、複数個所に同じ数字を使うこと、ありますよね。
これを使えば、一つの引数宣言で複数の場所に対してreplaceが可能です。

至らない部分もあると思います。なにかあればコメントで教えてください。

以下コード

document.addEventListener('DOMContentLoaded', function() {
    // 複数テンプレート定義を取得(.replace直下のクラス属性を持つ要素)
    const templateDefinitions = document.querySelectorAll('.replace > [class]');
  
    // 置換対象となる要素を取得(.replaceの外にある.product-templateの要素)
    const targetElements = Array.from(document.querySelectorAll('.p_temp')).filter(el => 
      !el.closest('.replace')
    );
  
    // 各置換対象に対して処理を実行
    targetElements.forEach(targetElement => {
      // マッチするテンプレートを検索
      let matchedTemplate = Array.from(templateDefinitions).find(templateDef => 
        Array.from(templateDef.classList).some(cls => 
          targetElement.classList.contains(cls)
        )
      );
  
      if (!matchedTemplate) {
        console.warn('No matching template found:', targetElement);
        return;
      }
  
      // テンプレートのクローンを作成
      const templateClone = matchedTemplate.cloneNode(true);
  
      // マッピング対象の属性を処理
      const mappingKeys = Array.from(templateClone.querySelectorAll('*'))
        .filter(el => el.className)
        .flatMap(el => el.className.split(/\s+/)); // 複数クラス対応
  
      // 修正後のマッピング処理部分
mappingKeys.forEach(key => {
    const sourceEl = targetElement.querySelector(`.${key}`);
    if (!sourceEl) return;
  
    // テンプレート内の該当クラス要素をすべて取得
    const templateElements = templateClone.querySelectorAll(`.${key}`);
    
    // すべての一致する要素に内容をコピー
    templateElements.forEach(templateEl => {
      templateEl.innerHTML = sourceEl.innerHTML;
    });
  });
  
      // コンテンツ挿入処理
      const contentContainer = templateClone.querySelector('[data-insert="content"]');
      if (contentContainer) {
        const unmappedNodes = Array.from(targetElement.childNodes).filter(node => {
          if (node.nodeType === Node.TEXT_NODE) {
            return node.textContent.trim() !== '';
          }
          if (node.nodeType === Node.ELEMENT_NODE) {
            return !Array.from(node.classList).some(cls => mappingKeys.includes(cls));
          }
          return false;
        });
  
        unmappedNodes.forEach(node => {
          contentContainer.appendChild(node.cloneNode(true));
        });
      }
  
      // 要素置換処理
      targetElement.parentNode.replaceChild(templateClone, targetElement);
    });
  
    // テンプレートコンテナを非表示
    document.querySelectorAll('.replace').forEach(el => {
      el.style.display = 'none';
    });
  });

Discussion