What is the best way to include specific script tags within the <head> and <body> sections when utilizing HtmlWebpackPlugin?

I am currently utilizing HtmlWebpackPlugin in order to create HTML files that include JavaScript.

Now, I am interested in inserting custom scripts at various locations within the <head> and <body> tags

For instance:

How can I,

  1. Insert
    <script> alert('in head tag') </script>
    inside the <head> tag as the first child
  2. Insert
    <script> alert('in body tag') </script>
    inside the <body> tag as the first child

Below is a snippet from my Webpack configuration

        new HtmlWebpackPlugin({
        hash: true,
        chunks: ["app"],
        filename: path.resolve(__dirname, "./public/pages/app.html"),
        title: "Title of webpage",
        template: path.resolve(__dirname, "./src/pages/app.page.html"),
        minify: {
            collapseWhitespace: true
        }
    })

Answer №1

Your inquiry seems a bit unclear, suggesting that you wish to include static script tags in your template. In that case, you simply need to navigate to your src/pages/app.page.html file and insert those two script tags within the head and body sections.

If I understand correctly, you might be asking "How can I incorporate generated bundles into two different parts of my template?" In this scenario, there's a helpful section in the documentation outlining the data passed to the template file:

"htmlWebpackPlugin": {
  "files": {
    "css": [ "main.css" ],
    "js": [ "assets/head_bundle.js", "assets/main_bundle.js"],
    "chunks": {
      "head": {
        "entry": "assets/head_bundle.js",
        "css": [ "main.css" ]
      },
      "main": {
        "entry": "assets/main_bundle.js",
        "css": []
      },
    }
  }
}

If your entry appeared like so

entry: {
  head: './src/file1.js',
  body: './src/file2.js',
}

and your plugin setup was

new HtmlWebpackPlugin({
  template: './src/pages/app.page.ejs' // pay attention to the .ejs extension
})

then from within app.page.ejs, you should be able to access the plugin data and position those entries wherever needed. There exists an expansive ejs example file in their repository. A more concise example, tailored to your specific use case, could be:

<!DOCTYPE html>
<head>
  <% if(htmlWebpackPlugin.files.chunks.head) { %>
  <script src="<%= htmlWebpackPlugin.files.chunks.head.entry %>"></script>
  <% } %>
</head>
<body>
  <% if(htmlWebpackPlugin.files.chunks.body) { %>
  <script src="<%= htmlWebpackPlugin.files.chunks.body.entry %>"></script>
  <% } %>
</body>
</html>

Note that instead of using files.js, I'm utilizing files.chunks since individual files can be accessed by entry name.


Setting Up for Multiple Pages

In a multi-page environment, your WP configuration may look something like this

const pages = [
  'home',
  'about',
];

const conf = {
  entry: {
    // other entries here
  }
  output: {
    path: `${ __dirname }/dist`,
    filename: 'scripts/[name].js'
  },
  plugins: [
    // other plugins here
  ]
};

// adding entries dynamically along with `HtmlWebpackPlugin` for each page
pages.forEach((page) => {
  conf.entry[page] = `./src/pages/${ page }.js`;
  conf.plugins.push(new HtmlWebpackPlugin({
    chunks: [page],
    // customized per-page output
    filename: `${ __dirname }/dist/pages/${ page }.html`,
    googleAnalytics: { /* your props */ },
    // shared head scripts
    headScripts: [
      {
        src: 'scripts/jQuery.js'
      },
      {
        content: `
          console.log('hello world');
          alert('huzah!');
        `
      }
    ],
    // per-page HTML content
    pageContent: fs.readFileSync(`./src/pages/${ page }.html`, 'utf8'),
    // one template for all pages
    template: './src/pages/shell.ejs',
  }));
});

module.exports = conf;

The template would resemble the following structure

<!DOCTYPE html>
<head>
  <%
    for (var i=0; i<htmlWebpackPlugin.options.headScripts.length; i++) {
      var script = htmlWebpackPlugin.options.headScripts[i];
  %>
  <script
    <% if(script.src){ %>src="<%= script.src %>"<% } %>
  >
    <% if(script.content){ %><%= script.content %><% } %>
  </script>
  <% } %>
</head>
<body>
  <% if(htmlWebpackPlugin.options.pageContent) { %>
  <%= htmlWebpackPlugin.options.pageContent %>
  <% } %>

  <% for (var chunk in htmlWebpackPlugin.files.chunks) { %>
  <script src="<%= htmlWebpackPlugin.files.chunks[chunk].entry %>"></script>
  <% } %>

  <% if (htmlWebpackPlugin.options.googleAnalytics) { %>
  <script>
    (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
      (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
      m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
    })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
    <% if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
      ga('create', '<%= htmlWebpackPlugin.options.googleAnalytics.trackingId%>', 'auto');
      <% } else { throw new Error("html-webpack-template requires googleAnalytics.trackingId config"); }%>
    <% if (htmlWebpackPlugin.options.googleAnalytics.pageViewOnLoad) { %>
      ga('send', 'pageview');
    <% } %>
  </script>
  <% } %>
</body>
</html>

Answer №2

Although my response may be delayed, I wanted to share how I successfully resolved the issue in case it can assist someone else.

  1. To address the problem, I made sure to disable auto injection of assets by setting inject:false.
new HtmlWebpackPlugin({
  hash: true, // hash used for cache bursting
  template: "index.html", // original template
  minify: true, // option to minify HTML file
  inject: false,
}) 
  1. I manually rendered assets in the template file.

CSS files were placed in the head section

<% for (var css in htmlWebpackPlugin.files.css) { %>
<link href="<%= htmlWebpackPlugin.files.css[css] %>" rel="stylesheet">
<% } %>

Javascript files were included in the body section

<% for (var js in htmlWebpackPlugin.files.js) { %>
<script src="<%= htmlWebpackPlugin.files.js[js] %>"></script>
<% } %>

This method allows for various filtering and sorting options.

For more information on available options and how to implement them, you can refer to the following link:

https://github.com/jaketrent/html-webpack-template/blob/master/index.ejs

Answer №3

If you want to make use of template parameters, take a look at the demonstration provided in the official example

const path = require('path');
const HtmlWebpackPlugin = require('../..');
const webpackMajorVersion = require('webpack/package.json').version.split('.')[0];
module.exports = {
  context: __dirname,
  entry: './example.js',
  output: {
    path: path.join(__dirname, 'dist/webpack-' + webpackMajorVersion),
    publicPath: '',
    filename: 'bundle.js'
  },
  plugins: [
    new HtmlWebpackPlugin({
      templateParameters: {
        'foo': 'bar'
      },
      template: 'index.ejs'
    })
  ]
};

Answer №4

Encountering the same issue prompted me to develop a solution in the form of a plugin.

  • HtmlWebpackInjector - An auxiliary tool for HtmlWebpackPlugin that facilitates injecting chunks into the head

  • By working in conjunction with HtmlWebpackPlugin, simply appending _head to the chunk name automatically injects it into the head section.

const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlWebpackInjector = require('html-webpack-injector');

module.exports = {
  entry: {
    index: "./index.ts",
    index_head: "./index.css" // add "_head" at the end to inject in head.
  },
  output: {
    path: "./dist",
    filename: "[name].bundle.js"
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./index.html",
      filename: "./dist/index.html",
      chunks: ["index", "index_head"]
    }),
    new HtmlWebpackInjector()
  ]
}

This feature ensures automatic injection of the index chunk into the body and the index_head chunk into the head of the HTML document, resulting in the following final HTML structure:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Archit's App</title>
    <script type="text/javascript" src="index_head.bundle.js"></script> <--injected in head
  </head>
  </head>
  <body>
    <script src="index_bundle.js"></script> <--injected in body
  </body>
</html>

Answer №5

One simple approach that I find effective is as follows:

// webpack.config.js
new HtmlWebpackPlugin({
    template: 'index.html',
    templateParameters: {
        foo: process.env.BAR,
    },
}),
<!-- index.html -->
<% if (foo) { %>
    <script src="bar.min.js"></script>
<% } %>

It works flawlessly.

Answer №6

Apologies for resurrecting an old question, but I encountered a similar issue and ended up creating a solution.

I developed a plugin that effectively addresses the problem.

To resolve this (as of 2019-11-20), you may need to uninstall the current stable version of html-webpack-plugin and install html-webpack-plugin@next instead.

In Summary:

The plugin I created can replace or insert text within the htmlWebpackPlugin output. It enables modification of any text on the page, provided the target string is unique (like a </body> tag).

Implementation details:

This plugin leverages hooks provided by html-webpack-plugin during its compilation process. It searches the compiled output string and incorporates a custom string either before, after, or in place of the identified text.

Motivation behind the creation:

I faced challenges while developing a Wordpress theme framework with Webpack to streamline certain tasks. Specifically, I needed to include an async script tag for browser-sync to interact with the page. Like yourself, I struggled to find a universal method to attach the script without repetitive boilerplate.

Hence, I devised a plugin that inserts the required string into files containing </body>, signifying full-page templates. This ensured automatic page refresh whenever the source file was updated.

Validation:

The solution has been successful! One minor inconvenience encountered was dealing with escaped characters due to the compilation process before rendering as HTML in the browser. Therefore, slight adjustments may be necessary, and additional obstacles could emerge.

I believe this plugin offers a potential fix to your initial query.

Plugin Reference:

https://github.com/smackjax/html-webpack-inject-string-plugin

If you encounter any issues or if this solution does not meet your requirements, feel free to reach out!

Answer №7

You might consider utilizing the html-webpack-plugin/template-option along with the raw-loader.

By the way, make sure to use the default property if you are receiving a [object Module] result.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <%= require('!!raw-loader!./common/file1').default %>
    <title>Document</title>
</head>
<body>
    <%= require('!!raw-loader!./common/file2').default %>
</body>
</html>

Answer №8

configure your settings like so.

instructed template: set the root path of your HTML file

new HtmlWebpackPlugin({
    title: "chucknorris-app",
    template: "./src/template.html",
}),

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Guide to smoothly scroll to the top of an element containing collapsible panels using Jquery

I've encountered an issue with a series of divs that are set to toggle the state of a collapsible div upon clicking (similar to an accordion widget). The functionality works as intended, but I'm facing a challenge - I want the page to scroll to t ...

Tips for managing pagination in a Single Page Application

Within my Single Page Application (built with Javascript and AngularJs), I have an items list that is paginated, displaying 10 items per page. To ensure that the user's current pagination remains intact even when navigating to other pages, I store th ...

Conceal the div element five seconds after the registration process is completed

Is it possible to automatically hide a div 5 seconds after a user registers? Using the timestamp in PHP for the user's registration, there may be a way to achieve this with jQuery, but it's not certain. I found a script online that successfully ...

Tricks for displaying a dynamic object tooltip in Three.js

Can anyone help me figure out how to create a HUD hint effect for a moving object? I envision something like this: An asteroid is floating through space, and when I click on it, a hint box with information pops up. I've been playing around with thi ...

When using the jQuery datepicker with the minDate set to 0, any past dates entered in the text box will be automatically reset to the current date

How can I prevent users from selecting past dates in a jQuery datepicker, while keeping any existing past dates displayed as is? My current code looks like this: $("#t1").datepicker({ minDate:0 }); And the text box code is, <input type="t1" va ...

Stop automated web crawlers from crawling through JavaScript links

To enable remote jQuery templating, I have embedded some links in JavaScript. For example: <script type="text/javascript"> var catalog = {}; catalog['key1'] = 'somepath/template_1.html'; catalog['key2'] = 'anotherp ...

The error message "gulp error - _.flattenDeep is not a function when using gulp.watch" is indicating

Whenever I run a watch task, why am I encountering the TypeError: _.flattenDeep is not a function error? Below is the content of my gulpfile.js : var gulp = require('gulp'); var sass = require('gulp-sass'); var sourcemaps = require(&a ...

Only the initial value is being processed in the for loop using Javascript

I am having issues trying to sum the values within a for loop sourced from my Django database using JavaScript. Currently, when I iterate through the loop, only the first value in the array is returned. I need assistance in finding a solution that allows ...

Exploring Angular 2 Beta 8: An Introduction to @Query Usage

My attempt to utilize @Query to fetch data regarding an element in my template has not been successful. I made an effort using the following approach: Referenced here. Here is a snippet of my code, import {Component, Query, QueryList, ElementRef} from &a ...

Issue encountered: Framer Motion animation is not functioning properly on elements that are rendered using the map()

I'm attempting to create an animation similar to this: https://drive.google.com/file/d/1WQCg7j49xd5XfuaYuC2YFQCUU-UXassp/view?usp=sharing Here's the code I have: <motion.div layout className="grid grid-cols-2 md:grid-cols-3 gap-8 py-10&q ...

What is the process for immediately changing the background color of an input field as soon as text is entered?

I am encountering an issue with the code snippet provided below. My goal is to change the background color of an input field as soon as I start typing something into it. The scenario involves 4 input fields where if the submit button is clicked and any f ...

Issues with passing Angular directive attributes to the scope were encountered

I am having an issue with my angular directives where the arguments are not being passed into the scope: app.directive('sectionLeft', function() { return { restrict:'E', scope: { sectionContent: '=', s ...

What could be causing the discrepancy in results between the first and second methods?

Implementing Weather Icons: const getWeatherIcon = (iconParameter) => { const icon = `https://openweathermap.org/img/wn/${iconParameter}@2x.png` return <img src={icon} alt={iconParameter} /> } <div className="weathericon"> ...

Enhance your web forms with jQuery Chosen's automatic formatting feature

Having trouble adding a feature to my multi-select input box using jQuery Chosen. The current functionality allows users to enter custom values not in the list. The new feature should automatically format numbers entered by users, for example: User input ...

Highcharts JS encountered an error: x[(intermediate value)(intermediate value)(intermediate value)] is not a valid constructor

I'm in the process of creating a bar chart by fetching options from an ajax response. However, I encountered an error when passing the object to the highcharts constructor. Uncaught TypeError: x[(intermediate value)(intermediate value)(intermediate v ...

Having difficulty populating the token in the h-captcha-response innerHTML and g-recaptcha-response innerHTML

I am attempting to use 2captcha along with Selenium and Python to bypass an Hcaptcha. After receiving my 2captcha token, I attempt to input it into the textareas labeled 'h-captcha-response' and 'g-captcha-response'. However, this app ...

What is the process for transferring ng-model values to a table in Angular?

My goal is to populate a table with JSON data using ng-repeat by clicking a button. I need to input either a first name or last name in order to display the results in the table. Is this the correct JavaScript function for achieving this? JavaScript Funct ...

The 'Required' attribute in HTML is malfunctioning

The 'required' attribute is not functioning properly when I try to submit the form. I've searched online for a solution, but none of them have resolved my problem. Take a look at the code snippet below - I've used the required attribute ...

The incorrect value of variable V is being passed to the doSomethingWithData(v) function. This could lead to unexpected results

const Greetings = () => { const [num, setNum] = React.useState(1); React.useEffect(() => { setTimeout(async () => { await setNum(2); processData(num) }, 3000) }, []); retur ...

Error: ng-messages syntax issue with the field parameter

Encountering the following error: Syntax Error: Token '{' invalid key at column 2 of the expression [{{field}}.$error] starting at [{field}}.$error]. when attempting to execute the code below (form-field.html) <div class='row form-grou ...