javascript 使用Handlebars(HTML)为迭代组件构造JSON对象,以创建3个级别的相关内容

mpbci0fu  于 2023-05-05  发布在  Java
关注(0)|答案(1)|浏览(96)

我正在创建一个大型菜单组件-这基本上是采取的3个层次的内容的格式。
L1 s:在“典型”导航栏中水平显示(当悬停在扩展菜单上时)
L2:应该是L1的“子”和L3的父。(显示在mega-menu左侧的垂直列表中)
L3:显示为figure s,带有img和标题
我将下面的JSON对象放在一起,试图使这个组件数据驱动(静态内容不适用于我的用例):

{
    "desktopL1s": [{
            "label": "Level 1 Heading 1"
        },
        {
            "label": "Level 1 Heading 2"
        },
        {
            "label": "Level 1 Heading 3"
        },
        {
            "label": "Level 1 Heading 4"
        },
        {
            "label": "Level 1 Heading 5"
        }
    ],
    "desktopL2s": [{
    "label": "Level 2 Item 1",
    "desktopL3s": [{
        "imgSrc": "../../assets/images/mega-menu/image1.png",
        "caption": "Level 3 Child 1"
      },
      {
        "imgSrc": "../../assets/images/mega-menu/image2.png",
        "caption": "Level 3 Child 2"
      },
      {
        "imgSrc": "../../assets/images/mega-menu/image3.png",
        "caption": "Level 3 Child 3"
      }
    ]
  },
  {
    "label": "Level 2 Item 2",
    "desktopL3s": [{
      "imgSrc": "../../assets/images/mega-menu/image4.png",
      "caption": "Level 3 Child 1"
    },
    {
      "imgSrc": "../../assets/images/mega-menu/image5.png",
      "caption": "Level 3 Child 2"
    }
  ]
  }
 ]
}

下面的HTML使用Handlebars来遍历我的代码中的各个元素:

<div class="desktop-navigation d-none d-lg-block">
    <ul
      class="nav-menu d-flex flex-row justify-content-between align-items-center"
    >
      {{#each this.desktopL1s}}
      <li id="{{id}}">
        <a href="#">{{label}} </a>
      </li>
      {{/each}}
    </ul>

    <div class="mega-menu">
      <div class="row">
        <div class="content-links col-md-3">
          <ul>
            {{#each this.desktopL2s}}
            <li><a href="#">{{label}}</a></li>
            {{/each}}
          </ul>
        </div>
        <div class="col-md-9 content-panel">
          <div class="d-flex flex-wrap">
            {{#each this.desktopL3s}}
            <figure>
              <a href="#"
                ><img class="content-square" src="{{imgSrc}}" alt="{{altText}}"
              /></a>
              <figcaption>{{caption}}</figcaption>
            </figure>
            {{/each}}
          </div>
          <button class="close-btn-desktop">
            <img src="../../assets/cross.svg" alt="close-icon-desktop" />
          </button>
          <button class="btn btn-tertiary" type="button">
            Explore level 2 content
          </button>
        </div>
      </div>
    </div>
  </div>

有人能建议如何更好地构建我的JSON,以便在3个级别之间存在实际的关系吗?l1 s已经被稍微抽象化了,因为它位于与实际的mega菜单不同的div中,但是关于如何正确构建它的一些指针将非常感谢。
编辑:我已经附上了标记如何呈现,希望能提供对每个级别关系的洞察:

myzjeezk

myzjeezk1#

你可以构造你的JSON,以反映你的对象之间的关系。例如:

{
  "desktopL1s": [
    {
      "label": "Label 1",
      "desktopL2s": [
        {
          "label": "Label 1 > Label 2",
          "desktopL3s": [
            {
              "caption": "Label 1 > Label 2 > Label 3"
            }
          ]
        }
      ]
    }
  ]
}

然后可以在模板中使用嵌套的{{#each}}循环来呈现每个级别的导航。
更具挑战性的部分是创建鼠标事件处理程序来显示/隐藏导航项。一种方法是根据每个导航项的索引及其祖先的索引为每个导航项创建标识符。
顶级导航项如下所示:['0', '1', '2']
后续级别上的项目将使用分隔符,如_,将自己的索引与其父级的索引分开:['0_0', '0_1', '1_0', '1_1', '2_0', '2_1']
第三层,也是如此。

const template = Handlebars.compile(document.getElementById('Template').innerHTML);

const data = {
  "desktopL1s": [
    {
      "label": "Level 1 Heading 1",
      "desktopL2s": [
        {
          "label": "Heading 1 > Level 2 > Item 1",
          "desktopL3s": [
            {
              "imgSrc": "../../assets/images/mega-menu/image1.png",
              "caption": "Level 3 Child 1"
            },
            {
              "imgSrc": "../../assets/images/mega-menu/image2.png",
              "caption": "Level 3 Child 2"
            },
            {
              "imgSrc": "../../assets/images/mega-menu/image3.png",
              "caption": "Level 3 Child 3"
            }
          ]
        },
        {
          "label": "Heading 1 > Level 2 Item 2",
          "desktopL3s": [
            {
              "imgSrc": "../../assets/images/mega-menu/image4.png",
              "caption": "Level 3 Child 1"
            },
            {
              "imgSrc": "../../assets/images/mega-menu/image5.png",
              "caption": "Level 3 Child 2"
            }
          ]
        }
      ]
    },
    {
      "label": "Level 1 Heading 2",
      "desktopL2s": [
        {
          "label": "Heading 2 > Level 2 > Item 1",
          "desktopL3s": [
            {
              "imgSrc": "../../assets/images/mega-menu/image1.png",
              "caption": "Level 3 Child 1"
            },
            {
              "imgSrc": "../../assets/images/mega-menu/image2.png",
              "caption": "Level 3 Child 2"
            },
            {
              "imgSrc": "../../assets/images/mega-menu/image3.png",
              "caption": "Level 3 Child 3"
            }
          ]
        },
        {
          "label": "Heading 2 > Level 2 Item 2",
          "desktopL3s": [
            {
              "imgSrc": "../../assets/images/mega-menu/image4.png",
              "caption": "Level 3 Child 1"
            },
            {
              "imgSrc": "../../assets/images/mega-menu/image5.png",
              "caption": "Level 3 Child 2"
            }
          ]
        }
      ]
    },
    {
      "label": "Level 1 Heading 3",
      "desktopL2s": []
    },
    {
      "label": "Level 1 Heading 4",
      "desktopL2s": []
    },
    {
      "label": "Level 1 Heading 5",
      "desktopL2s": []
    }
  ]
};

const output = template(data);

document.body.innerHTML = output;

$('[data-id]').on('mouseenter', function () {
  const $this = $(this);
  const targetId = String($this.data('id'));
  const targetParts = targetId.split('_');
  const targetLevel = targetParts.length - 1;
  
  $('[data-id]').each(function () {
    const $this = $(this);
    const id = String($this.data('id'));
    const parts = id.split('_');
    const level = parts.length - 1;
    // don't affect items on a lower-level
    if (level <= targetLevel) { return; }
    
    // show items on the level 1 higher than the target
    // whose id starts with the id of the target
    const isToShow = level === (targetLevel + 1) && id.startsWith(targetId);
    
    $this.toggle(isToShow);
  });
});
body {
  margin: 0;
}

.mega-menu {
  background: azure;
  border: 1px solid aquamarine;
}

.nav-menu {
  display: flex;
  list-style: none;
  margin: 0;
  padding: 0;
}

.nav-menu li {
  background: coral;
  padding: 10px 5px;
}

.nav-level-2 {
  list-style: none;
  margin: 0;
  padding: 0;
}

.nav-level-2 li {
  background: darkkhaki;
  display: none;
  padding: 10px 5px;
}

.nav-level-3 {
  background: palegreen;
  display: none;
}

.row {
  display: flex;
}

.content-links {
  background: lightblue;
  flex: 0 0 33.3%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.7/handlebars.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script id="Template" type="text/template">
  <div class="desktop-navigation d-none d-lg-block">
    <ul
      class="nav-menu d-flex flex-row justify-content-between align-items-center"
    >
      {{#each this.desktopL1s}}
        <li id="{{id}}" data-id="{{@index}}">
          <a href="#">[{{@index}}] {{label}}</a>
        </li>
      {{/each}}
    </ul>

    <div class="mega-menu">
      <div class="row">
        <div class="content-links col-md-3">
            <ul class="nav-level-2">
                {{#each this.desktopL1s}}
                    {{#each this.desktopL2s}}
                <li data-id="{{@../index}}_{{@index}}"><a href="#">[{{@../index}}_{{@index}}] {{label}}</a></li>
                {{/each}}
             {{/each}}
         </ul>
        </div>
        <div class="col-md-9 content-panel">
          <div class="d-flex flex-wrap">
            {{#each this.desktopL1s}}
                {{#each this.desktopL2s}}
                {{#each this.desktopL3s}}
                  <figure class="nav-level-3" data-id="{{@../../index}}_{{@../index}}_{{@index}}">
                    <a href="#"
                      ><img class="content-square" src="{{imgSrc}}" alt="{{altText}}"
                    /></a>
                    <figcaption>[{{@../../index}}_{{@../index}}_{{@index}}]{{caption}}</figcaption>
                  </figure>
                {{/each}}
                {{/each}}
            {{/each}}
          </div>
          <button class="close-btn-desktop">
            <img src="../../assets/cross.svg" alt="close-icon-desktop" />
          </button>
          <button class="btn btn-tertiary" type="button">
            Explore level 2 content
          </button>
        </div>
      </div>
    </div>
  </div>
</script>

相关问题