Shadow DOM v1 styling with polyfill - javascript

I'm trying to work with just ShadowDOM v1 (no Polymer or other web components) but I can't get the styling to work with a polyfill.
The special :host and ::slotted CSS selectors are not working.
I've tried with various polyfills, but TBH, I'm a bit lost. I have seen this question, but it's using custom elements and templates. My scenario is without these.
Can anyone help me make this code work in Firefox or Edge? - specifically, you should see three colors: blue, red and yellow, but only red works.
const shadowHtml =
`<style>
:host {
background: yellow;
}
.inside {
background: red;
margin: 10px;
}
::slotted(div[slot="content"]) {
background: blue;
color: white;
}
</style>
<div>
<p>i am yellow</p>
<div class=inside>i am red</div>
<slot name="content"></slot>
</div>`;
const root = document.querySelector('#root');
const shadow = root.attachShadow({ mode: 'open' });
shadow.innerHTML = shadowHtml;
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Shadow DOM v1 Test</title>
<script src="https://rawgit.com/webcomponents/webcomponentsjs/v1/webcomponents-lite.js"></script>
</head>
<body>
<div id=root>
<div slot="content">
<p>I am blue</p>
</div>
</div>
</body>
</html>

webcomponents-lite.js "v1" now embeds a Shadow DOM v1 polyfill:
the ShadyDOM shim which provides attachShadow()
the ShadyCSS object which simulates :host and ::slotted().
Note that to use it you'll have to put your template string inside a <template> element in order to call ShadyCSS.prepareTemplate()
const makeTemplate = function (strings) {
const template = document.createElement('template');
template.innerHTML = strings[0];
return template;
}
const shadowHtml =
makeTemplate`<style>
:host {
background: yellow;
}
.inside {
background: red;
margin: 10px;
}
::slotted(div[slot="content"]) {
background: blue;
color: white;
}
</style>
<div>
<p>i am yellow</p>
<div class=inside>i am red</div>
<slot name="content"></slot>
</div>`
ShadyCSS.prepareTemplate(shadowHtml, 'div');
const root = document.querySelector('#root');
const shadow = root.attachShadow({ mode: 'open' });
shadow.innerHTML = shadowHtml.innerHTML;
<script src="https://cdn.rawgit.com/webcomponents/shadydom/master/shadydom.min.js"></script>
<script src="https://cdn.rawgit.com/webcomponents/shadycss/master/scoping-shim.min.js"></script>
<div id=root>
<div slot="content">
<p>I am blue</p>
</div>
</div>

Related

How to blur the whole body except a list item?

I wanted to create an effect where the whole body gets blurred or dimmed and only a particular list item appears clear. However when I set the z-index to the list item, it doesn't work. And when I set the z-index of the whole un-ordered list, it works but the all the list items appear clear (which I don't want).
Let me show you my html code:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ashish Toppo</title>
<link href="https://fonts.googleapis.com/css?family=Oxanium&display=swap" rel="stylesheet">
<link rel="stylesheet" href="css/style.css">
</head>
<body >
<!-- the html for the top bar starts here -->
<div class="top_bar" id="topBar">
<div class="logo_name" id="logoName">Ashish Toppo</div>
<ul class="menu">
<li class="menu_items currently_active_menuItem" id="home">home</li>
<li class="menu_items" id="about">about</li>
<li class="menu_items" id="education">education</li>
</ul>
</div>
<!-- the html for the top bar ends here -->
<!-- the html for the intro starts here -->
<div class="intro" id="intro">
<div class="profile_pic" id="profilePic">
<img id="profileImg" src="images/ashish-toppo-green.jpg" width="100%" height="100%" alt="a picture of mine">
</div>
<div class="intro_box" id="introBox">
<!-- some introduction text here -->
<center id="aboutPointer">To know more about me, go to the about section!</center>
</div>
</div>
<!-- the html for the intro ends here -->
<script src="js/uiversal.js"></script>
<script src="js/index.js"></script>
</body>
</html>
Now, the Universal javaScript file:
/* this is a reusable js file universal to all web pages */
/* Ashish Toppo */
"use strict";
function get(id_or_class){
var obj = {
element: ( document.getElementById(id_or_class) ) ? document.getElementById(id_or_class) :
( document.getElementsByClassName(id_or_class) ) ? document.getElementsByClassName(id_or_class) :
( document.querySelector(id_or_class) ) ? document.querySelector(id_or_class) :
console.error("The provided HTML element could not be found"),
html: () => { return obj.element; },
changeText: (text) => { obj.html().innerHTML = text; },
appendText: (text) => {
let appendOn = obj.html().innerHTML;
obj.html().innerHTML = appendOn + text;
},
previousDisplayMode: "block",
hide: () => {
obj.previousDisplayMode = obj.html().style.display;
obj.html().style.display = "none";
},
show: () => {
obj.html().style.display = obj.previousDisplayMode;
},
on: (event, callBack) => {
obj.html().addEventListener(event, callBack);
},
previousZIndex: 1,
focusOn: () => {
let blur = document.createElement("div");
blur.className = "theDivThatBlurs";
blur.style.width ="100vw";
blur.style.height ="100vh";
blur.style.display ="block";
blur.style.position ="fixed";
blur.style.top ="0";
blur.style.left ="0";
blur.style.zIndex ="9";
blur.style.backgroundColor ="rgba(0, 0, 0, 0.9)";
blur.innerHTML = "";
document.body.appendChild(blur);
obj.html().style.zIndex = "100";
}
}
return obj;
}
and the index.js file was as followed:
/* my css wasn't working as i wanted, so i had to fix it using js */
"use strict";
(function(d){
const active = d.getElementsByClassName("currently_active_menuItem");
active[0].style.textDecoration = "none";
})(document);
var about = get("about");
var aboutPointer = get("aboutPointer");
aboutPointer.on("click", function(){
console.log("the about pointer has been clicked");
focus(about);
});
function focus(theElement){
console.log("the focus is working");
theElement.focusOn();
}
You can use the box-shadow property to achieve the dimming effect. Quick and easy :)
Just toggle a class programmatically and it should work for any element you have.
Code
function focusAndDim() {
document.getElementById("maindiv").classList.toggle("visible");
// if you want to get more fancy ;)
document.getElementsByTagName("body")[0].classList.toggle("blur");
}
.visible {
box-shadow: 0 0 0 10000px #ccc;
/* this code below make everything else hidden */
/* box-shadow: 0 0 0 10000px #fff; */
position: relative;
}
.btn {
height: 20px;
line-height: 1.4;
border: 2px solid #999;
padding: 12px 24px;
margin-bottom: 10px;
border-radius: 2px;
cursor: pointer;
}
body {
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
height: 100vh;
}
body.blur div {
filter: blur(2px);
}
body.blur div.visible {
filter: blur(0);
}
<div class="btn" onclick="focusAndDim()" id="maindiv">Click Me</div>
<div>Other elements</div>

How to lazy load html div tags using intersection-observer?

Is it possible to lazy-load the entire div-tag using Intersection Observer API?
I have lazy loaded images using the intersection observer api approach. Not sure how to do it for html elements.
Yes, you can lazy load content into divs. The example below simply uses html() to populate the div with a random string on intersect. If the content you want is a separate html page, you could use load() instead.
function lazyDivs() {
let lis = [].slice.call(document.querySelectorAll("div.lazy")),
items = ["Aruba", "Jamaica", "Bermuda", "Bahama", "Key Largo", "Montego"];
if (!lis.length) {
//do nothing
} else if ("IntersectionObserver" in window) {
let o = new IntersectionObserver(function(es, obs) {
es.forEach(function(e) {
if (e.isIntersecting) {
let li = $(e.target);
li.html(items[Math.floor(Math.random() * items.length)]);
//li.load('/path/to/html/fragment'); //option to load content from a separate page
li.removeClass("lazy");
o.unobserve(e.target);
}
});
});
lis.forEach(function(li) {
o.observe(li);
});
} else {
lis.forEach(function(li) {
let l = $(li);
l.html(items[Math.floor(Math.random() * items.length)]);
//l.load('/path/to/html/fragment'); //option to load content from a separate page
l.removeClass("lazy");
});
}
}
$(document).ready(function() {
lazyDivs();
});
div {
border: 1px solid blue;
width: 100px;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
margin: 10px auto;
}
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<div class="lazy"></div>
<div class="lazy"></div>
<div class="lazy"></div>
<div class="lazy"></div>
<div class="lazy"></div>
<div class="lazy"></div>
<div class="lazy"></div>
<div class="lazy"></div>
<div class="lazy"></div>
</body>
</html>

Vue: How do I generate a new instance of a div with a buttonclick?

I need to create a function that generates a new instance of a specific div (.container in the template) when a button (#addDiv) is clicked. The button should be the only thing visible on the webpage before that. How do I do?
I know that it may be possible to do this with document.appendChild. Is there a better way?
I am using toggle-function that works great, I have included it in the code to give you the whole picture.
Vue
Vue.component('dynamicdDvs', {
template: `
<div>
<div class="headerDiv">
<button class="addDiv" type="button" v-on:click="createDiv">Create</button>
</div>
<div class="container">
<div class="createdDiv">
<h2>I am dynamic!</h2>
<button class="minimize" v-on:click="expand">Make me smal</button>
</div>
<div class="createdDivMinimized" v-if="!displayDiv">
<p>I am a smal version of the created div!</p>
<button class="maximize" v-on:click="expand">Expand me</button>
</div>
</div>
</div>
`,
data:function () {
return {
displayDiv: false
}
},
methods: {
expand: function () {
this.displayDiv = !this.displayDiv;
},
createDiv: function() {
//The function that creates a new div, with the same code as
//.createdDiv and .createDivMinimized may be placed here
}
}
});
CSS
.headerDiv {
height: 100px;
width: 400px;
background-color: blue;
color: white
}
.createdDiv {
height: 300px;
width: 400px;
background-color: black;
color: white
}
.createdDivMinimized {
height: 300px;
width: 400px;
background-color: red;
color: white
}
HTML
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="parent">
<dynamicDivs></dynamicDivs>
</div>
So what you can do is set a number in data for the number of <div> elements and use v-for with a range. For example
data () {
return {
displayDiv: [false],
divs: 1
}
}
And in your template
<template v-for="n in divs">
<div class="createdDiv">
<!-- snip -->
<button class="minimize" v-on:click="displayDiv[n-1] = !displayDiv[n-1]">Make me small</button>
</div>
<div class="createdDivMinimized" v-if="!displayDiv[n-1]">
<!-- snip -->
</div>
</template>
and in your methods...
createDiv () {
this.divs++
this.displayDiv.push(false)
}

Polymer local dom

I played around with Polymer's local dom selector $$. I want to know if an element with a certain attribute exists. I wrote below example.
Is it by intention, that I cannot reach elements within other custom elements with the $$ selector?
How can I check for the existence of elements inside anothers element?
Is it good to go with document.querySelector?
<script src="https://elements.polymer-project.org/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="https://cdn.rawgit.com/download/polymer-cdn/1.0.1/lib/polymer/polymer.html">
<dom-module id="my-moduleone">
<style>
:host {
height: 100%;
width: 100%;
display: block;
}
</style>
<template>
<div class="content-wrapper">
<content select="*" />
</div>
Counter 1: <span counter1>0</span>
<input type="button" value="Increment counter" on-click="_incrementCounter"/>
</template>
<script>
(function () {
Polymer({
is: 'my-moduleone',
_incrementCounter: function() {
this.$$('[counter1]').innerHTML++;
this.querySelector('[counter2]').innerHTML++;
this.$$('[counter3]').innerHTML++;
}
});
})();
</script>
</dom-module>
<dom-module id="my-moduletwo">
<style>
:host {
width: 100%;
display: inline-block;
}
.counter {
float: left;
height: 100px;
width: 100px;
display: block;
}
.blue {
background-color: lightblue;
}
.magenta {
background-color: magenta;
}
</style>
<template>
<content select="*" />
<span class="counter blue">
Counter 2: <span counter2>0</span>
</span>
<span class="counter magenta">
Counter 3: <span counter3>0</span>
</span>
</template>
<script>
(function () {
Polymer({
is: 'my-moduletwo'
});
})();
</script>
</dom-module>
<my-moduleone>
<my-moduletwo>
</my-moduletwo>
</my-moduleone>
You've said it yourself: this.$$ is a local dom selector. That means you cannot access elements that are added in the light dom. If you are going to access elements in the light dom (or access elements that aren't added in the local dom at registration time), use Polymer.dom instead. This is Polymer's suggested way of manipulating elements, be it in the light dom or the local dom. You also should not use document.querySelector in accessing elements that are inside the local dom of the element since this method cannot (by default) access the shadow dom.
Here is an example of using Polymer.dom:
<!doctype html>
<html>
<head>
<base href="http://polygit.org/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link href="polymer/polymer.html" rel="import">
</head>
<body>
<dom-module id="my-component-one">
<template>
<div class="content-wrapper">
<content select="*" />
</div>
Counter 1: <span counter1>0</span>
<input type="button" value="Increment counter" on-click="_incrementCounter" />
</template>
</dom-module>
<dom-module id="my-component-two">
<template>
<content select="*" />
<span class="counter blue">
Counter 2: <span counter2>0</span>
</span>
<span class="counter magenta">
Counter 3: <span counter3>0</span>
</span>
</template>
</dom-module>
<my-component-one>
<my-component-two></my-component-two>
</my-component-one>
<script>
(function() {
Polymer({
is: 'my-component-one',
_incrementCounter: function() {
// Note: Polymer.dom(this) accesses the light dom. Use Polymer.dom(this.root)
// to access the element's local dom
var componentTwo = Polymer.dom(this).querySelector('my-component-two');
if (!componentTwo) return; // don't do anything when there is no my-component-two
// access element's local dom by getting it's root property
var c2_counter2 = Polymer.dom(componentTwo.root).querySelector('[counter2]');
var c2_counter3 = Polymer.dom(componentTwo.root).querySelector('[counter3]');
var c1_counter1 = Polymer.dom(this.root).querySelector('[counter1]'); // this can be shortened to this.$$
var c1c1 = parseInt(c1_counter1.innerHTML);
var c2c2 = parseInt(c2_counter2.innerHTML);
var c2c3 = parseInt(c2_counter3.innerHTML);
c1_counter1.innerHTML = c1c1 + 1;
c2_counter2.innerHTML = c2c2 + 1;
c2_counter3.innerHTML = c2c3 + 1;
}
});
Polymer({
is: 'my-component-two'
});
})();
</script>
</body>
</html>

Is element a descendant of some class?

<div class="blah">
<div id="hi">Hi</div>
<div id="hi2">
<p id="hi3">Hi3</p>
</div>
</div>
How to test with JavaScript if an element is child (or grandchild, or grandgrandchild, etc.) of an element of class blah? (said in another way : if an element is contained in an element of class blah).
Note: After some tests, .contains(...) seems not to be the solution.
Use matches:
elt.matches('.blah *')
See https://developer.mozilla.org/en-US/docs/Web/API/Element.matches. Check browser compatibility.
See also Test if a selector matches a given element.
Modernizr can help if the browser you are targeting requires a prefix:
var ms = Modernizr.prefixed("matchesSelector", HTMLElement.prototype, elt)
ms('.blah *')
See http://modernizr.com/docs/#prefixeddom.
Loop through parents, check each one.
function isContainedByClass(src,cls) {
while(src && src.tagName) {
if( src.classList.contains(cls)) return true;
// apply old-browser compatibility as needed
src = src.parentNode;
}
return false;
}
**check this out**
============================================================================
<!DOCTYPE html>
<html>
<head>
<style>
.a *
{
display: block;
border: 2px solid lightgrey;
color: lightgrey;
padding: 5px;
margin: 15px;
}
</style>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script>
$(document).ready(function(){
//for checking if span with class c1 is children of class a element
if($('.a').children('span.c1').length > 0) {
console.log("Found");
}
//for checking if span with class c1 has parent element with class a
if($('.c1').parents('.a').length > 0) {
console.log("Found");
}
});
</script>
</head>
<body>
<div class="a">a
<div class="b">b
<div class="b1">b1</div>
<div class="b2">b2</div>
<span class="b3">b3</span>
</div>
<div class="c">c</div>
<span class="c1">c1</span>
<p class="c2">c2</p>
</div>
</body>
</html>

Categories