タブの中にフィールドが入らない

タブ作成のプラグインを作成中です。

タブの名称、色、文字色、形状、幅、折り返しを自由にできてサイズ調整もできるように作成しています。
タブは何とか表示させるまでできたんですが、タブの中にフィールドが入っていかず、ただタブが作成されただけになっています。何が原因なのでしょうか?コンソールでもエラーは出ておらず、一見実行できたように見えるものの表示がうまくいきません。どなたか助けて下さい。

実行したコードをコピー&ペーストしましょう

(function () {
  "use strict";

  const PLUGIN_ID = kintone.$PLUGIN_ID;
  const EVENTS = ['app.record.detail.show', 'app.record.edit.show'];

  const initializeTabs = function (event) {
    const config = kintone.plugin.app.getConfig(PLUGIN_ID);
    console.log("✅ プラグイン設定内容:", config);

    if (!config || !config.tabs) {
      console.warn("❌ config.tabs が存在しません");
      return;
    }

    const tabsData = JSON.parse(config.tabs);
    console.log("✅ tabsData:", tabsData);

    const wrapArray = config.wrapJson ? JSON.parse(config.wrapJson) : [tabsData.length];
    const globalScale = config.globalScale ? parseInt(config.globalScale, 10) : 100;

    // 修正:スペースIDを画面種別に依存させない
    const getSpaceId = id => `user-js-${id}`;

    const spaceElements = tabsData.map(tab => document.getElementById(getSpaceId(tab.space)));
    tabsData.forEach((tab, i) => {
      console.log(`✅ スペース要素 ${getSpaceId(tab.space)}:`, spaceElements[i]);
    });
    if (spaceElements.some(el => !el)) {
      console.warn("❌ 一部のスペース要素が見つかりません");
      return;
    }

    const tabHeaderContainer = document.createElement("div");
    tabHeaderContainer.className = "custom-tab-header";
    tabHeaderContainer.style.transform = `scale(${globalScale / 100})`;
    tabHeaderContainer.style.transformOrigin = "top left";
    spaceElements[0].parentNode.insertBefore(tabHeaderContainer, spaceElements[0]);

    const tabContents = {};
    spaceElements.forEach((spaceElem, idx) => {
      const tab = tabsData[idx];
      const container = document.createElement("div");
      container.id = `${getSpaceId(tab.space)}-container`;
      container.className = "custom-tab-content";
      container.style.display = idx === 0 ? "block" : "none";
      spaceElem.parentNode.insertBefore(container, spaceElem.nextSibling);
      tabContents[tab.space] = container;
    });

    let tabIndex = 0;
    wrapArray.forEach(count => {
      const row = document.createElement("div");
      row.className = "custom-tab-row";
      tabHeaderContainer.appendChild(row);

      for (let i = 0; i < count && tabIndex < tabsData.length; i++, tabIndex++) {
        const tab = tabsData[tabIndex];
        const btn = document.createElement("button");
        btn.innerText = tab.name;
        btn.style.backgroundColor = tab.color;
        btn.style.color = tab.textColor;
        btn.style.width = tab.width + "px";
        btn.dataset.space = tab.space;

        switch (tab.shape) {
          case "round": btn.style.borderRadius = "12px"; break;
          case "radius": btn.style.borderRadius = "6px"; break;
          case "square": btn.style.borderRadius = "0"; break;
          case "ellipse": btn.style.borderRadius = "50% / 50%"; break;
          default: btn.style.borderRadius = "6px";
        }

        btn.addEventListener("click", () => {
          document.querySelectorAll(".custom-tab-header button").forEach(b => b.classList.remove("active"));
          btn.classList.add("active");

          document.querySelectorAll(".custom-tab-content").forEach(c => c.style.display = "none");
          const content = document.getElementById(`${getSpaceId(tab.space)}-container`);
          if (content) content.style.display = "block";
        });

        row.appendChild(btn);
      }
    });

    // ==========================
    // 修正:フィールドを確実にタブに振り分け
    // ==========================
    // タブごとのフィールドIDを明示的に設定
    const tabFieldsMap = {
      tab1: ["field-13312472","field-13312473"], 
      tab2: ["field-13312474","field-13312475"],
      tab3: ["field-13312476","field-13312477","field-13312478","field-13312479"]
    };

    Object.keys(tabFieldsMap).forEach(tabSpace => {
      const container = document.getElementById(`${getSpaceId(tabSpace)}-container`);
      if (!container) return;
      tabFieldsMap[tabSpace].forEach(id => {
        // 修正:編集画面・詳細画面どちらでもフィールド取得可能
        const field = document.querySelector(`#${id}`);
        if (field && !container.contains(field)) {
          container.appendChild(field);
          console.log(`✅ フィールド ${id} を ${tabSpace} に移動`);
        }
      });
    });

    const firstButton = tabHeaderContainer.querySelector("button");
    if (firstButton) firstButton.click();
  };

  // MutationObserver で DOM 準備完了を監視
  kintone.events.on(EVENTS, function (event) {
    const config = kintone.plugin.app.getConfig(PLUGIN_ID);
    if (!config || !config.tabs) return;

    const tabsData = JSON.parse(config.tabs);

    const observer = new MutationObserver(() => {
      const ready = tabsData.every(tab => document.getElementById(`user-js-${tab.space}`));
      if (ready) {
        observer.disconnect();
        initializeTabs(event);
      }
    });

    observer.observe(document.body, { childList: true, subtree: true });
  });
})();
コードをここに入力または貼り付け

既にやっていたら申し訳ないのですが、まずはブレークポイントを張ってコードの各部分が正しく動いているか確認してみてはいかがでしょうか。

デバッグのやり方は cybozu developer network でも紹介されているので、一度読んでみると良いかもしれません。

例えば

このあたりでフィールド要素を移動していますが、ここが想定どおり動いているか、変数の中身を確認しながら追っていった方がいいと思います。


それはそうとして、一応、非推奨な DOM 操作にあたるので、プラグインとして使いまわすのであれば十分注意したほうが良いと思います。

個人的には kintone.app.record.setFieldShown() を使って、フィールドの表示/非表示の切替でそれっぽく見せるほうが安全だと思います。

「いいね!」 3

なるほど、ありがとうございます。
でもまだタブに入らず、悔しい日々を過ごしています…:head_shaking_vertically:

@wv-sumichan さんのご指摘のとおり、こういうケースはブレークポイントはって、1行1行処理をすすめたりするとわかることがあるとおもいます
やはりappendChild付近が一番あやしいとおもうので、まずはそこ付近でどこまで到達してるか?をみたほうがよさそうにみえますね

自分も、住田さんの

個人的には kintone.app.record.setFieldShown() を使って、フィールドの表示/非表示の切替でそれっぽく見せるほうが安全だと思います。

の意見に同感です!:+1:

ちなみにですが...
自分は、既製Plugin を利用していますが、実装方法は
①Boost! Attachment で「タブ」を作成
②Boost! Style で「表示」の切換え
をしています

ご参考に、動作GIF と 設定画面を貼っておきます:blush:

TAB表示Plugin

このトピックはベストアンサーに選ばれた返信から 3 日が経過したので自動的にクローズされました。新たに返信することはできません。