Why is my CSS taking effect outside of my template? - html

I am importing a web component containing a template into index.html. Why is the style in my-template having an effect on the paragraph in index.html.
Put another way, why is all the text in index.html rendered as blue?
I thought CSS inside templates was namespaced in some way. Is this not the case?
index.html
<!DOCTYPE html>
<html>
<head>
<style> p { color: red; }</style>
<link rel="import" href="my-component.html"/>
</head>
<body>
<p>FOO</p>
<my-component></my-component>
</body>
</html>
my-component.html
<template id="my-template">
<style>
p {
color: blue;
}
</style>
<p class="foo">BAR</p>
</template>
<script>
(function() {
var importDoc, myComponentProto;
importDoc = document.currentScript.ownerDocument;
myComponentProto = Object.create(HTMLElement.prototype);
myComponentProto.createdCallback = function() {
var template1, template1Clone;
template1 = importDoc.querySelector('#my-template');
template1Clone = importDoc.importNode(template1.content, true);
this.appendChild(template1Clone); // Insert into the DOM.
};
document.registerElement('my-component', {
prototype: myComponentProto
});
}())
</script>

I think what you want is a scoped stylesheet. Try adding the scoped attribute:
<style scoped>
p {
color: blue;
}
</style>

You should initially create shadow root to hide your template dom into shadow:
myComponentProto.createdCallback = function() {
var shadow = this.createShadowRoot();
shadow.appendChild(importDoc.querySelector('#my-template').content);
};
Live preview: http://plnkr.co/edit/QrvYEUYvzIfstUEoD4Od?p=preview

Related

How can I insert CSS styles inside <body> independent of specific elements?

Traditionally, you can add CSS in three ways:
External CSS via <link rel="stylesheet" href="foo.css">
Internal CSS via <style> h1 { ... } in the <head> element
Inline CSS via the style="..." attribute on specific elements
Inline CSS has the drawback that I can't use CSS classes, which is something I need to do. Is there a way to define internal CSS (e.g. a <style></style> fragment in the <body> element?
This would be much easier for me because I could create a self-contained HTML snippet with a method in my server code. This kind of method is necessary because I don't control the <head> section. It is owned by a commercial product. I can only insert content inside the <body>.
Example:
<div>
<style>
.myclass {...}
</style>
<div class="myclass">...</div>
</div>
Related: https://htmx.org/essays/locality-of-behaviour/
I have seen other websites (like https://amazon.com) where they appear to have several style tags inside the <body>.
There is a huge gap between theory and practice. Many sites use <style> in the body.
The editors decided against it. But maybe there will be a change in the future: https://github.com/whatwg/html/issues/1605
Under the premise, you don't care about invalid HTML in relation of <style> inside <body> (which is not that uncommon), you can assign unique identifier i.e.:
<style>
.my-class-1 {
color: gold;
}
</style>
<div class="my-class-1">Fragment Content</div>
<style>
.my-class-2 {
color: tomato;
}
</style>
<div class="my-class-2">Fragment Content</div>
<div class="my-fragment-1">
<style>
.my-fragment-1 .my-class {
color: teal;
}
</style>
<div class="my-class">Fragment Content</div>
</div>
<div class="my-fragment-2">
<style>
.my-fragment-2 .my-class {
color: hotpink;
}
</style>
<div class="my-class">Fragment Content</div>
</div>
<style id="my-style-1">
#my-style-1 + div {
color: orangered;
}
</style>
<div>Fragment Content</div>
<style id="my-style-2">
#my-style-2 + div {
color: indigo;
}
</style>
<div>Fragment Content</div>
the simpler answer to your question is "Yes" and I'll elaborate on this with several examples below. A <style> tag will work wherever you place it within either the <head> or the <body>.
A style tag placed in the <body> tag technically does violate HTML syntax rules, it's surprisingly common in practice, even among some larger corporations.
There are several different methods for including <body>-level <style> tags in your project.
1. Pure HTML <style> tags (the static method)
If you have all the styles you need already written up and there are no dynamic pieces needed, you can simply write those styles into a <style> tag statically and include those in the code, as seen in this example below:
<html>
<head></head>
<body>
<div class="custom-widget">
<h1>This is a title</h1>
<p>This is some text.</p>
<style>
.custom-widget {
display: block;
padding: 20px;
border: 5px double red;
}
.custom-widget h1 {
text-transform: uppercase;
}
.custom-widget h1::first-letter {
font-size: 150%;
}
.custom-widget p {
font-style: italic;
}
</style>
</div>
</body>
</html>
2. Writing the styles into a <style> tag as text using JavaScript
If you need to load the styles into your <style> tag dynamically and you simply need plain text styles that you will not need to change much after creating them. You can create the <style> block and then inject the CSS styles as plain text as desired, as seen in this example below:
const counter = document.getElementById('counter');
let count = +counter.dataset.count;
const customWidgetStyle = document.querySelector('.custom-widget style'),
countdown = setInterval(() => {
if (count--) {
counter.innerText = `Importing CSS in ${count}…`;
} else {
clearInterval(countdown);
counter.remove();
customWidgetStyle.innerHTML = `
.custom-widget {
display: block;
padding: 20px;
border: 5px double red;
}
.custom-widget h1 {
text-transform: uppercase;
}
.custom-widget h1::first-letter {
font-size: 150%;
}
.custom-widget p {
font-style: italic;
}
`;
}
}, 1000);
<html>
<head></head>
<body>
<div class="custom-widget">
<h1>This is a title</h1>
<p>This is some text.</p>
<style></style>
</div>
<span id="counter" data-count="3">Importing CSS in 3…</span>
</body>
</html>
3. Creating cssRules styles into a <style> tag using the JavaScript CSSStyleSheet.insertRule() method
If you need even more flexibility with how you add your styles, you can use the CSSStyleSheet.insertRule() (MDN docs), which will dynamically allow you to add and manage the styles more granularly. This may be overkill for your specific need but there's a lot of power, flexibility, and control when working with the CSSOM.
Here is an example of this method, in which I use an addStylesheetRules function example defined on the MDN docs page for insertRule under the heading Examples, here:
const addStylesheetRules = (rules, stylesheet) => {
if (stylesheet === undefined) {
const style = document.createElement('style');
stylesheet = style.sheet;
document.head.appendChild(style);
}
for (let i = 0; i < rules.length; i++) {
let j = 1,
propStr = '';
rule = rules[i];
const selector = rule[0];
if (Array.isArray(rule[1][0])) {
rule = rule[1];
j = 0;
}
for (let pl = rule.length; j < pl; j++) {
const prop = rule[j];
propStr += prop[0] + ': ' + prop[1] + (prop[2] ? ' !important' : '') + ';\n';
}
stylesheet.insertRule(selector + '{' + propStr + '}', stylesheet.cssRules.length);
}
}
const customWidget = document.querySelector('.custom-widget'),
customWidgetStyleTag = document.createElement('style');
customWidget.appendChild(customWidgetStyleTag);
const customWidgetStylesheet = customWidgetStyleTag.sheet;
addStylesheetRules([
['.custom-widget',
['display', 'block'],
['padding', '20px'],
['border', '5px double red']
],
['.custom-widget h1',
['text-transform', 'uppercase']
],
['.custom-widget h1::first-letter',
['font-size', '150%']
],
['.custom-widget p',
['font-style', 'italic']
]
], customWidgetStylesheet);
<html>
<head></head>
<body>
<div class="custom-widget">
<h1>This is a title</h1>
<p>This is some text.</p>
</div>
</body>
</html>
Please let me know if there is any more context I can add to better answer your question.
It is going to work in html5, even if it's regarded as invalid in html4.
I have an example from where I work now.
We are adding a slideshow for some books in a library catalogue and because this is done with a plugin the only possible way to style this is to include a <style> block with the html, as this plugin doesn't and shouldn't have access to <head> of the CMS it is designed for.
However, this solution is a last resort because of limitations of how the CMS is built and should be avoided.
Couldn't you target the head element with Javascript and insert a style programmatically?
<script>
var head = document.querySelector('head')[0];
var css = 'div { background: red; }',
var style = document.createElement('style');
style.appendChild(document.createTextNode(css));
head.appendChild(style) ;
</script>
As far as I can understand from the description you gave, you don't have the access to the <head>...</head> element, but you are free to edit the body. Also, you want to use CSS3 Classes, but with inline CSS, you can't.
I can't find a way in pure HTML/CSS, so I suggest you to use JQuery.
<script async src="https://cdn.statically.io/libs/jquery/3.6.0/jquery.min.js" />
<script>$('head').append('<style>/*Your styles here*/</style></script>');
Now, add classes to the html elements and edit the content between the <style /> tag. Also, place this script at the end of <body> so that you can avoid the probable weird problems caused by placing this in between. XD
But remember, this will change the head after the user has loaded the page. So, theoretically, the users will first see an ugly html page without styles, then styles will be loaded and they'd see the page with styles.For more explanation, check out the official documentation: https://api.jquery.com/append/
Your example should work. I work with WordPress using custom html where all the custom code goes into a <body> tag, and styling like so should work within each page (added more divs just to show an example that one style tag can hold classes for all divs within a div):
<div>
<style>
.className { ... }
.classNameTwo{ ... }
.classNameThree{ ... }
</style>
<div class="className"></div>
<div class="classNameTwo">
<div class="classNameThree"></div>
</div>
</div>

Shadow DOM global css inherit possible?

Is there a way to inherit :host element css styles into shadow DOM?
The reason is if we start developing web components, each web component style must be consistent on a page.
The page can have global css, and this global css styles can be inherited to shadow DOM. There was ::shadow and /deep/, but it's deprecated now.
Or, is this against pattern? If so, why?
I found this Q/A, but seems outdated for me.
Can Shadow DOM elements inherit CSS?
http://plnkr.co/edit/qNSlM0?p=preview
const el = document.querySelector('my-element');
el.attachShadow({mode: 'open'}).innerHTML = `
<!-- SEE THIS 'red' is not red -->
<p class="red">This is defined in Shadow DOM. I want this red with class="red"</p>
<slot></slot>
`;
.red {
padding: 10px;
background: red;
font-size: 25px;
text-transform: uppercase;
color: white;
}
<!DOCTYPE html>
<html>
<head>
<!-- web components polyfills -->
<script src="//unpkg.com/#webcomponents/custom-elements"></script>
<script src="//unpkg.com/#webcomponents/webcomponentsjs"></script>
<script src="//unpkg.com/#webcomponents/shadydom"></script>
<script src="//unpkg.com/#webcomponents/shadycss#1.0.6/apply-shim.min.js"></script>
</head>
<body>
<div>
<p class="red">I'm outside the element (big/white)</p>
<my-element>
<p class="red">Light DOM content is also affected.</p>
</my-element>
<p class="red">I'm outside the element (big/white)</p>
</div>
</body>
</html>
As supersharp pointed out it's very simple but not obvious from the examples you can find on the internet. Take this base class as an example. Alternatively, you could make two different ones (e.g. Component and ShadowComponent). There is also the option to use adoptedStyleSheets or the ::part selector.
class HtmlComponent extends HTMLElement {
static ModeShadowRoot = 0;
static ModeRoot = 1;
static styleSheets = [];
static mode = HtmlComponent.ModeShadowRoot;
#root = null;
constructor() {
super();
if (this.constructor.mode === HtmlComponent.ModeShadowRoot) {
const shadowRoot = this.attachShadow({ mode: 'closed' });
shadowRoot.adoptedStyleSheets = this.constructor.styleSheets;
this.#root = shadowRoot;
} else {
this.#root = this;
}
}
get root() {
return this.#root;
}
init() {
this.root.innerHTML = this.render();
}
render() {
return '';
}
}
class Test extends HtmlComponent {
static mode = HtmlComponent.ModeRoot;
constructor() {
super();
super.init();
}
render() {
return `
<div>
<x-nested-component></x-nested-component>
</div>
`;
}
}
One of the features of Shadow DOM is to isolate CSS styles.
So if you want your Custom Elements to inherit styles from the main page, don't use Shadow DOM. It's not mandatory.

Polymer add class when property is true

I have seen this in angular before and wondered if this is possible in polymer as well. Angular - What is the best way to conditionally apply a class?
I have set up a property named 'animated':
animated: {
type: Boolean,
value: false,
},
When animated is true, a div inside my element should have a css class of .animate.
<div class=""></div>
For now I have done that inside of the ready function.
But since I came across that Stackoverflow question I wondered if this is prossible in polymer.
Thanks!
One way to do that is using a function as follow:
<div class$="{{_getClass(animated)}}"></div>
Where class$ with $ symbol indicates to Polymer's that property is generate using data binding. So, your _getClass function will look like this:
_getClass: function(animated){
return animated ? "animate" : "";
}
When animate property changes, the _getClass function will be invoked and this function will return the string that indicates the class you need.
You can also use toggleClass method of Polymer
<base href="https://polygit.org/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="polymer/polymer.html">
<dom-module id="my-element">
<template>
<style>
.show {
display: block !important;
border: 1px solid black;
}
.hide {
width: 100px;
height: 100px;
display: none;
}
</style>
<div class="hide" id="toggle"></div>
<button on-tap="_toggleDiv">Press to toggle</button>
</template>
</dom-module>
<script>
Polymer({
is: 'my-element',
properties: {
show: {
type: Boolean,
value: false
}
},
_toggleDiv: function() {
this.show = !this.show;
this.toggleClass('show', this.show, this.$.toggle);
}
});
</script>
<my-element></my-element>

Load multiple babylon files in one html

I want to know how can I load two or more babylon files in one html page or if its possible to join them.
I have the following code which its good to see one simple model (exported) but I need add more exported models in the same html page. I heard about a "options.babylonFolder + "/", options.babylonFile" option but I dont know more than that.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Using babylon.js - How to load a scene</title>
<script type="text/javascript" src="./103A_files/hand.js"></script>
<script type="text/javascript" src="./103A_files/cannon.js"></script>
<script type="text/javascript" src="./103A_files/babylon.js"></script>
<style>
html, body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
overflow: hidden;
}
#renderCanvas {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<canvas id="renderCanvas"></canvas>
<script>
if (BABYLON.Engine.isSupported()) {
var canvas = document.getElementById("renderCanvas");
var engine = new BABYLON.Engine(canvas, true);
BABYLON.SceneLoader.Load("", "101A.babylon", engine, function (newScene) {
//BABYLON.SceneLoader.Load(options.babylonFolder + "./GrupoBabylon", options.babylonFile, engine, function (newScene) {
newScene.executeWhenReady(function () {
// Attach camera to canvas inputs
newScene.activeCamera.attachControl(canvas);
// Once the scene is loaded, just register a render loop to render it
engine.runRenderLoop(function() {
newScene.render();
});
});
}, function (progress) {
// To do: give progress feedback to user
});
}
</script>
You can use BABYLON.SceneLoader.Append to merge a new scene with the current one

Polymer - styling childnodes of host

first, i searched for similar questions but haven't found a solution for my problem, which is basically simple, i guess. :)
I built a simple image-slider for clearing up the whole concepts of web components for myself with a real world example.
My custom component is made out of 5 components and a headline.
stage-slider
stage-element
h1
stage-button
stage-teaserdock
stage-teaser
The component slides fine. Now i wanted to add teaser navigation at the bottom. So first i tried adding a single teaser item.
Ok.. what i want to do is access an element inside of the stage-slider:
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../stage-element/stage-element.html">
<link rel="import" href="../stage-button/stage-button.html">
<polymer-element name="stage-slider" attributes="items slideInterval">
<template>
<style>
:host {
width: 960px;
height: 485px;
position: absolute;
top: 50%;
left: 50%;
margin: -242px 0px 0px -480px;
overflow: hidden;
display: block;
}
:content .teaser
{
left: 30px;
}
</style>
<div id="wrapper">
<template id="slider" repeat="{{item in items}}">
<stage-element headline="{{item.headline}}"
image="{{item.image}}"
buttonLabel="{{item.buttonLabel}}"
buttonTargetWindow="{{item.buttonTargetWindow}}"
buttonTargetURL="{{item.buttonTargetURL}}">
</stage-element>
</template>
<content class="teaser" select="stage-teaser"></content>
</div>
</template>
<script src="./libs/TweenLite/easing/EasePack.min.js"></script>
<script src="./libs/TweenLite/plugins/CSSPlugin.min.js"></script>
<script src="./libs/TweenLite/TweenLite.min.js"></script>
</polymer-element>
<script>
Polymer('stage-slider',
{
slideInterval: 7000,
items: [],
index: 0,
ready: function ()
{
console.log('-----------------');
console.log('stage slider ready!');
},
attached: function ()
{
console.log('-----------------');
console.log('stage slider attached!');
this.$.wrapper.style.width = (960 * (this.items.length)).toString() + "px";
//
if (this.items.length > 1 && this.slideInterval != 0)
{
var that = this;
setInterval(function ()
{
that.startSliding(that);
}, this.slideInterval
);
}
},
startSliding: function (shadowDom)
{
console.log('More children than 1 -> SLIDE EM!');
TweenLite.to(shadowDom.$.wrapper, 1.5, {
marginLeft: -960,
ease: Expo.easeInOut,
onStart: function ()
{
console.log('tween started'); //, this = ', this);
},
onComplete: function ()
{
// console.log('tween complete');
// console.log(shadowDom.$.wrapper.getElementsByTagName('stage-slide')[0]);
shadowDom.$.wrapper.style.marginLeft = 0;
shadowDom.$.wrapper.appendChild(shadowDom.$.wrapper.getElementsByTagName('stage-element')[0]);
}});
}
});
</script>
This is how my markup looks like:
<stage-slider slideInterval="0"
items='[
{
"headline" : "Test headline",
"image" : "img/slide0.jpg",
"buttonLabel" : "Test buttonlabel",
"buttonTargetURL" : "http://www.google.com"
}
]'>
<stage-teaser class="teaser"
image="img/teaser0.jpg"
headline="Test teasertext"
targetURL="http://google.com">
</stage-teaser>
</stage-slider>
So there is a stage-teaser element nested inside my stage-slider element.
I thought i have to distribute it to the content tag inside my template element. Which is why there is a content tag like this:
<content class="teaser" select="stage-teaser"></content>
It displays the teaser item correctly.
But now i want to define its css from within the slider component. This is where i am totally stuck..
I can access the element itself with :host, thats good.
But how do i access the content element, which renders the teaser?
i tried the following:
:host(stage-teaser),
:host(.teaser),
:host(#teaser),
:content .teaser,
:host(:content .teaser),
as you can see.. i am kinda stuck. :-/
any idea would be cool!
thanks,
Rob
I suspect that the issue you're seeing is just a typo. Instead of :content you want ::content. Here's a jsbin showing a simple example: http://jsbin.com/mijifiru/1/edit and for more info on styling web components with the shadow DOM, check out this article: http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom-201/
If that doesn't solve the issue it would be helpful if you reduced your code down to a Minimal, Complete, and Verifiable example, and for bonus points do so in an online editor like jsbin.
<polymer-element name='my-container' noscript>
<template>
<style>
::content .innerContent {
background-color: red;
}
</style>
Shadow Dom
<content></content>
</template>
</polymer-element>
<my-container>
<div class='innerContent'>Contained matching Light DOM</div>
<div>Contained unmatched Light DOM</div>
</my-container>