What I'm trying to do:
Render an svg icon.
My results: Although svg and img property append to DOM, HTML does not render icon and I get no webpack or browser error.
My component file structure/ approach
I import SVG into sidebar.js
sidebar.js
import './sidebar.scss';
import photo from './usrPic.jpg';
import '../base/SVG/twitter.svg';
import '../base/sprite.svg';
export default class Sidebar {
render() {
const img = document.createElement('img');
img.alt = 'image';
img.width = 200;
img.src = photo;
img.classList = 'sidebar__user-photo';
// const svg = document.createElement('svg');
const use = document.createElement('use');
use.setAttribute('href', `../base/SVG/twitter.svg#icon-twitter.svg`);
const html = `<div class="sidebar-box">
<div class="sidebar-header">
<span class="sidebar-title__main">Something</span>
<span class="sidebar-title__sub">Sub</span>
<div class="Icon-box">
<svg class="twitter__icon">
</svg>
</div>
</div>
</div>`;
const contentBox = document.querySelector('.content');
contentBox.insertAdjacentHTML('beforeend', html);
const sidebarHeader = document.querySelector('.sidebar-header');
sidebarHeader.insertAdjacentElement("afterbegin", img);
const twitterIcon = document.querySelector('.twitter__icon');
twitterIcon.insertAdjacentElement("afterbegin", use);
}
}
webpack.dev.config
...
{
test: /\.svg$/,
use: 'svg-sprite-loader'
},
{
test: /\.(png|svg|jpg|gif)$/,
use: 'file-loader',
},
html
HTML code
What I have tried thus far:
swapping sprite.svg with direct icon.svg in <use href="..." />
I swapped <use href="..." /> . with <use xlink:href="..." />
I swapped the element with "img" property.
Applied a 40px to width & height on svg element
Given all this, I do not get an error yet the element is appended to
the DOM, but no icon.
Any assistance is greatly appreciated.
1st UPDATE:
sidebar.js
Was modified to this:
...
var SVG_NS = 'http://www.w3.org/2000/svg';
var XLink_NS = 'http://www.w3.org/1999/xlink';
const use = document.createElementNS(SVG_NS, 'image');
use.setAttributeNS(null, 'width', '100');
use.setAttributeNS(null, 'height', '100');
use.setAttributeNS(XLink_NS, 'xlink:href', '../base/sprite.svg#icon-twitter' );
const twitterIcon = document.querySelector('.twitter__icon');
twitterIcon.insertAdjacentElement("afterbegin", use);
This produced a Webpack error
ERROR in ./src/components/sidebar/sidebar.js
Module not found: Error: Can't resolve 'svg-sprite-loader' in '/Users/dev/Developer/projects/dcbio'
# ./src/components/sidebar/sidebar.js 10:0-28
# ./src/index.js
Commenting out ../sprite.svg file took care of the said error. Yet, the browser now produces a 404 error that's driving me crazy.
Browser Error
Elements Rendered
HLTM Elements
Recommendations is appreciated.
Thanks
2nd UPDATE
After reading through webpack's doc and its git, I made changes to my webpack.config to this:
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const SpriteLoaderPlugin = require('svg-sprite-loader/plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './dist'),
publicPath: '',
},
mode: 'development',
devServer: {
contentBase: path.resolve(__dirname, './dist'),
index: 'index.html',
port: 9000
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader', 'css-loader'
]
},
{
test: /\.scss$/i,
use: [
'style-loader', 'css-loader', 'sass-loader'
],
},
{
test: /\.(png|jpg|gif)$/,
use: [ 'file-loader'],
},
{
test: /\.svg$/,
use: [
{
loader: 'svg-sprite-loader',
options: {
extract: true,
publicPath: '/src'
}
},
'svg-inline-loader',
'svgo-loader'
]
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [ '#babel/env' ],
plugins: [ 'transform-class-properties' ]
}
}
},
{
test: /\.hbs$/,
use: [
'handlebars-loader'
]
}
],
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: 'src/index.hbs',
title: 'Hello world',
description: 'Some description'
}),
new SpriteLoaderPlugin({
plainSprite: true,
spriteAttrs: {
id: 'icon-twitter'
}
})
],
};
I still get a browser error - see pic
Browser Error
DOM returns this:
enter image description here
FINAL Update and Answer:
After extensive research and edits, I realized within my base.js file the property inner.HTML element contributed to the <svg> icon not loading.
In addition, I modified the following files mentioned on my original post to this:
sidebar.js
...
import symbol from '../../images/icons_sprite.svg';
...
html = `<svg class="icon-fb">
<use href="#icons_sprite_icon-facebook"></use>
</svg>`;
const iconBox = document.querySelector('.Icon-box');
iconBox.insertAdjacentHTML("afterbegin", html);
Notice I'm only using href and NOT xlink:href. According to MDN documentation xlink:href is in the deprecation bucket, so I'm choosing to keep with their recommended approach - Just use href.
webpack.dev.config
...
{
test: /\.svg$/i,
include: /\.*_sprite\.svg/,
use:[
{
loader: 'svg-sprite-loader',
options: {
publicPath: './src',
}
},
{
loader: 'svgo-loader',
options: {
plugins: [
{ cleanupIDs: false },
]
}
},
..
Problem solved...
Here are the links that contributed to a resolution. I hope this solution provides help to someone in the future.
MDN
https://developer.mozilla.org/en-US/docs/Web/SVG
Webpack-svg
https://github.com/JetBrains/svg-sprite-loader
https://github.com/svg/svgo
https://github.com/vp-online-courses/webpack-tutorial/compare/svg-sprites-begin...svg-sprites-end
Related
I recently upgraded to Webpack 5 and my html-loader no longer loads svg files and inlines them.
Here's my svg rule in webpack
{
test: /\.svg$/,
use: [
{
loader: 'html-loader',
options: {
minimize: true,
},
},
],
},
No matter how I try to import it, it seems to just create a file and not give me a string of HTML.
import mySvg from "../path/to/my.svg"
let mySvg = require("../path/to/my.svg").default;
// output = "/build/path/my.svg"
// output I want = "<svg>...."
It used to not give me several build files instead it inlined them in my JS.
Help would be appreciated.
svg-inline-loader can achieve the same (confirmed to work).
I have listed other options for loading SVGs at https://survivejs.com/webpack/loading/images/#loading-svgs.
I use posthtml and posthtml-inline-svg plugin.
const postHtml = require('posthtml');
const postHtmlInlineSvg = require('posthtml-inline-svg');
{
test: /\.html$/,
use: {
loader: 'html-loader',
options: {
esModule: false,
preprocessor: async (content, loaderContext) => {
try {
return await postHtml(postHtmlInlineSvg({ cwd: loaderContext.context, tag: 'icon', attr: 'src' }),).process(content)).html;
} catch (error) {
loaderContext.emitError(error);
return content;
}
},
},
},
},
If you're using HtmlWebpackPlugin, the HtmlWebpackInlineSVGPlugin can be used to inline svgs.
Here are the relevant parts of the webpack config to achieve the same:
{
// ...
module: {
rules: [
{
test: /\.html/,
loader: "html-loader",
},
// ...
],
},
plugins: [
new HtmlWebpackPlugin(),
new HtmlWebpackInlineSVGPlugin({
inlineAll: true,
}),
// ...
]
}
I have a problem, Webpack generates duplicate images, while one of the duplicates is broken.
I have an image image, and two duplicates are generated from it, a working one: image, and a non-working one: image. I 'm in CSS for a class .logo I'm hanging the background-image style: image. After compiling the code, it inserts the path to the non-working image: image, and I don't understand what's the matter :(
Please help me fix it, Thanks in advance!!!
My webpack.config.js:
const path = require('path')
const HTMLWebpackPlugin = require('html-webpack-plugin')
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
module.exports = {
context: path.resolve(__dirname, 'src'),
mode: 'development',
entry: {
main: './index.js',
analytics: './analytics.js'
},
output: {
filename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HTMLWebpackPlugin({
title: 'Webpack Tenzo',
template: './index.html'
}),
new CleanWebpackPlugin()
],
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(png|jpg|svg|gif)$/,
use: ['file-loader']
}
]
}
}
I solved the problem. The point was that it was necessary to use "assets" instead of leaders such as "file-loader".
I am trying to do a dynamic import so that my classes are pulled by information from json. In json, I only have classes specified that I need to get inside the script. I make paths from strings to json then put them in an array.I make an import and put the values into an array. But as a result, I only get the value of the first import, the rest is undefined. Tell me how to correctly perform dynamic imports inside a loop, or is there a fundamentally different approach altogether?
My index.js:
var popupsConfig = myConfig.popups
var popups = []
async function getPopups(popupsConfig) {
for (let i = 0; i < popupsConfig.length; i++) {
let obj = await
import (`../src/assets/popups/${myConfig.popups[i].popupType}`)
popups.push(obj.default)
}
}
getPopups(popupsConfig)
My webpack.config:
const webpack = require("webpack")
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
context: path.resolve(__dirname, '../src'),
mode: 'development',
devtool: "eval-source-map",
entry: {
entry: ['babel-polyfill', '../dev/index.js'],
},
devServer: {
port: 8080
},
experiments: {
topLevelAwait: true
},
resolve: {
alias: {
'#dev': path.resolve(__dirname, '../src/')
}
},
plugins: [
new HtmlWebpackPlugin({
template: '../dev/index.html'
}),
new CleanWebpackPlugin(),
new webpack.DefinePlugin({
CANVAS_RENDERER: JSON.stringify(true),
WEBGL_RENDERER: JSON.stringify(true)
})
],
module: {
rules: [{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(png|jpeg|svg|gif)$/,
use: ['file-loader']
}, {
test: [/\.vert$/, /\.frag$/],
use: "raw-loader"
},
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
}
]
}
}
My .babelrc:
{
"plugins": ["dynamic-import-webpack", "#babel/plugin-syntax-dynamic-import"]
}
enter image description here
I'm having any issue loading images from a different folder than where my index.html file is located. Below is the message I am getting from the console.
GET http://localhost:3000/company-logo.jpg 404 (Not Found)
Here is my file directory
webpack.config.js
const currentTask = process.env.npm_lifecycle_event
const path = require('path')
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const fse = require('fs-extra')
const postCSSPlugins = [
require('postcss-import'),
require('postcss-simple-vars'),
require('postcss-nested'),
require('autoprefixer')
]
class RunAfterComile {
apply(compiler) {
compiler.hooks.done.tap('Copy images', function() {
fse.copySync('./public/images', './dist/public/images')
})
}
}
let cssConfig = {
test: /\.css$/i,
use: ['css-loader?url=false', {loader: 'postcss-loader', options: {plugins: postCSSPlugins}}]
}
let pages = fse.readdirSync('./views').filter(function(file) {
return file.endsWith('.html')
}).map(function(page) {
return new HtmlWebpackPlugin({
filename: page,
template: `./views/${page}`
})
})
let config = {
entry: './public/scripts/App.js',
plugins: pages,
module: {
rules: [
cssConfig
]
}
}
if (currentTask == 'dev') {
cssConfig.use.unshift('style-loader')
config.output = {
filename: 'bundled.js',
path: path.resolve(__dirname, 'public')
}
config.devServer = {
before: function(app, server) {
server._watch('./views/**/*.html')
},
contentBase: path.join(__dirname, './views'),
hot: true,
port: 3000
}
config.mode = 'development'
}
if (currentTask == 'build') {
config.module.rules.push({
test: /\.js$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
})
cssConfig.use.unshift(MiniCssExtractPlugin.loader)
postCSSPlugins.push(require('cssnano'))
config.output = {
filename: '[name].[chunkhash].js',
chunkFilename: '[name].[chunkhash].js',
path: path.resolve(__dirname, 'dist')
}
config.mode = 'production'
config.optimization = {
splitChunks: {chunks: 'all'}
}
config.plugins.push(
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({filename: 'styles.[chunkhash].css'}),
new RunAfterComile()
)
}
module.exports = config
The css works perfectly fine. I seem to only be having problems with images.
I tried the following file paths but they didn't work. I think I am missing something.
index.html
<img src="./company-logo.jpg" alt="dfgadfg">
<img src="../public/images/company-logo.jpg" alt="dfgadfg">
<img src="./images/company-logo.jpg" alt="dfgadfg">
any help would be appreciated. Thank you very much.
Try use
module: {
rules: [
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader',
],
},
],
},
You might require file-loder to get the images correctly served
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
loader: 'file-loader',
options: {
outputPath: 'images' // Chage this like 'public/images' or any other relative path to the root
}
},
...
]
Try to inspect and check how the images are loading in Source tab of Developer Tools, change the outputPath accordingly.
Update
If images are still not loading to Source tab of browser, try importing it in your App.js (Entry point file)
....
import "../public/images/company-logo.jpg";
...
I am new to Webpack and not so experienced in JavaScript either. I'm trying to set up a new project for the JS text editor. However, I noticed that after setting up the first addEventListener for the #btn button, the event gets fired twice.
I don't think that this behaviour is related to the event itself, since I have created three console.log functions. One is outside the eventListener, two remaining is inside. All of the three functions get console logged twice in the browsers console, which leads me to think that this might be related to Webpack config.
If anyone has a clue or and advice, please help.
HTML:
<main class="main__content">
<article class="text__editor-wrapper">
<h1 class=" ">Text Editor</h1>
<div class="toolbar">
<button id="btn" class="toolbar__option"><span class="fas fa-bold fa-2x"></span></button>
</div>
<div class="text__editor" contenteditable="true"></div>
</article>
</main>
index.js:
import "./scss/style.scss";
import '#fortawesome/fontawesome-free/css/all.css';
import '#fortawesome/fontawesome-free/js/all.js';
const btn = document.querySelector("#btn");
function format(command, value) {
document.execCommand(command, false, value);
}
console.log("Outside")
btn.addEventListener('click', function(e) {
e.preventDefault();
console.log("inside");
console.log(e.target);
format('bold');
});
webpack config file:
const path = require("path"),
HtmlWebpackPlugin = require("html-webpack-plugin"),
BrowserSyncPlugin = require("browser-sync-webpack-plugin"),
MiniCssExtractPlugin = require("mini-css-extract-plugin"),
UglifyJsPlugin = require("uglifyjs-webpack-plugin");
module.exports = {
entry: "./src/index.js",
output: {
filename: "main.js",
path: path.resolve(__dirname, "dist"),
},
optimization: {
minimizer: [new UglifyJsPlugin()]
},
devServer: {
contentBase: path.join(__dirname, "dist"),
port: 900
},
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["#babel/preset-env"]
}
}
},
{
test: /\.(svg|eot|woff|woff2|ttf)$/,
use: [{
loader: "file-loader"
}]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [{
loader: "url-loader"
}]
},
{
test: /\.(sass|scss|css)$/,
use: [
{ loader: "style-loader" },
{ loader: "css-loader" },
{ loader: "sass-loader" }
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
filename: "./index.html"
}),
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
})
]
}
You haven't given us your full HTML file, but I suspect this is where the problem lies.
Check your final (post-Webpack) index.html file - I suspect you're importing the main.js bundle in a <script> tag and also including the bundled JS.
This is because you're using Webpack to bundle your HTML (the HtmlWebpackPlugin plugin) and also creating a bundle (main.js) which you're importing somewhere in your HTML.