使用 append child 时更短的方法

Shorter method when using append child

我正在从通过获取请求返回的对象创建一些元素,想知道是否有更好的方法来简化它?

就目前而言,它按预期工作,但对于看似简单的任务来说,它似乎太长了?

async function getJobs() {

  const response = await fetch('https://app.beapplied.com/public/all-jobs');
  const data = await response.json();
  console.log(data.jobs);
  for (const job of data.jobs) {
    const jobContainer = document.getElementById("jobData");

    const jobListing = document.createElement('li');
    jobContainer.appendChild(jobListing);
    jobListing.setAttribute('class', 'whr-item')

    const jobLink = document.createElement('a');
    jobListing.appendChild(jobLink);
    jobLink.setAttribute('href', job.applyLink)

    let jobTitle = document.createElement("h3");
    jobTitle.innerHTML = job.title;
    jobTitle.setAttribute('class', 'whr-title')
    jobLink.appendChild(jobTitle);

    const jobInfo = document.createElement('ul');
    jobListing.appendChild(jobInfo);
    jobInfo.setAttribute('class', 'whr-info')

    let jobTeam = document.createElement("li");
    jobTeam.innerHTML = job.team;
    jobTeam.setAttribute('class', 'whr-dept')
    jobInfo.appendChild(jobTeam);

    let jobLocation = document.createElement("li");
    jobLocation.innerHTML = job.location
    jobLocation.setAttribute('class', 'whr-location')
    jobInfo.appendChild(jobLocation);
  }
}

getJobs()
<ul id="jobData" class="whr-items"></ul>

由于您要分配给嵌套子项的 .innerHTML,写出 HTML 字符串和插值将是等效的,并且一目了然更容易理解:

const jobContainer = document.getElementById("jobData");
for (const job of data.jobs) {
    jobContainer.insertAdjacentHTML(
        'beforeend',
        `<li class="whr-item">
          <a href="${job.applyLink}">
            <h3>${job.title}</h3>
          </a>
          <ul>
            <li class="whr-dept">${job.team}</li>
            <li class="whr-location">${job.location}</li>
          </ul>
        </li>
        `
    );
}

也就是说,直接插入外部值 HTML(或将 .innerHTML 与外部值一起使用)存在安全风险,因为它允许任意代码执行。我建议写出 HTML 而没有 插入的值,然后在创建结构后安全地插入它们。例如,您可以将 createElement 用于 .whr-item,这样您就可以引用它,插入它的 HTML 而没有动态值,然后执行

item.querySelector('a').href = job.applyLink;
item.querySelector('h3').href = job.title;
item.querySelector('.whr-dept').href = job.team;
item.querySelector('.whr-location').href = job.location;

这将是一种更安全的方法。

我创建了自己的 class 来制作包含标签、class 和文本的简单元素。您可以对其进行扩展和修改以满足您的所有需求。

更新:

我为您的问题添加了一个如何使用 class 的示例。

由于 CORS 错误,我复制了请求数据并创建了一个对象,而不是执行获取请求。这也意味着您必须向下滚动一点,因为 json 数据很长。

我还制作了一个名为 insert 的辅助函数,用于更轻松地附加节点。

class Elm {
  constructor(tag) {
    this.tag = tag;
    this.classList = [];
    this.attributes = [];
    this.txt = ""
    return this;
  }

  class(cls) {
    this.classList.push(cls);
    return this;
  }

  text(txt) {
    this.txt = txt;
    return this;
  }

  attr(attr, val) {
    this.attributes.push({
      attr,
      val
    });
    return this;
  }

  create() {
    const e = document.createElement(this.tag);
    if (this.classList.length)
      e.classList.add(...this.classList);
    if (this.txt.length) e.textContent = this.txt;
    if (this.attributes.length) this.attributes.forEach(atr => e.setAttribute(atr.attr, atr.val));
    return e;
  }
}

function insert(targetNode, childrenArray) {
  targetNode.append(...childrenArray);
  return targetNode;
}

async function getJobs() {
  const data = {
    jobs: [{
        id: 12878,
        title: "Facilities Coordinator",
        location: "Avon Tyrrell Outdoor Centre",
        closes: "2021-09-27T07:59:00.000Z",
        public: true,
        archived: false,
        phase: "ACTIVE_APPLYING",
        OrgId: 1415,
        createdAt: "2021-09-15T10:30:13.312Z",
        package: "£18,525",
        hash: "dbppfgddee",
        Org: {
          customLogoURL: "https://beapplied.global.ssl.fastly.net/org/1415/logo.png",
          apiUrl: "https://app.beapplied.com/B1o/public-jobs",
          retentionMonths: 48,
          id: 1415,
          name: "UK Youth",
          customLogo: true,
          domain: "ukyouth.org",
          OrgTiers: [{
            id: "1382",
            OrgId: "1415",
            TierId: "4",
            createdAt: "2021-02-19T18:34:45.692Z",
            updatedAt: "2021-02-19T18:34:45.692Z",
          }, ],
        },
        orgLogo: "https://beapplied.global.ssl.fastly.net/org/1415/logo.png",
      },
      {
        id: 12875,
        title: "Digital Recruitment Officer",
        location: "Home based with some travel",
        closes: "2021-10-15T11:00:00.000Z",
        public: true,
        archived: false,
        phase: "ACTIVE_APPLYING",
        OrgId: 1085,
        createdAt: "2021-09-15T09:30:44.770Z",
        package: "£30,000 per annum plus benefits",
        hash: "lalasq2s0f",
        Org: {
          customLogoURL: "https://beapplied.global.ssl.fastly.net/org/1085/logo.png",
          apiUrl: "https://app.beapplied.com/NDV/public-jobs",
          retentionMonths: 48,
          id: 1085,
          name: "The Trussell Trust",
          customLogo: true,
          domain: "trusselltrust.org",
          OrgTiers: [{
            id: "1052",
            OrgId: "1085",
            TierId: "4",
            createdAt: "2020-08-19T15:36:57.700Z",
            updatedAt: "2020-08-19T15:36:57.700Z",
          }, ],
        },
        orgLogo: "https://beapplied.global.ssl.fastly.net/org/1085/logo.png",
      },
      {
        id: 12873,
        title: "Editorial Manager",
        location: "Central London, Blackfriars - we offer flexibility to work remotely",
        closes: "2021-09-28T07:00:00.000Z",
        public: true,
        archived: false,
        phase: "ACTIVE_APPLYING",
        OrgId: 2,
        createdAt: "2021-09-15T07:30:43.858Z",
        package: "circa £45k plus excellent benefits",
        hash: "yefkapfn7d",
        Org: {
          customLogoURL: "https://logo.clearbit.com/nesta.org.uk",
          apiUrl: "https://app.beapplied.com/0M/public-jobs",
          retentionMonths: 48,
          id: 2,
          name: "Nesta",
          customLogo: false,
          domain: "nesta.org.uk",
          OrgTiers: [{
            id: "93",
            OrgId: "2",
            TierId: "4",
            createdAt: "2017-02-13T12:56:56.014Z",
            updatedAt: "2018-02-21T10:42:01.318Z",
          }, ],
        },
        orgLogo: "https://logo.clearbit.com/nesta.org.uk",
      },
      {
        id: 12867,
        title: "Partnerships Senior Account Executive - Motorsport & Football",
        location: "London",
        closes: "2021-09-21T22:59:00.000Z",
        public: true,
        archived: false,
        phase: "ACTIVE_APPLYING",
        OrgId: 878,
        createdAt: "2021-09-14T18:24:32.381Z",
        package: "£27,000 ",
        hash: "ozd7o9yxmy",
        Org: {
          customLogoURL: "https://beapplied.global.ssl.fastly.net/org/878/logo.png",
          apiUrl: "https://app.beapplied.com/gDo/public-jobs",
          retentionMonths: 48,
          id: 878,
          name: "OMG UK",
          customLogo: true,
          domain: "omnicommediagroup.com",
          OrgTiers: [{
            id: "845",
            OrgId: "878",
            TierId: "4",
            createdAt: "2020-02-17T15:45:22.547Z",
            updatedAt: "2020-02-17T15:45:22.547Z",
          }, ],
        },
        orgLogo: "https://beapplied.global.ssl.fastly.net/org/878/logo.png",
      },
      {
        id: 12862,
        title: "Researcher",
        location: "London or Birmingham ",
        closes: "2021-10-17T19:00:00.000Z",
        public: true,
        archived: false,
        phase: "ACTIVE_APPLYING",
        OrgId: 1490,
        createdAt: "2021-09-14T14:01:43.555Z",
        package: "£24,000-30,000 PA depending on experience",
        hash: "pjqnt94d3x",
        Org: {
          customLogoURL: "https://beapplied.global.ssl.fastly.net/org/1490/logo.png",
          apiUrl: "https://app.beapplied.com/0vJ/public-jobs",
          retentionMonths: 48,
          id: 1490,
          name: "BOP Consulting",
          customLogo: true,
          domain: "bop.co.uk",
          OrgTiers: [{
            id: "1457",
            OrgId: "1490",
            TierId: "4",
            createdAt: "2021-04-27T20:02:37.063Z",
            updatedAt: "2021-04-27T20:02:37.063Z",
          }, ],
        },
        orgLogo: "https://beapplied.global.ssl.fastly.net/org/1490/logo.png",
      },
      {
        id: 12861,
        title: "Talent Manager: FTC Nov 2021 -April 2022",
        location: "London and Remote - we are a hybrid organisation",
        closes: "2021-09-29T11:00:00.000Z",
        public: true,
        archived: false,
        phase: "ACTIVE_APPLYING",
        OrgId: 706,
        createdAt: "2021-09-14T13:57:57.425Z",
        package: "£37,600 - £41,000",
        hash: "9fcnvbhovr",
        Org: {
          customLogoURL: "https://beapplied.global.ssl.fastly.net/org/706/logo.png",
          apiUrl: "https://app.beapplied.com/X4M/public-jobs",
          retentionMonths: 48,
          id: 706,
          name: "Comic Relief",
          customLogo: true,
          domain: "comicrelief.com",
          OrgTiers: [{
            id: "673",
            OrgId: "706",
            TierId: "4",
            createdAt: "2018-05-10T07:28:08.566Z",
            updatedAt: "2019-01-08T08:07:28.542Z",
          }, ],
        },
        orgLogo: "https://beapplied.global.ssl.fastly.net/org/706/logo.png",
      },
      {
        id: 12860,
        title: "Analyst (A Sustainable Future)",
        location: "Central London, Blackfriars - we offer flexibility to work remotely",
        closes: "2021-10-05T07:00:00.000Z",
        public: true,
        archived: false,
        phase: "ACTIVE_APPLYING",
        OrgId: 2,
        createdAt: "2021-09-14T12:42:54.213Z",
        package: "£34k, plus excellent benefits",
        hash: "1yut7jhl6h",
        Org: {
          customLogoURL: "https://logo.clearbit.com/nesta.org.uk",
          apiUrl: "https://app.beapplied.com/0M/public-jobs",
          retentionMonths: 48,
          id: 2,
          name: "Nesta",
          customLogo: false,
          domain: "nesta.org.uk",
          OrgTiers: [{
            id: "93",
            OrgId: "2",
            TierId: "4",
            createdAt: "2017-02-13T12:56:56.014Z",
            updatedAt: "2018-02-21T10:42:01.318Z",
          }, ],
        },
        orgLogo: "https://logo.clearbit.com/nesta.org.uk",
      },
    ],
  };

  const container = document.querySelector("#jobData");

  data.jobs.forEach(job => {

    const jobItem = new Elm("li")
      .class("whr-item")
      .create();

    const jobTitle = new Elm("a")
      .attr("href", job.Org.apiUrl)
      .text(job.title)
      .create();

    const jobInfo = new Elm("ul")
      .class("whr-info")
      .create();

    const jobTeam = new Elm("li")
      .class("whr-dept")
      .text(`Group: ${job.Org.name}`)
      .create();

    const jobLocation = new Elm("li")
      .class("whr-location")
      .text(`Location: ${job.location}`)
      .create();

    // <JobItem class="whr-item">
    //   <JobTitle href="">Title of the Job </JobTitle>
    //   <JobInfo class="whr-info">
    //     <JobTeam class="whr-dept">Team Name</JobTeam>
    //     <JobLocation class="whr-location">Location of the Job</JobLocation>
    //   </JobInfo>
    // </JobItem>

    const final = insert(jobItem, [
      jobTitle,
      insert(jobInfo, [
        jobTeam,
        jobLocation,
      ]),
    ]);

    container.appendChild(final);
  });
}

window.onload = () => getJobs();
html,
body {
  padding: 0;
  margin: 0;
  height: 100%;
}

.whr-items {
  padding: 0;
}

.whr-item {
  display: flex;
  flex-direction: column;
  padding: 0.75rem 1rem;
  list-style-type: none;
}

.whr-item:nth-child(2n) {
  background-color: lightgrey;
}

.whr-item a {
  text-decoration: none;
  color: black;
  font-size: 1.2rem;
  margin-bottom: 0.5rem;
}

.whr-item li,
.whr-item ul {
  padding: 0;
  list-style-type: none;
}
<ul id="jobData" class="whr-items"></ul>

function getJobs() {

    fetch('https://app.beapplied.com/public/all-jobs')
    .then(response => response.json())
    .then(data => data.jobs.map(job => createJobHTML(job))) // create HTML for each job
    .then(htmlArr => document.getElementById("jobData").innerHTML = htmlArr.join(''));
  
}

function createJobHTML(job){
  return `<li class="whr-item">
      <a href="${job.applyLink}">
        <h3 class="whr-title">${job.title}</h3>
      </a>
      <ul class="whr-info">
        <li class="whr-dept">${job?.team ?? '-'}</li>
        <li class="whr-location">${job?.location ?? '-'}</li>
      </ul>
    </li>`;
}

getJobs();
<ul id="jobData" class="whr-items"></ul>