How do I style ShadowDOM elements using external CSS in Firefox? - html

I'm trying to build a custom HTML element. The problem is that I'm not able to apply styles to the Shadow DOM elements provided using external CSS. The code is working in Chrome but not in Firefox.
var proto = Object.create(HTMLElement.prototype);
proto.createdCallback = function() {
console.log('Element creation started...');
var inputTextElement = document.createElement('input');
inputTextElement.type = 'text';
inputTextElement.className = 'simpleElem';
// Shadow DOM root
var shadowRoot = this.createShadowRoot();
shadowRoot.appendChild(inputTextElement);
console.log('Element creation ended...');
};
var SimpleElement = document.registerElement('simple-element', { prototype: proto });
simple-element {
}
simple-element::shadow .simpleElem {
height: 30px;
margin: 0px;
padding: 0px;
width: 180px;
}
<!doctype html>
<html>
<head>
<title>HTML5 | Custom Elements</title>
<link type="text/css" rel="stylesheet" href="simple-elem.css">
<script type="text/javascript" src="simple-elem.js"></script>
</head>
<body>
<simple-element></simple-element>
</body>
</html>
Not able to figure out what is wrong with Firefox.

As noted by Gábor Imre, Shadow DOM is not enabled by default in Firefox because it is still under development. You can, however, use a polyfill to get pretty good Shadow DOM behavior in all browsers that don't support it.. If you do, you'll then need to use polyfill-next-selector to obtain the behavior you want.

Update: FF Shadow DOM support has arrived.
Firefox has no Shadow DOM support yet, see CanIUse.com. I recommend sticking to Chrome.
EDIT: FF Nightly has some support, it can be enabled manually.

While Shadow DOM in general has been supported in Firefox for a while now (invalidating the two other answers), with Firefox 72 you can now target custom/Shadow DOM elements via the part attribute and ::part() pseudo-element, respectively:
//this JS boilerplate adapted from MDN
let template = document.getElementById("simple-element");
globalThis.customElements.define(template.id, class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
this.shadowRoot.appendChild(template.content);
}
});
simple-element::part(shadow) {
height: 30px;
margin: 0;
padding: 0;
width: 180px;
background: green;
}
<template id="simple-element">
<div part="shadow">Hi</div>
</template>
<simple-element></simple-element>
Obviously this code looks a lot different from what your question code looks like because the Shadow DOM spec/implementations have changed quite a lot since 2014.

Related

Model-viewer outline

<!doctype html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script type="module" src="https://unpkg.com/#google/model-viewer/dist/model-viewer.js"></script>
<script nomodule src="https://unpkg.com/#google/model-viewer/dist/model-viewer-legacy.js"></script>
<!-- Use it like any other HTML element -->
<model-viewer src="shared-assets/models/Astronaut.glb" alt="A 3D model of an astronaut" auto-rotate camera-controls></model-viewer>
</body>
</html>
When you click on model viewer the outline appears, I am unable to remove the outline from the model-viewer how would one remove it?
Edit
/**
* This is mixin function is designed to be applied to a class that inherits
* from HTMLElement. It makes it easy for a custom element to coordinate with
* the :focus-visible polyfill.
*
* #param {Function} SuperClass The base class implementation to decorate with
* implementation that coordinates with the :focus-visible polyfill
*/
export function FocusVisiblePolyfillMixin(SuperClass) {
var coordinateWithPolyfill = function(instance) {
// If there is no shadow root, there is no need to coordinate with the
// polyfill. If we already coordinated with the polyfill, we can skip
// subsequent invokcations:
if (
instance.shadowRoot == null ||
instance.hasAttribute('data-js-focus-visible')
) {
return;
}
// The polyfill might already be loaded. If so, we can apply it to the
// shadow root immediately:
if (self.applyFocusVisiblePolyfill) {
self.applyFocusVisiblePolyfill(instance.shadowRoot);
} else {
// Otherwise, wait for the polyfill to be loaded lazily. It might never
// be loaded, but if it is then we can apply it to the shadow root at
// the appropriate time by waiting for the ready event:
self.addEventListener(
'focus-visible-polyfill-ready',
function() {
self.applyFocusVisiblePolyfill(instance.shadowRoot);
},
{ once: true }
);
}
};
// IE11 doesn't natively support custom elements or JavaScript class syntax
// The mixin implementation assumes that the user will take the appropriate
// steps to support both:
return class extends SuperClass {
// Attempt to coordinate with the polyfill when connected to the document:
connectedCallback() {
super.connectedCallback && super.connectedCallback();
coordinateWithPolyfill(this);
}
};
}
So I added this in file name focus-visible.js
added this to html
<body>
<!-- The :focus-visible polyfill removes the focus ring for some input types -->
<script src="focus-visible.js" defer></script>
<script type="module" src="https://unpkg.com/#google/model-viewer/dist/model-viewer.js"></script>
<script nomodule src="https://unpkg.com/#google/model-viewer/dist/model-viewer-legacy.js"></script>
<!-- Use it like any other HTML element -->
<model-viewer src="shared-assets/models/Astronaut.glb" alt="A 3D model of an astronaut" auto-rotate camera-controls>
</model-viewer>
</body>
and this in css :focus-visible polyfill{ outline: none; }
Am I doing something wrong?
Just make sure the focus-visible.js is included in your page. You probably started your modelviewer page with an older example that didn't have it included.
Pick it up from the repo or this link:
https://unpkg.com/focus-visible#5.1.0/dist/focus-visible.js
I add the attribute data-js-focus-visible to <model-viewer>. Like this:
<model-viewer src="myFile.glb" data-js-focus-visible></model-viewer>
After that the outline is no longer shown.
If you inspect the <model-viewer> component in the main example from the official doc https://modelviewer.dev/ you can see that they use the data-js-focus-visible attribute:
<model-viewer src="shared-assets/models/Astronaut.glb" alt="A 3D model of an astronaut" auto-rotate="" camera-controls="" data-js-focus-visible="" ar-status="not-presenting"></model-viewer>
It seems to appear that this is an ongoing issue with model-viewer as it is still in development. I would go leave some feedback on their github page, or see if this issue matches yours.
Requires this script to be added as per model-viewer official documentation
<script src="./_model-viewer_ Interactive Example_files/focus-visible.js.download" defer=""></script>

create switch for changing theme intead of browser page style html

I added a theme in my HTML page by:
<link rel="alternate stylesheet" href="css/dark.css" title="dark">
this creates an option to switch theme from view>style in browser as it is expected to. I want to create a switch in the page itself for changing the theme. <button>Switch Theme</button>will create a button but how do I make it switch theme?
You should trigger a change in the href attribute of the <link> tag on the click of the button, as demonstrated:
HTML
<link rel="stylesheet" href="dark.css" id="theme">
<button id="switch">Switch Theme</button>
JavaScript
document.getElementById('switch').onclick = function() {
if (document.getElementById('theme').href == "dark.css") {
document.getElementById('theme').href = "light.css";
} else {
document.getElementById('theme').href = "dark.css";
}
};
Now, the dark.css and light.css would contain different styles for your elements for both the themes respectively. For example:
dark.css
body{
background: #000;
}
light.css
body{
background: #fff;
}
this is very simple you need to bind the click to function and call it:
Replacing css file on the fly (and apply the new style to the page)

How do I add a css line ONLY for Safari

I am making a website, but an element needs margin in Chrome and other browsers, but not in safari. So I want to add a css line to fix it, but I can't find any method to add css for safari 3+ only.
I believe this should work
Fiddle : http://jsfiddle.net/kHFjM/1/
var userAgent = navigator.userAgent.toLowerCase();
if (userAgent .indexOf('safari')!=-1){
if(userAgent .indexOf('chrome') > -1){
//browser is chrome
}else if((userAgent .indexOf('opera') > -1)||(userAgent .indexOf('opr') > -1)){
//browser is opera
}else{
//browser is safari, add css
}
}
here is the link to detect the browser version
https://stackoverflow.com/a/5918791
jQuery integrated solution:
<script type="text/javascript">
$(function () {
if (navigator.userAgent.indexOf('Safari') != -1 &&
navigator.userAgent.indexOf('Chrome') == -1) {
$("body").addClass("safari");
}
});
</script>
<style>
div {
margin:20px;
}
.safari div {
margin:0;
}
</style>
Pure JS integrated solution:
<style>
div {
margin:20px;
}
.safari div {
margin:0;
}
</style>
<body>
<script type="text/javascript">
if (navigator.userAgent.indexOf('Safari') != -1 &&
navigator.userAgent.indexOf('Chrome') == -1) {
document.body.className += " safari";
}
</script>
</body>
This is not possible since you would be applying the same property to Chrome as well. As Chrome, and Safari both use the -webkit- prefix.
But you could do this in PHP.
<?php
$browser = get_browser();
if(strtolower($browser->browser) == 'safari') {
echo '<link href="safari.css" rel="stylesheet" type="text/css" />';
}
?>
Replace safari.css with your own stylesheet. Credit to #theorise
In safari 9.0 (only, not > 9.0) you can do it now in CSS with this nice hack. You don't need any JS code. I did it and its working fine for me. Use this hack:
#supports (overflow:-webkit-marquee) and (justify-content:inherit) {
/* type your custom css code here */
}
The reason it is working is: Safari 9.0 and above have feature detection. So by detecting a feature which is exclusively for Safari you can detect Safari. overflow:-webkit-marquee and justify-content:inherit are exclusively for safari. Thats why we can detect safari with this simple CSS hack.
This worked for me for old and new safari browsers:
#media not all and (min-resolution:.001dpcm) {
#supports (-webkit-appearance: none) {
/* Safari Only CSS here */
}
}
Instead of adding more code to fix your problem, since the default margins are different, you could try resetting all off the margins and paddings of surrounding elements to 0 first before changing it. That could solve your issue.
It's completely a personal preference, but I start all of my webpages with:
*{
margin:0;
padding:0;
}
I've never had any cross browser issues regarding margins or padding while doing this.
There is a question similar to this on the CSS-Tricks forum. But the answer is basically, nope.
You could attempt user-agent sniffing server side or with JavaScript and then add a class to the html (like for old IE versions in HTML5 BoilerPlate).
Hope this helps.
--beaten to it by the guys above and below!
I have tried almost all of the above-suggested solutions, but nothing is working around until I saw a piece of code in my says:
#-webkit-keyframes fadein {
//code here
}
And it did the job and applied the required CSS for:
Safari, Chrome and Opera > 12.1
Hope this can help someone.

Fabric.js canvas not catching mouse events on Google Chrome

Googled this a lot and didn't get any useful hint/solution.
I have this simple html page including some CSS styles, jQuery, jQuery-ui and obviously Fabric.js; on document.ready I launch an ajax call and render something on the canvas. Until now everything seems fine but when a I need to catch some mouse events I get nothing. This behaviour is shown only on Chrome (current version 25.0.1364.97); everything works fine on Firefox or Internet Explorer (v. 9).
Here's some of the js code:
$(document).ready(function() {
//setup canvas etc.
eCanvas = new fabric.Canvas('EViewport', {
backgroundColor: 'rgba(255, 50, 50, .3)',
selection: true,
selectionColor: 'blue',
selectionLineWidth: 2
});
EViewport = $("#CanvasContainer");
viewW = EViewport.width();
viewH = EViewport.height();
eCanvas.setWidth(viewW);
eCanvas.setHeight(viewH);
eCanvas.observe('object:selected', function(options) {
if (options.target) {
console.log('an object was selected! ', options.target.type);
}
});
eCanvas.observe('mouse:down', function() {
console.log('mouse click! ');
});
eCanvas.on('mouse:down', function() {
console.log('mouse click! ');
});
eCanvas.on('mousedown', function() {
console.log('mouse click! ');
});
//... render some rectangles and stuff...
});
And here's the html structure (notice that Eviewport.js file contains previously pasted code):
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="baseCss/jquery-ui.css" type="text/css">
<script type="text/javascript" src="baseJs/jquery.js"></script>
<script type="text/javascript" src="baseJs/jquery-ui.js"></script>
<script type="text/javascript" src="Eviewport.js"></script>
<link type="text/css" rel="stylesheet" href="Eviewport.css">
<script type="text/javascript" src="baseJs/Fabric.js"></script>
</head>
<body>
<div id="MainContainer">
<div id="CanvasContainer">
<canvas id="EViewport">
Canvas is not supported
</canvas>
</div>
</div>
</body>
</html>
Selection features don't work with chrome either while they work on IE and Firefox.
I tried many things (as you can see I tried changing canvas.observe with canvas.on), changed jQuery and jQueryui versions but nothing changed.
Using developer tools on Google Chrome doesn't show much.
There's no z-index on html elements given by CSS, and I tried disabling different js and CSS but that didn't solve the problem.
I noticed that the problem shows also shows on the demo page of Fabric.js (just tried http://fabricjs.com/stickman/); render works, effects also but no mouse events or selection working.
Is this a bug?
Ok, finally found what's not working.
I have a Wacom device attached and looks like latest Chrome version sets a flag about "touch enabled device" and that's breaking my code.
A simple solution can be changing chrome flags (chrome://flags/)
Related posts:
https://github.com/kangax/fabric.js/issues/450

How to apply a style to an embedded SVG?

When an SVG is directly included in a document using the <svg> tag, you can apply CSS styles to the SVG via the document's stylesheet. However, I am trying to apply a style to an SVG which is embedded (using the <object> tag).
Is it possible to use anything such as the following code?
object svg {
fill: #fff;
}
Short answer: no, since styles don't apply across document boundaries.
However, since you have an <object> tag you can insert the stylesheet into the svg document using script.
Something like this, and note that this code assumes that the <object> has loaded fully:
var svgDoc = yourObjectElement.contentDocument;
var styleElement = svgDoc.createElementNS("http://www.w3.org/2000/svg", "style");
styleElement.textContent = "svg { fill: #fff }"; // add whatever you need here
svgDoc.getElementById("where-to-insert").appendChild(styleElement);
It's also possible to insert a <link> element to reference an external stylesheet:
var svgDoc = yourObjectElement.contentDocument;
var linkElm = svgDoc.createElementNS("http://www.w3.org/1999/xhtml", "link");
linkElm.setAttribute("href", "my-style.css");
linkElm.setAttribute("type", "text/css");
linkElm.setAttribute("rel", "stylesheet");
svgDoc.getElementById("where-to-insert").appendChild(linkElm);
Yet another option is to use the first method, to insert a style element, and then add an #import rule, e.g styleElement.textContent = "#import url(my-style.css)".
Of course you can directly link to the stylesheet from the svg file too, without doing any scripting. Either of the following should work:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="my-style.css" type="text/css"?>
<svg xmlns="http://www.w3.org/2000/svg">
... rest of document here ...
</svg>
or:
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<link href="my-style.css" type="text/css" rel="stylesheet"
xmlns="http://www.w3.org/1999/xhtml"/>
</defs>
... rest of document here ...
</svg>
Update 2015: you can use jquery-svg plugin for apply js scripts and css styles to an embedded SVG.
You can do this without javsscrpt by putting a style block with your styles inside the SVG file itself.
<style type="text/css">
path,
circle,
polygon {
fill: #fff;
}
</style>
If the only reason for using the tag to inlcude the SVG is that you do not want to clutter your source code with the markup from the SVG, you should take a look at SVG injectors like SVGInject.
SVG injection uses Javascript to inject an SVG file inline into your HTML document. This allows for clean HTML source code while making the SVGs fully styleable with CSS. A basic example looks like this:
<html>
<head>
<script src="svg-inject.min.js"></script>
</head>
<body>
<img src="image.svg" onload="SVGInject(this)" />
</body>
</html>
Based on #Erik Dahlström answer, found a short path as follow:
let svg_objecst = document.getElementsByClassName('svg-object')
const forceStylingObjSvg = (svg)=>{
var svgDoc = svg.contentDocument;
svgDoc.firstElementChild.setAttribute('fill','blue')
}
Array.from(svg_objecst).forEach((obj)=>{
obj.addEventListener('load',()=>forceStylingObjSvg(obj))
})
You can create a custom element to inject the SVG file into your html.
This way, the SVG will be inlined, and you can easily apply styles using CSS.
This custom element will work just like the <object> or <embed> tags. The only difference is that <object> or <embed> tags injects the data in a shadow root, which prevents styling from the parent document, while <custom-svg> injects the data in the document itself.
I have tried too many ways to style my SVG images, and this was the easiest and more flexible way i have found so far.
<html>
<head>
<style>
.blue {
fill: blue;
}
.red {
fill: red;
}
</style>
</head>
<body>
<custom-svg class="blue" src="icon.svg"></custom-svg>
<custom-svg class="red" src="icon.svg"></custom-svg>
<script>
class CustomSVG extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
fetch(this.getAttribute('src'))
.then(response => response.text())
.then(text => {
this.innerHTML = text;
});
}
}
customElements.define('custom-svg', CustomSVG);
</script>
</body>
</html>
Notes
The SVG image must not have the "fill" attribute if you want, for example, change the fill color using CSS.
custom-svg works very good, for width style use this:
custom-svg svg{
max-width:64px;
max-height:64px;
}