At the begining I want to say that I'm aware that the question is similar to:
How to let imported css font / icons have effects on elements in the shadow dom?
It's not the case and it does not help.
Issue:
I've recently decided to use the ShadowDOM to encapsulate the styles in my project. At the very begining it I thought it worked as expected but I've noticed that some of the icons coming from the external CSS files were gone.
It's important to notice that those styles are external and possibilities of making changes are limited.
I've prepared the code to demonstrate the issue (look at the snippet below).
Everything seems to be working fine except the #font-face
As You can see, HTML containing the external CSS file with icons works as expected outside the ShadowDOM. I'd like to use it inside the shadowDOM as well.
How can I achieve that ?
NOTE: If you check the dev tools, there is a problem with the CSS path in network tab but it's the SO snippet issue. If You run the snippet locally, everything is ok in network.
const body = document.getElementsByTagName('body')[0];
const wrapper = document.querySelector('.wrapper')
const handleAddToShadowClick = (param) => {
const host = document.querySelector('#shadowHost');
if(param === 'insideShadow') {
const shadowRoot = host.attachShadow({mode: 'open'});
shadowRoot.innerHTML = firstComponent
} else {
const shadowRoot = host;
wrapper !== null ? body.removeChild(wrapper): ''
shadowRoot.innerHTML = firstComponent
}
}
const firstComponent = `
<div class="wrapper">
<div class="icon login">Bla bla</div>
<div style="font-family: testFont;">Sample String od text</div>
<link rel="stylesheet" href="./style.css" />
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.13.0/css/all.css" integrity="sha384-Bfad6CLCknfcloXFOyFnlgtENryhrpZCe29RTifKEixXQZ38WheV+i/6YWSzkz3V" crossorigin="anonymous">
</div>
`
.wrapper {
font-family: agGridBalham;
background-color: aquamarine;
color: black;
}
.balham:before {
content: "\F11F";
}
.login::before {
font-family: "Font Awesome 5 Free";
font-weight: 900;
content: "\f007";
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
Refresh the page after each button click
</p>
<button onclick="handleAddToShadowClick('outside')">Add outside shadow</button>
<button onclick="handleAddToShadowClick('insideShadow')">Add inside shadow</button>
<div>
<div id="shadowHost"> </div>
</div>
<script src="./script.js"></script>
</body>
</html>
The behavior mentioned in the question is a bug which has been there in chromium(It was also there in Gecko too not sure if it has been fixed or not).
Here is the link for the bug reported at chromium related
to this issue, which still not resolved by them. At, present i feel
this is the only workaround which will work.
The issue is mainly related to scoping of #font-face. Currently you cannot use the fonts awesome fonts when they are only included inside the shadow DOM. So inorder to use the fonts the fonts css must be present inside both light DOM and shadow DOM. So you need to import the font css both inside the shadow dom and the light dom.
Here is a Working Plunker which solves your problem.
<html>
<head>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.13.0/css/all.css" integrity="sha384-Bfad6CLCknfcloXFOyFnlgtENryhrpZCe29RTifKEixXQZ38WheV+i/6YWSzkz3V" crossorigin="anonymous">
</head>
<body>
Refresh the page after each button click
</p>
<button onclick="handleAddToShadowClick('outside')">Add outside shadow</button>
<button onclick="handleAddToShadowClick('insideShadow')">Add inside shadow</button>
<div>
<div id="shadowHost"> </div>
</div>
<script src="lib/script.js"></script>
</body>
</html>
As you can see the plunker above, we have included the font-awesome css both in light DOM and Shadow Dom. And it is working fine as intended.
Basically I'm trying to get a plugin to work which opens up a webpage as an overlay rather than taking you away from the current page you are on. I cannot get the demo to work properly. It is demonstrated here:
http://jquerytools.github.io/demos/overlay/external.html
with a working demo here:
http://jquerytools.github.io/demos/overlay/external.htm
I can get this working perfectly for other pages which I host (relative paths), but I would like the overlay to open, for example, www.google.com - when I try this the overlay is simply blank. I am at a loss!
<!DOCTYPE html>
<html>
<!--
This is a jQuery Tools standalone demo. Feel free to copy/paste.
http://flowplayer.org/tools/demos/
Do *not* reference CSS files and images from flowplayer.org when in
production Enjoy!
-->
<head>
<title>jQuery Tools standalone demo</title>
<!-- include the Tools -->
<script src="http://cdn.jquerytools.org/1.2.6/full/jquery.tools.min.js"></script>
<!-- standalone page styling (can be removed) -->
<link rel="shortcut icon" href="/media/img/favicon.ico">
<link rel="stylesheet" type="text/css"
href="/media/css/standalone.css"/>
<link rel="stylesheet" type="text/css"
href="/media/css/overlay-apple.css"/>
<style>
/* use a semi-transparent image for the overlay */
#overlay {
background-image:url(/media/img/overlay/transparent.png);
color:#efefef;
height:450px;
}
/* container for external content. uses vertical scrollbar, if needed */
div.contentWrap {
height:441px;
overflow-y:auto;
}
</style>
</head>
<body><!-- external page is given in the href attribute (as it should be) -->
<a href="external-content.htm" rel="#overlay" style="text-decoration:none">
<!-- remember that you can use any element inside the trigger -->
<button type="button">Show external page in overlay</button>
</a>
<!-- another link. uses the same overlay -->
<a href="external-content2.htm" rel="#overlay" style="text-decoration:none">
<button type="button">Show another page</button>
</a>
<!-- overlayed element -->
<div class="apple_overlay" id="overlay">
<!-- the external content is loaded inside this tag -->
<div class="contentWrap"></div>
</div>
<!-- make all links with the 'rel' attribute open overlays -->
<script>
$(function() {
// if the function argument is given to overlay,
// it is assumed to be the onBeforeLoad event listener
$("a[rel]").overlay({
mask: 'darkred',
effect: 'apple',
onBeforeLoad: function() {
// grab wrapper element inside content
var wrap = this.getOverlay().find(".contentWrap");
// load the page specified in the trigger
wrap.load(this.getTrigger().attr("href"));
}
});
});
</script>
</body>
</html>
I have a page which has <link> in the header that loads the CSS named light.css. I also have a file named dark.css. I want a button to swap the style of the page all together (there are 40 selectors used in css file and some do not match in two files).
How can I remove reference to light.css with JS and remove all the styles that were applied and then load dark.css and apply all the styles from that? I can't simply reset all of the elements, since some of the styles are applied through different css files and some are dynamically generated by JS. Is there a simple, yet effective way to do that without reloading the page? Vanilla JS is preferable, however I will use jQuery for later processing anyways, so jQ is also fine.
You can include all the stylesheets in the document and then activate/deactivate them as needed.
In my reading of the spec, you should be able to activate an alternate stylesheet by changing its disabled property from true to false, but only Firefox seems to do this correctly.
So I think you have a few options:
Toggle rel=alternate
<link rel="stylesheet" href="main.css">
<link rel="stylesheet alternate" href="light.css" id="light" title="Light">
<link rel="stylesheet alternate" href="dark.css" id="dark" title="Dark">
<script>
function enableStylesheet (node) {
node.rel = 'stylesheet';
}
function disableStylesheet (node) {
node.rel = 'alternate stylesheet';
}
</script>
Set and toggle disabled
<link rel="stylesheet" href="main.css">
<link rel="stylesheet" href="light.css" id="light" class="alternate">
<link rel="stylesheet" href="dark.css" id="dark" class="alternate">
<script>
function enableStylesheet (node) {
node.disabled = false;
}
function disableStylesheet (node) {
node.disabled = true;
}
document
.querySelectorAll('link[rel=stylesheet].alternate')
.forEach(disableStylesheet);
</script>
Toggle media=none
<link rel="stylesheet" href="main.css">
<link rel="stylesheet" href="light.css" media="none" id="light">
<link rel="stylesheet" href="dark.css" media="none" id="dark">
<script>
function enableStylesheet (node) {
node.media = '';
}
function disableStylesheet (node) {
node.media = 'none';
}
</script>
You can select a stylesheet node with getElementById, querySelector, etc.
(Avoid the nonstandard <link disabled>. Setting HTMLLinkElement#disabled is fine though.)
You can create a new link, and replace the old one with the new one. If you put it in a function, you can reuse it wherever it's needed.
The Javascript:
function changeCSS(cssFile, cssLinkIndex) {
var oldlink = document.getElementsByTagName("link").item(cssLinkIndex);
var newlink = document.createElement("link");
newlink.setAttribute("rel", "stylesheet");
newlink.setAttribute("type", "text/css");
newlink.setAttribute("href", cssFile);
document.getElementsByTagName("head").item(cssLinkIndex).replaceChild(newlink, oldlink);
}
The HTML:
<html>
<head>
<title>Changing CSS</title>
<link rel="stylesheet" type="text/css" href="positive.css"/>
</head>
<body>
STYLE 1
STYLE 2
</body>
</html>
For simplicity, I used inline javascript. In production you would want to use unobtrusive event listeners.
If you set an ID on the link element
<link rel="stylesheet" id="stylesheet" href="stylesheet1.css"/>
you can target it with Javascript
document.getElementsByTagName('head')[0].getElementById('stylesheet').href='stylesheet2.css';
or just..
document.getElementById('stylesheet').href='stylesheet2.css';
Here's a more thorough example:
<head>
<script>
function setStyleSheet(url){
var stylesheet = document.getElementById("stylesheet");
stylesheet.setAttribute('href', url);
}
</script>
<link id="stylesheet" rel="stylesheet" type="text/css" href="stylesheet1.css"/>
</head>
<body>
<a onclick="setStyleSheet('stylesheet1.css')" href="#">Style 1</a>
<a onclick="setStyleSheet('stylesheet2.css')" href="#">Style 2</a>
</body>
This question is pretty old but I would suggest an approach which is not mentioned here, in which you will include both the CSS files in the HTML, but the CSS will be like
light.css
/*** light.css ***/
p.main{
color: #222;
}
/*** other light CSS ***/
and dark.css will be like
/*** dark.css ***/
.dark_theme p.main{
color: #fff;
background-color: #222;
}
/*** other dark CSS ***/
basicall every selector in dark.css will be a child of .dark_theme
Then all you need to do is to change the class of body element if someone selects to change the theme of the website.
$("#changetheme").click(function(){
$("body").toggleClass("dark_theme");
});
And now all your elements will have the dark css once the user clicks on #changetheme. This is very easy to do if you are using any kind of CSS preprocessors.
You can also add CSS animations for backgrounds and colors which makes the transition highly smooth.
Using jquery you can definitely swap the css file. Do this on button click.
var cssLink = $('link[href*="light.css"]');
cssLink.replaceWith('<link href="dark.css" type="text/css" rel="stylesheet">');
Or as sam's answer, that works too. Here is the jquery syntax.
$('link[href*="light.css"]').prop('disabled', true);
$('link[href*="dark.css"]').prop('disabled', false);
Using jquery .attr() you can set href of your link tag .i.e
Sample code
$("#yourButtonId").on('click',function(){
$("link").attr(href,yourCssUrl);
});
Maybe I'm thinking too complicated, but since the accepted answer was not working for me I thought I'd share my solution as well.
Story:
What I wanted to do was to include different 'skins' of my page in the head as additional stylesheets that where added to the 'main' style and switch them by pressing a button on the page (no browser settings or stuff).
Problem:
I thought #sam's solution was very elegant but it did not work at all for me. At least part of the problem is that I'm using one main CSS file and just add others on top as 'skins' and thus I had to group the files with the missing 'title' property.
Here is what I came up with.
First add all 'skins' to the head using 'alternate':
<link rel="stylesheet" href="css/main.css" title='main'>
<link rel="stylesheet alternate" href="css/skin1.css" class='style-skin' title=''>
<link rel="stylesheet alternate" href="css/skin2.css" class='style-skin' title=''>
<link rel="stylesheet alternate" href="css/skin3.css" class='style-skin' title=''>
Note that I gave the main CSS file the title='main' and all others have a class='style-skin' and no title.
To switch the skins I'm using jQuery. I leave it up to the purists to find an elegant VanillaJS version:
var activeSkin = 0;
$('#myButton').on('click', function(){
var skins = $('.style-skin');
if (activeSkin > skins.length) activeSkin=0;
skins.each(function(index){
if (index === activeSkin){
$(this).prop('title', 'main');
$(this).prop('disabled', false);
}else{
$(this).prop('title', '');
$(this).prop('disabled', true);
}
});
activeSkin++
});
What it does is it iterates over all available skins, takes the (soon) active one, sets the title to 'main' and activates it. All other skins are disabled and title is removed.
Simply update you Link href attribute to your new css file.
function setStyleSheet(fileName){
document.getElementById("WhatEverYouAssignIdToStyleSheet").setAttribute('href', fileName);
}
I reworked lampe's example, and you can add a class using a selector in this way:
first apply the class to specific selectors in a javascript (repeat as many times you need for specific element selectors (in my HTML):
$("p:nth-of-type(even)").toggleClass("main mainswitch");
Then the html looks like this
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js">
</script>
<script>
$(document).ready(function(){
$("button").click(function(){
$(".mainswitch").toggleClass("main");
});
});
</script>
<style>
.main {
font-size: 120%;
color: red;
}
</style>
</head>
<body>
<div>
<p>This is a paragraph.</p>
<p class="main mainswitch">This is another paragraph.</p>
<p>This is a paragraph.</p>
<p>This is a paragraph.</p>
<p>This is a paragraph.</p>
</div>
<button>Toggle class "main" for p elements</button>
</body>
</html>
If you're using Angular (cause it's not 2013 anymore), you can try the answer/solution/suggestion from here:
How can I change the targeted CSS file on a click event
It did the trick for me.
I'm loading a file into a div with load() function, but after I load the data - the styles for it doesn't want to work.
For example:
index.html:
$("a ").click(
function(e)
{
e.preventDefault();
$("#Display").load('file.html');
$("#Display").show();
});
file.html:
<h1 id="title">Item number #1</h1>
<p id="content">Lorem ipsum like text...</p>
style.css:
#title {
color: red;
}
#content {
color: green;
}
After I click "a" link, the content is loaded to #Display div, then it's perfectly displayed, but the title header is not red and content paragraph is not green.
I believe that's a default behavior because when site loads at first there are no such elements and I'm loading them dynamically with jQuery. Is there a way to lazy load them and refresh style-sheet in the background somehow? Or any other tricks to help me?
Thanks a lot!
Is the stylesheet referenced in the head element of the file being loaded? If so, it won't be loaded by load because load silently filters everything but the content of the body element. If you include the stylesheet in the document from which the script runs and into which the other document will be loaded, it should work just fine.
<head>
<link rel="stylesheet" type="text/css" href="styles.css" media="screen />
...
<script type="text/javascript" src="...jquery.js"></script>
<script type="text/javascript">
$(function() {
$("a").click( function(e) {
$("#Display").load('file.html');
$("#Display").show();
return false;
});
});
</script>
</head>
<body>
<p>Load Content</p>
<div id="Display"></div>
</body>
This question already has answers here:
No-Javascript Detection Script + Redirect
(4 answers)
Closed 6 years ago.
I need to show COMPLETELY different content when JavaScript is disabled. I know I can use <noscript> tag... but how can I hide the rest of the page when JavaScript is disabled?
Thanks.
here is another solution:
inside your head below your normal stylesheet have:
<link rel="stylesheet" href="jsdisabled.css" />
<script type="text/javascript">
document.write('<link rel="stylesheet" href="jsenabled.css" />');
</script>
that jsenabled.css would have:
#content { display:block; }
while jsdisabled.css would have:
#content { display:none; }
although the solution below works it does not validate
inside your noscript tag you can put some style tag that hides the rest of your content
ie:
<noscript>
<style type="text/css">
#content { display:none; }
</style>
no js content goes here.
</noscript>
<div id="content">
content here.
</div>
I like using this method because it doesn't make the page flash when javascript is enabled.
I would not use noscript to show alternate content. For one, only a limited number of tags are valid in a <noscript> tag, and <style> is not one of them. Instead, take a different approach. Have your content when javascript is disabled visible by default, and show alternate content when JavaScript is enabled. The best way to do this is with simple CSS and one line of JS. If you do it as I show here, there should not be an awkward flash of content:
<head>
...
<script type="text/javascript"> document.documentElement.className += " js"</script>
<link type="text/css" href="css/style.css" media="all" rel="stylesheet" />
</head>
<body>
<div id="header" class="no_js">header</div>
<div id="alt_header" class="js">header</div>
.. etc ..
<div id="super_duper" class="js">Whatever</div>
</body>
Just apply js to everything that should show when JavaScript is enabled, and no_js to everything else. Then in your CSS put this:
html.js .no_js, html .js { display: none }
html.js .js { display: block }
Use meta tags to redirect to a different page.
<noscript>
<meta http-equiv="refresh" content="2; URL=enable_javascript.php">
</noscript>
You could use Javascript to switch to a different page containing Javascript.
For example:
<script type="text/javascript">
location = "JSEnabled.html";
</script>
Non-javascript content goes here
All the above are fine, I believe.
Just an alternative, you can try this,
<head>
<script type="text/javascript">
//if javascript enabled
window.onload=function(){
document.getElementById('jsContentWrapper').style.display='block';
}
</script>
</head>
<body>
<div id='jsContentWrapper' style='display:none;'>
---
--
-
</div>
<noscript>
Place your javascript disabled content here
</noscript>
</body>
Thanks.
Setup a div tag with the id of content. Add the content you want displayed when javascript is disabled. Use jquery $("#content ").load("content.html") to load content if javascript is enabled.