Parse an HTML template as you require it in JavaScript

▼魔方 西西 提交于 2019-12-10 15:20:03

问题


I spent hours on a basic webpack configuration but I'm still not able to make it work. My aim is to perform the parsing of a html template as you import it in a JavaScript file. It looks like a common use-case, but there should be something odd in my webpack configuration or in my understanding.

I looked for configurations of html-loader, html-webpack-plugin, posthtml as well as pug and I've read all of their documentations, but none of them worked.

According to PostHTML Readme:

PostHTML is a tool for transforming HTML/XML with JS plugins. PostHTML itself is very small. It includes only a HTML parser, a HTML node tree API and a node tree stringifier.

So, since it was the most promising, I report my attempt with posthtml:

   rules: [
      {
        test: /.html$/,
        use: [
          {
            loader: "html-loader",
            options: {
              minimize: true,
              interpolation: false
            }
          },
          {
            loader: "posthtml-loader"
          }
        ]
      }
    ]

It doesn't return any error but it looks like is totally ignoring the posthtml-loader, since executing import template from 'src/test.html' I get the template as string (how is supposed to do html-loader alone).

In my understanding, loaders are supposed to compile/transform files with different formats and make them available to JavaScript, and since html is the most common type in a front-end project I supposed it was easy, but I'm not finding anything on the internet related to this question.

What I expect is to have a DOM tree object or, anyway, something that can be used by JavaScript.

Is anyone able to help me?

EDIT: My question is about getting a webpack configuration up and working. I know many solution for parsing HTML strings, but they're not applicable here


回答1:


To me it seems like the posthtml-loader is primarily a tool that helps to "prepare" your HTML during the build. Its parser options allow you to break in to the string -> PostHTML AST Tree step, and its plugin options allow you to modify the tree. Then, it stringifies back to HTML.

I couldn't find an option in the webpack plugin to return the interim tree format.


You could write a small custom loader to parse HTML strings to DOM objects:

// custom-loader.js
module.exports = function(content) {
  return `module.exports = (function() {
    const parser = new DOMParser();
    const doc = parser.parseFromString("${content}", "text/html");
    return doc.body.children; 
  }())`;
};

Then, in your webpack.config.js, you can tell webpack to make .html files pass this loader:

// webpack.config.js
module.exports = {
  mode: 'development',
  entry: './main.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'main.bundle.js'
  },
  devtool: "eval-source-map",
  module: {
    rules: [
      {
        test: /\.html$/,
        use: [ './custom-loader' ]
      }
    ]
  }
};

Now, whenever you type something like const template = require('./template.html'); you'll get an HTMLCollection instance rather than just a string.


Note that this loader adds a dependency to DOMParser, which is only available in the browser. You could replace it by something like jsdom if you want to run in non-browser environments.




回答2:


Believe it or not this is not a common use case, importing html files into JavaScript and displaying them sounds like a workaround or a mid-port hack. JavaScript libraries are used to generate dynamic html on the the fly in the browser in response to user input. If this is what you want you should be using React, Vue, Angular, jQuery or similar.

As others have mentioned, the solution to your problem is not to look for a loader that converts HTML into a DOM node, it's for you to do it yourself. The DOM is not present till code is executed in the browser, It's not a compile time transformation. It's a run-time transformation.

import template from 'src/test.html'

const html = document.createElement('div')
html.innerHTML = template

A loader simply cannot do what you ask for. The browser is responsible for parsing HTML and building the DOM tree based on it's platform and vendor specific implementation. querySelector or createElement are API methods for us to access this browser functionality. But we do not own DOM nodes in our code. They are created for us by the platform. Our JavaScript is merely consuming it.

There are some solutions such as DOMParser and jsDOM that can do this on the server. But they are incomplete implementations of the browser DOM and they should not be brought into front-end code meant to be shipped to the browser. They are meant to be used for things such as headless testing, web crawling and other forms of machine consumption of web pages.

If you can present a specific scenario where you cannot have a HTML string parsed in the browser along with the rest of the code and just before it is consumed by JavaScript, by all means show us and we will present a solution. But It is highly likely that you have misunderstood the roles of JavaScript, Webpack, and HTML and the relationship between them.

TLDR: HTML is a string that gets sent to the browser via HTTP and the browser decides how to build a DOM tree based on it's vendor specific implemenation. In front end code there is no alternative except to get the browser to build a DOM tree for you with a string you provide and then consume it with the DOM Api.




回答3:


Well, transforming a string to html is easy. So if you get a string reposonse back for your template you can transform it to a DOM structure like below.

/** Create a NON attached DOM element. This floats in nothing. Hello Dave */
var dave = document.createElement("div");

/** Give dave a body with the HTML string we received from "somewhere" */
dave.innerHTML = "<div class='foo'><input type='text'></div>";

/**
  * dave is now <div><div class='foo'><input type='text'></div></div>
  */
dave.querySelector('input').value = "I'm sorry Dave, I'm afraid I can't do that";


/** ======================================================================
 * Now render we render Dave, this isn't really needed, but will do anyways.
 * We don't need the "wrapping" floating div we created so we get all of Dave's children
 * And let Dave be forgotten about in the abyss of eternity.
 */
var content = dave.children;

var main = document.getElementById('main');
for(var i = 0; i < content.length; i++) {
  main.appendChild(content[i]);
}
.foo {
   background-color: red;
}
.foo input {
   background-color: black;
   color: white;
}
<body id="main">
</body>

You can then do transformation on the 'children' as well, just as you would with a normal DOM tree object.



来源:https://stackoverflow.com/questions/56355899/parse-an-html-template-as-you-require-it-in-javascript

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!