【问题标题】:Create HTML using javascript with context/variables使用带有上下文/变量的 javascript 创建 HTML
【发布时间】:2019-01-22 22:00:42
【问题描述】:

我想使用 javascript 从存储的对象创建 HTML。它们包括带有 onclick 函数的按钮,这些按钮调用对象内部的函数。

我尝试使用 document.createElement("Div") 创建每个节点并设置其内容/类...但发现此解决方案非常笨拙。

目前我正在将节点的 innerHTML 设置为我创建的字符串,这样更具可读性。

let objHTML = document.createElement("Div");
objHTML.innerHTML = 
`<div class="ObjWrap ${status}">
    <p class="objName">${name}</p>
    <p class="objContent">${content}</p>
    <button class="objEdit" onclick="${edit(evt)}">e</button>
    <button class="objActive" onclick="${activity(evt)}">x</button>
</div>`;

编辑和活动功能是对象的本地和访问变量,所以我不能写“onclick =“编辑(evt)”... - 但我的解决方案也不起作用,我猜编辑和活动功能只是评估并放入 html。我可以使用事件侦听器而不是按钮上的 onclick 事件,但这又不那么可读,我还必须再次访问按钮以附加事件侦听器(在这种情况下类似于 objHTML .getElementsByTagName('div')[0].getElementsByTagName('div')[0].getElementsByTagName('button')[0/1])。

我的问题是如何创建我的 HTML,同时保持它的可读性,并允许我添加将变量引用到按钮/任何元素的 onclick 函数。我不能使用 Id,因为这些元素被多次创建。

编辑:Codepen 显示我的意思,断章取义,所以有些事情可能没有意义:https://codepen.io/anon/pen/jdEqwQ

【问题讨论】:

  • 您正在寻找 React 或 Angular。
  • 我或多或少刚开始使用 javascript,希望在学习新框架之前能够做所有的事情,但很高兴知道 React 和 Angular 的用途
  • 你应该考虑使用 Vue 之类的框架。这个想法是将业务和演示之间的关注点分开。
  • 你能在你的问题中添加一个“存储对象”的例子吗?
  • 是的,一定要学习原生 JavaScript!但是,当您感到自信时(看起来确实如此,因为您基本上是在想象其中一个库),也请花点时间学习它们!当然不是每个站点都需要使用这些库,但是对于类似应用程序的站点,如果您不想发疯,它们基本上是必须的。

标签: javascript html


【解决方案1】:

我相信您正在寻找的是一个视图框架。基本上,一个库可以完成所有这些有趣的数据绑定业务、数据转换等等。

有一个whole bunch of them,但现在最流行和受支持的是ReactAngularVue

他们将负责此类数据绑定,并确保代码和 UI 之间的值保持同步,并尽可能有效地重用元素。他们每个人都有不同的工作流程,您需要进行一些研究以找到您认为最适合您的用例和知识的工作流程。

如果您的应用程序很小,您可能不需要使用它们,但如果您已经感受到这种痛苦,请考虑一下,它们是非常完善的工具。

【讨论】:

    【解决方案2】:

    是否可以选择定义您希望按钮执行的功能?

    这将创建您正在寻找的 div 并向其中添加已定义函数的处理程序。

    function test() { console.log('test') };
    let obj = document.createElement("Div");
    obj.innerHTML = '<div>test <button onclick="test()">run</button></div>';
    document.body.appendChild(obj)
    

    但除此之外,我同意 cmets 表明使用像 vue 这样的框架。

    编辑:这样的事情是否朝着您期望的方向发展?

    function generate() {
        function test() {
            return function(e) {
                console.log('event', e);
            }
        };
    
        const btn1 = document.createElement('button');
        btn1.onclick = test();
        btn1.innerHTML = 'Run'
    
    
        const obj = document.createElement("Div");
    
    
        obj.innerHTML = '<div>test<br></div>';
        obj.appendChild(btn1);
        document.body.appendChild(obj)
    }
    
    generate()
    

    我怀疑是否有可能在其中获取函数引用作为不在全局范围内的字符串。不过,这与您编写 HTML 模板的简洁方法背道而驰。

    【讨论】:

    • 函数已定义,但它们使用您无法从 onclick“区域”中访问(据我所知)的局部变量
    • @Teiem 我尝试使用范围函数更新了答案,但我不确定这是否是您正在寻找的。​​span>
    • 是的,这描述了我的问题,我也不知道您可以为此使用 node.onclick = xxx 而不是事件监听器(同时保持上下文)
    • 很高兴它对您有用!如果你接受这个作为答案,我会很高兴,假设它实际上解决了你遇到的问题。您实际上还可以添加 onclick 处理程序,而不是分配 onclick 属性。
    • 这是我在写成​​字符串之前的做法,问题是我在添加
    【解决方案3】:

    这实际上并不难创建。它有助于制作实用函数来处理繁重的工作:

    let create = (tag, opts = {}) => (Object.assign(document.createElement(tag), opts)),
    nested = (p, c) =>(c) ?(p.appendChild(c), (c2) => nested.call(p, p, c2)) : p;
    

    create 所做的只是让您以更快的速度更轻松地创建具有属性和属性的元素。

    nested 所做的只是将节点一个接一个地放置在原始容器中。容器是嵌套中的第一个元素,第一个子元素是第二个,依此类推。

    然后我们加载一些虚拟数据:

    //your template stuff
    let template=new Map([["status","online"],["name","Zak"],["content","This is some 
    content"],["edit",function(){alert("editing!")}],["activity",function(){alert("some 
    activity!")}]]);
    

    把它放在一起:

    let objHTML = nested(create("DIV", {
      className: "container"
    }), create("p", {
      className: "objName"
    }))(create("button", {
      className: "objEdit",
      onclick: template.get("edit"),
      textContent: "edit!"
    }))(create("button", {
      className: "objContent",
      textContent: template.get("content"),
      onclick: () => alert("clicking does nothing... oh wait")
    }))(create("button", {
      className: "objActive",
      onclick: template.get("activity"),
      textContent: "activity"
    }))();
    

    瞧。差不多就这些了,显然我们可以单独使用以上两个函数做更多的事情。

    这是您上面代码的演示(只是快速浏览一下

    let create = (tag, opts = {}) => (Object.assign(document.createElement(tag), opts)),
      nested = (p, c) =>  (c) ?(p.appendChild(c), (c2) => nested.call(p, p, c2)) : p;
    
    
    //your template stuff
    let template=new Map([["status","online"],["name","Zak"],["content","This is some content"],["edit",function(){alert("editing!")}],["activity",function(){alert("some activity!")}]]);
    
    let objHTML = nested(
    create("DIV", {
      className: "container"
    }), create("p", {
      className: "objName"
    }))(create("button", {
      className: "objEdit",
      onclick: template.get("edit"),
      textContent: "edit!"
    }))(create("button", {
      className: "objContent",
      textContent: template.get("content"),
      onclick: ()=>alert("clicking does nothing... oh wait"),
    }))(create("button", {
      className: "objActive",
      onclick: template.get("activity"),
      textContent: "activity"
    })
    )();
    
    
    
    document.body.appendChild(objHTML);
    console.log(objHTML);

    我确实同意其他人的观点,即有很多库可以为这些类型的事物提供服务,但重要的是要认识到,其中许多库并不难到需要大量的外部资源,而您只需滚动即可你自己的 - 当然,任务足够小,你不需要大量额外的花里胡哨。

    注意:顺便说一句,没有 JSX 转译器的 React 看起来非常像这段代码 - 尽管正如我上面所说的,React 仍然可以访问许多东西您将不得不花费大量时间来重新创建-只是不要卖空自己。进入基础并有时检查一下齿轮是件好事! 快乐编码!

    【讨论】:

      【解决方案4】:

      如果您正在学习 JavaScript,并且不想为新的库或框架而烦恼,您可以使用 vanilla JS 类。这意味着您需要设置处理侦听器的方法,而不是将它们放在 HTML 中,但这样更简洁。

      这个小例子并不完美,但它可能会让你知道你能做什么。如果您决定在某个时候最终迁移到 React 的功能,它会被故意设计成类似于 React 的功能。

      class Component {
      
        constructor(props, i) {
      
          // Assign the component index as `id`
          this.state = { id: i };
      
          // Loop over the passed in data and add them
          // to the class context
          for (let [p, v] of Object.entries(props)) {
            this.state[p] = v;
          };
      
          // Bind the edit handler so that the context sticks
          this.handleEdit = this.handleEdit.bind(this);
      
          // Now create the wrapper element, render the HTML
          // and set the listeners
          this.addElement();
          this.render();
          this.setListeners();
        }
      
        // `querySelector` and `addEventListener` shortcut
        // Takes a selector and a function to call when the button
        // is clicked
        qsl(s, fn) {
          const el = document.querySelector(s);
          return el.addEventListener('click', fn, false);
        }
      
        // set the button listeners for HTML corresponding
        // to this component id
        setListeners() {
          const { id } = this.state;
          const selector = `[data-id="${id}"]`;
          this.qsl(`${selector} .edit`, this.handleEdit);
       }
      
        // An example based on your code - toggle
        // the status and then re-render the HTML
        handleEdit() {
          const { status } = this.state;
          this.state.status = !status;
          this.render();
        }
      
        // Adds the wrapper element to the DOM and caches it
        addElement() {
          const { id } = this.state;
          const html = `<div data-id="${id}" class="component"></div>`;
          document.body.insertAdjacentHTML('beforeend', html);
          this.wrapper = document.querySelector(`[data-id="${id}"].component`);
        }
      
        // return your compiled template
        getHTML() {
          const { id, status, name } = this.state;
          return `<div data-id="${id}"><p>${status} - ${name}</p><button class="edit">e</button></div>`;
        }
      
        // Render the output.
        // Sadly, because we're not using a virtual DOM (like React
        // for example) we're destroying the existing HTML and replacing it
        // so we need to re-add the event listeners to that new markup
        render() {
          this.wrapper.innerHTML = this.getHTML();
          this.setListeners();
        }
      
      }
      
      // Set up the data for the new objects
      const data = [
        { status: false, name: 'Bob', content: '' },
        { status: true, name: 'Mick', content: '' }
      ];
      
      // Loop over the data to create the components
      // We pass in the index to the class as it makes for
      // a good id
      const components = data.map((component, i) => {
        return new Component(component, i);
      });
      .component {
        border: 1px solid #454545;
        width: 50%;
        padding: 0.2em;
        margin-bottom: 0.1em;
      }
      &lt;div class="wrapper"&gt;&lt;/div&gt;

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-03-11
        • 1970-01-01
        • 2021-10-22
        • 1970-01-01
        • 2021-02-28
        • 2013-03-21
        • 1970-01-01
        相关资源
        最近更新 更多