I'm working on a Chrome extension that injects some UI react components into a page.
The UI components come from react-mdl. Using them requires me to include a css file in the top of my project.
Unfortunately, once the css is injected into the page, the entire page's font is changed.
Is there a way to limit the scope of the css used by react-mdl such that it doesn't affect the page into which I'm injecting?
Just posting this for posterity as accepted answer deserves credit, but if anyone finds themselves in a similar predicament, here is a snippet of the code that worked for me:
// my injected code
window.addEventListener('load', () => {
const injectDiv = document.createElement('div')
const shadowRoot = injectDiv.attachShadow({ mode: 'open' })
// note inline use of webpack raw-loader, so that the css
// file gets inserted as raw text, instead of attached to <head>
// as with the webpack style-loader
shadowRoot.innerHTML = // just using template string
`
<style>${require('raw-loader!app/styles/extension-material.css')}</style>
<div id='shadowReactRoot' />
`
document.body.appendChild(injectDiv)
ReactDOM.render(
<App />,
// note you have to start your query in the shadow DOM
// in order to find your root
shadowRoot.querySelector('#shadowReactRoot')
)
})
Then, sure enough:
I think you should use the Shadow DOM API. It is good practice for those cases when you just need to append your UI component to a webpage.
https://developers.google.com/web/fundamentals/getting-started/primers/shadowdom
As mentioned in this other SO post, <link> tag is also supported, so one can simply do as follows:
const injectedDiv = document.createElement('div');
const shadowRoot = injectedDiv.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `\
<link rel="stylesheet" type="text/css" href="${chrome.extension.getURL("bootstrap.min.css")}"></link>\
<link rel="stylesheet" type="text/css" href="${chrome.extension.getURL("whatever.css")}"></link>\
`;
document.body.appendChild(injectedDiv);
Notes:
Using chrome.extension.getURL is required for getting an extension's local resource url, see e.g. in this answer.
The linked .css resources must be declared under the web_accessible_resources property in your manifest.json (otherwise, you'll get this error)
Related
I would like to add in a non-vue application a custom element.
For that, I've created a classical SFC :
//test.ce.vue
<template>
<div class="text-primary">Test</div>
</template>
<script>
export default {
name: 'test',
};
</script>
<style>
.text-primary {
color: red;
}
</style>
And then a main script :
//app.js
import Test from 'test.ce.vue';
const testElement = defineCustomElement(Test);
customElements.define('test-element', testElement);
document.body.appendChild(document.createElement('test-element'));
Everything is running normally with the creation of a shadow dom element :
<test-component>
#shadow-root (open)
<style>
.text-primary {
color: red;
}
</style>
<div class="text-primary">Test</div>
</test-component>
I would like to avoid to redefine .text-primary class in the component as this class is already defined in the main css file. I also don't need to define specific classes for this component only, so in other terms, I would like to remove the shadow dom like a classical custom element will do.
So basically, render this :
<test-component>
<div class="text-primary">Test</div>
</test-component>
Is there's any option to define in vue that permit that ?
Older question, but in case someone still needs a solution for this...
there is currently no way to tell Vue not to use the shadow-dom. In Vue 2 there was a official package for creating web-components without shadow-root. And there is a community port for Vue 3 of that:
https://www.npmjs.com/package/vue3-webcomponent-wrapper
It was only meant to help people who have just migrated from Vue 2 to keep there application working. It was never intended to replace the official solution and should only be used until the official package can handle Vue 3.
Unfortunately that never happend.
The community port still works, but the package does not contain any source code, so it is a bit scary to use.
I came up with another solution for our project. Using defineCustomElement on a more complex vue component wich is composed by a bunch of nested components reveals another problem. The css of the child components wont be copied to shadow root. So only the css of the root component will work.
You can find the related issue and a workaround with full example here:
https://github.com/vuejs/core/issues/4662#issuecomment-1116001438
It basically grabs the css from the head and appends it to the shadow root.
You just have to extend it to also copy your main.css, like
<template>
<div id="app" ref="injectionElementRef">
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App" />
</div>
</template>
<script lang="ts">
import {defineComponent} from 'vue';
import HelloWorld from './components/HelloWorld.vue';
export default defineComponent({
name: 'App',
components: {
HelloWorld
},
mounted() {
const el = this.$refs.injectionElementRef as HTMLElement
const root = el.getRootNode()
const linkTag = document.getElementById('main-css-id')
root.insertBefore(linkTag.cloneNode(), el)
}
});
</script>
The downside of this method is, there is a short flicker because the css is applied after mount. You could show an empty element till css is applied to work around that.
You are using Vue as a Tool to create Web Components, but why use a Tool over Native Technology?
Tools are not better; Tools are only faster in performing a task.
And in your case the Tool does something you do not want it to do.
Using native Web Components Technology, all you need is:
<style>
.text-primary {
color: red;
}
</style>
<test-component></test-component>
<script>
customElements.define("test-component", class extends HTMLElement {
connectedCallback() {
this.innerHTML = `<div class="text-primary">Test</div>`;
}
})
</script>
<template>
<div class="container">
<head>
<link rel="stylesheet" href="~assets/css/style-light.css" />
<link rel="stylesheet" href="~assets/css/login-light.css" />
</head>
</div>
</template>
Importing css like above results in this error
vue.runtime.esm.js:5717 GET http://localhost:3000/~assets/css/login-light.css net::ERR_ABORTED 404 (Not Found)
Is there really no other way loading css other than putting the whole css in the template?
The first thing you need to know is, you can't declare a html head inside any place, neither in yours tamplate, neither in yours components, neither in yours pages, neither in nowhere.
Keep in mind that you can't use a html tags for this, you will use a json schema.
take a look https://nuxtjs.org/guide/configuration for more detailed explanations.
Now about you doubt if you want to import the CSS as globally, the correct place is inside your nuxt.config.js, inside this file, you have a property called head, and inside the head we will configure all the imports.
So, inside nuxt.config.js find your head session, and then create new property called css, some thing like this:
head: {
css: [
'~/assets/style/app.styl',
'~/assets/style/main.css'
],
}
...
Another way, is import your css directly inside your component, for this you can do some thing like this:
<style scoped>
#import '~/assets/style/main.css';
</style>
OR
<style scoped src="#/assets/styles/mystyles.css">
</style>
In Nuxt, you will need a CSS loader instaled in your application too, so have sure you had intalled a "stylus" and "stylus-loader" in your app.
try to impot your css files in script like this :
<script>
import "#/assets/css/style-light.css";
import "#/assets/css/login-light.css";
///
</script>
EDIT: changed ~ to #
You could bring your files in using the head method like so :
head () {
return {
link: [
{ rel: 'stylesheet', href: '/style-light.css' },
{ rel: 'stylesheet', href: '/login-light.css' }
]
}
}
You should also move these css files into the static folder. See this discussion on the Vue forum https://forum.vuejs.org/t/nuxt-import-css-file-and-js-file/42498
I'm preparing a starter for react from scratch, here is the code: https://github.com/antondc/react-starter
I managed to set up bundling for client and server, with css modules and less, and now I'm with server side rendering. I'm doing that with a js template:
// src/server/views/index.ejs
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>INDEX.EJS</title>
<link rel="stylesheet" type="text/css" href="assets/index.css">
</head>
<body>
<div id="app"></div>
<script src="/assets/bundle.js"></script>
</body>
</html>
As you see, the link to the css file is harcoded there. But in my Webpack configuration I have this file name hashed, because I want to prevent caching from browsers when I update the code on development.
I am wondering how can I link the css file there. Now in the template I have href="assets/index.css, but the css file is in /dist/assets/d47e.css.
It would be great if would be possible to do something like href="assets/*.css, but is not possible, so what is the common approach for a problem like this one?
Thanks!
It depends.
Step 1: Get the current asset name
To get the current name of the generated webpack css/js files, you can use the assets-webpack-plugin. This will (with default config) generate an assets.json file in your output folder with essentially this structure:
{
"bundle_name": {
"asset_kind": "/public/path/to/asset"
}
}
Step 2a: Your html is rendered from a template (pug/jade/what ever)
// in your render code
const assets = require('<webpack-output-folder>/assets.json');
// ...
res.render('template', {
scripts: [{src: `${WEBPACK_PUBLIC_PATH}/${assets.myEntryPointName.js}` }],
links: [{href: `${WEBPACK_PUBLIC_PATH}/${assets.myEntryPointName.css}` rel: 'stylesheet' }],
});
// in your template (example for pug)
// ...
each link in links
link(rel=link.rel href=link.href)
// ...
each script in scripts
script(src=script.src)
// ...
Step 2b: Your html is static
You need to update the html (using a script) with the information from the asset.json file. This script needs to be run after webpack. Something like
const assets = require('<webpack-output-folder>/assets.json');
const fs = require('fs');
const css = /assets\/[a-z0-9]*\.css/i;
const js = /assets\/[a-z0-9]*\.js/i;
fs.readFile('<yourhtml>.html', (err, data) => {
// ... (error handling)
const updatedCss = data.replace(css, assets.myEntryPointName.css);
const updatedJs = updatedCss.replace(js, assets.myEntryPointName.js);
fs.writeFile('<yourhtml>.html', updated, (err) => {
// ... (error handling)
});
});
You can use HTMLWebpackPlugin to generate an HTML file that will have your JS and CSS output inserted.
I'm new to NodeJS and I'm using Express to serve my pug files/view. Furthermore I'm using "express-sass-middleware" to compile and serve the scss/css files. Everything works very well but unfortunately, the CSS are not applied.
My app.js files looks like:
var express = require('express');
var sassMiddleware = require('express-sass-middleware');
var app = express();
app.set('view engine', 'pug');
app.get('/css/bootstrap.css', sassMiddleware({
file: 'css/bootstrap.scss', // the location of the entry point,
// this can also be a directory
precompile: true, // should it be compiled on server start
// or deferred to the first request
// - defaults to false
}));
app.get('/', function (req, res) {
res.render('index', {
varTitle: 'Hello World'
});
});
app.listen(3000, function() {
console.log('Example app listening on port 3000!');
});
And my simple css file looks like:
// $icon-font-path: /3rdparty/fonts;
// #import 'bootstrap/bootstrap';
// #import './node_modules/bootstrap-sass/assets/stylesheets/bootstrap/variables';
body
{
background-color: green;
font-size: 100px;
}
My index.pug file is:
doctype html
html(lang='en')
head
title= varTitle
link(ref='stylesheet' type='text/css' href='/css/bootstrap.css')
body
h1= varTitle
Now, when I start my webserver using "node app.js", accessing http://localhost:3000, I see "Hello World" but unfortunately the body background isn't green and the text is also not 100px. That means that the css file is not applied. But when I access http://localhost:3000/css/bootstrap.css, I see the valid, css file.
Anyone know what I'm missing here? I'm a bit confused that I see the CSS source when accessing it directly but the browser doesn't apply the css styling. I already tried different browsers without any success. None of them applying the css file.
You have typing error in index.pug file for loading css file. You had mentioned ref whereas it should be rel.
link(rel='stylesheet' type='text/css' href='/css/bootstrap.css')
Happy to help you.
you don't seem to be serving the static files from your nodejs server code. You have to add your css dir in order to allow access from your html code:
app.use('/static', express.static('public'))
Now, you can load the files that are in the public directory from the /static path prefix.
http://localhost:3000/static/images/kitten.jpg
http://localhost:3000/static/css/style.css
http://localhost:3000/static/js/app.js
http://localhost:3000/static/images/bg.png
http://localhost:3000/static/hello.html
I am currently working on a project that requires me to have computed styles send to the browser via JSDOM. I am currently looking for a way to inject some basic CSS into JSDOM so that it could compute the correct inline style (Yes I know that's bad).
From what I have found out I can use JSDOM Level 2, but from there I can't find any documentation on how to inject the styles.
This is what I have so far;
var document = jsdom.jsdom('<!DOCTYPE html><html><head></head><body id="abody" ></body></html>', jsdom.level(2, 'style'), {
features : {
FetchExternalResources : ['script', 'css'],
QuerySelector : true
}
});
I have been inserting the css into the head tag but to no avail. And I know I could be doing the above code wrong as well.
Any help would be great.
Well, this is going to sounds kinda dumb but this is what I did:
var path = require('path');
var fs = require('fs');
var mainCss = fs.readFileSync(path.normalize(__dirname + "web_main.css"), 'utf8');
var document = jsdom.jsdom('<!DOCTYPE html><html><meta http-equiv="content-type" content="text/html; charset=utf-8"><head></head><body id="abody" ></body></html>', jsdom.level(3, 'index'), {
features : {
FetchExternalResources : ['script', 'css'],
QuerySelector : true
}
});
var window = document.createWindow();
var head = document.getElementsByTagName('head')[0];
style = document.createElement("style");
style.type = 'text/css';
style.innerHTML = mainCss;
head.appendChild(style);
So basically all I changed was moving the level to 3 index, and instead of directly having it in the starting html, I appended it afterwards.
Its pretty simple and I hope it helps someone else out.
solution-preface
Seems Op's Answer_post's solution is:
directly copy and paste the style into the <style>.
If your question is:
How to let Jsdom load (/ why Jsdom doesnt load) external css stylesheet link in an html file? (eg: <link href="main_test.css" rel="stylesheet" />)
Following may help:
solution
Have an html file, say main_test.html.
main_test.html contains <link href="main_test.css" rel="stylesheet" /> -- which is the stylesheet you want to have
#note:
if main_test.html doesnt have it
& you want to add this at run time, maybe you can::
append this line as a string to html file, &
parse the html String (so, not fromFile()) into Jsdom
Load the html file into Jsdom, with the use of resources: 'usable'
dom = await JSDOM.fromFile(pathStr_htmlFile, {
contentType: 'text/html; charset="utf-8"',
resources: 'usable',
});
Make sure you wait until the css stylesheet is fully loaded, before accessing them.
reference
[Can't load external css] https://github.com/jsdom/jsdom/issues/1927