Mozilla says Web components consist of three main technologies:
Custom elements
Shadow DOM
HTML templates
Is number 3, "HTML templates", even necessary in light of ECMAscript's Template Literals?
Look at this example I got from James Milner:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Web Component</title>
<script type="text/javascript">
// We define an ES6 class that extends HTMLElement
class CounterElement extends HTMLElement{
constructor() {
super();
// Initialise the counter value
this.counter = 0;
// We attach an open shadow root to the custom element
const shadowRoot= this.attachShadow({mode: 'open'});
// We define some inline styles using a template string
const styles=`
:host {
position: relative;
font-family: sans-serif;
}
#counter-increment, #counter-decrement {
width: 60px;
height: 30px;
margin: 20px;
background: none;
border: 1px solid black;
}
#counter-value {
font-weight: bold;
}
`;
// We provide the shadow root with some HTML
shadowRoot.innerHTML = `
<style>${styles}</style>
<h3>Counter</h3>
<slot name='counter-content'>Button</slot>
<button id='counter-increment'> - </button>
<span id='counter-value'> 0 </span>
<button id='counter-decrement'> + </button>
`;
// We can query the shadow root for internal elements
// in this case the button
this.incrementButton = this.shadowRoot.querySelector('#counter-increment');
this.decrementButton = this.shadowRoot.querySelector('#counter-decrement');
this.counterValue = this.shadowRoot.querySelector('#counter-value');
// We can bind an event which references one of the class methods
this.incrementButton.addEventListener("click", this.decrement.bind(this));
this.decrementButton.addEventListener("click", this.increment.bind(this));
}
increment() {
this.counter++
this.invalidate();
}
decrement() {
this.counter--
this.invalidate();
}
// Call when the counter changes value
invalidate() {
this.counterValue.innerHTML = this.counter;
}
}
// This is where the actual element is defined for use in the DOM
customElements.define('counter-element', CounterElement);
</script>
</head>
<body>
<counter-element></counter-element>
</body>
</html>
Notice how he doesn't use an HTML template, but instead uses an ecmascript template literal to set the innerHTML of the shadowRoot.
After this, he uses querySelector to get internal elements of the shadowRoot and he ultimately adds event listeners to the increment and decrement buttons.
If you were to use a HTML template, instead of an ecmascript template literal, what does this gain you?
Conceptually, I'm struggling to find a situation where I'd prefer an HTML Template Element over an Ecmascript Template Literal.
Please advise.
The template tag is not 'required' for Web Components per se. It probably made more sense when HTML Imports were being pushed, allowing for importing and reusing HTML snippets, but that has since ceased. Here you could have imported a template and reused that.
It's important to note the specifications are designed to be standalone and can be used interdependently of each other also, which makes them versatile. The HTML tag has use cases outside of the realm of Web Components; it's useful because it allows you to define a piece of markup that doesn't render until instantiated via JavaScript later on. Indeed you can use templates without using any of the other specifications (Custom Elements, Shadow DOM etc).
The template tag can certainly be used in conjunction with the other specs. For example, we could have used it in the example shown to arguably make the code less imperative and more markup focused like so:
<template id="counterTemplate">
<style>
:host {
position: relative;
font-family: sans-serif;
}
#counter-increment, #counter-decrement {
width: 60px;
height: 30px;
margin: 20px;
background: none;
border: 1px solid black;
}
#counter-value {
font-weight: bold;
}
</style>
<h3>Counter</h3>
<slot name='counter-content'>Button</slot>
<button id='counter-increment'> - </button>
<span id='counter-value'> 0 </span>
<button id='counter-decrement'> + </button>
</template>
And then use this later in JavaScript like so:
const template = document.querySelector('#counterTemplate');
const counter = document.cloneNode(template);
shadowRoot.appendChild(counter);
The downside here is that it would require that the template existed in the DOM prior to the instantiation as it is relying on the #counterTemplate template being there. In some ways this makes the Custom Element less portable, hence why template literal might be more desirable. I haven't tested the performance of both, but my gut tells me that the template would possibly be more performant.
Disclaimer: I wrote the original blog post
Related
I was looking at styled-components and I saw this syntax:
const Title = styled.h1`
font-size: 1.5em;
text-align: center;
color: palevioletred;
`;
I can't understand what is going on under the hood and what property is actually applied to the styled object.
Is there anyone that can explain to me how this code even runs?
Read about tagged templates
This is basically a function, but you can run it without ()
const styled = data => {
return data + ' JavaScript';
};
const data = styled `I love`;
console.log(data);
Examples with built-in functions:
console.log('a b c'.split ` `)
console.log(Object.entries `abc`)
console.log([1, 2, 3].concat `abc`)
You create styled h1 element, for example in Header component :
const Title = styled.h1`
font-size: 2rem;
text-align: center;
color: blue;
`;
You use it like a <h1> tag but with custom name:
<Title>My portfolio</Title>
You finally get the static hash class name sc-<hashedStringName> and one is dynamic class:
<h1 class="sc-gsnERi fiwDZi">My portfolio</h1>
So styled-components:
generate static class name
generate dynamic class name
This basically allows you to create Custom Tag in JSX.
You can also pass props to the styling properties in styled-components.
If you have this jsx:
<Title>This is Custom H1</Title>
const Title = styled.h1`
color: red;
`
It will render HTML like this:
<h1 class="someRandomAlphabet">This is Custom H1</h1>
where the class of h1 tag will have these properties:
.someRandomAlphabet{
color: red;
}
I have a CSS Grid Layout with no preset columns or rows because the number of items to be added is unknown beforehand. I use JS to create DOM elements and set their column and row numbers. Later, I want to update specific spots in the grid with a new element; before doing so I want to check if there is an element already at that position in the grid and, if so, retrieve and remove it.
To do that I'm looking for something along the lines of a 'containerElement.getElements().gridOrder()' type of deal which would return children elements in an array (or 2D array).
Perhaps I'm looking in the wrong place but I haven't seen anything like this on MDN. Do I need to loop through child elements and use their 'style.gridColumn' and 'style.gridRow' properties to manually sort things?
Here is sample code showing how I'm dynamically adding items to a grid:
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<title>test page</title>
<style>
.box {
width: 30px;
height: 30px;
border-radius: 5px;
background-color: rgb(207,232,220);
margin: 5px;
}
.wrapper {
display: grid;
grid-gap: 20px;
background-color: rgb(79,185,227);
color: #fff;
overflow: auto;
}
</style>
<script>
function run(){
let con = document.getElementsByClassName('wrapper')[0];
for(let i=1; i<10; i++){
let e = document.createElement('div');
e.className = 'box';
e.id = i+"";
e.style.gridColumn = i;
e.style.gridRow = i;
con.appendChild(e);
}
}
</script>
</head>
<body onLoad='run()'>
<h1>Grid</h1>
<div class="wrapper">
</div>
</body>
</html>
I did not find any further information relating to my question so I went ahead with my own suggestion in the post; I just used a standard 2D array to track which spaces were filled or not.
Since arrays in JavaScript are dynamic this only requires checking if a slot contains an existing array for a row-- adding one if it doesn't --and then adding the item at the specified location.
I'm new to Vaadin and trying to create an instance that hides the vaadin-text-field from the component vaadin-date-picker.
I started out by reading the documentation for vaadin-date-picker about the shadow DOM property stated here.
I tried with "Scoping Styles in a Theme Module" but the whole thing including the calendar icon disappeared.
Current code as below,
render() {
return html`
<dom-module id="trim-inputbox" theme-for="vaadin-date-picker">
<template>
<style>
:host(.special_field) [part="text-field"] {
visibility:hidden;
}
</style>
</template>
</dom-module>
<vaadin-date-picker class="special_field"></vaadin-date-picker>
`;
}
Thanks so much again for any kind help.
As you noticed already a calendar icon is part of a text-field itself.
In Styling section there is an example of using <vaadin-date-picker-light>:
<style>
.my-input2 input {
border: none;
font-size: 14px;
background: none;
}
</style>
<vaadin-date-picker-light>
<div class="my-input2">
<iron-icon icon="event"></iron-icon>
CHECK-IN:
<iron-input>
<input size="10">
</iron-input>
</div>
</vaadin-date-picker-light>
Maybe you could use this instead?
I need to change dynamically my css properties. I know that the right way to do it is with: document.getElementById("XXXX").style.fontSize = "xx-large";
However, when I have this css:
#year1 .subdomain-text {
fill: #000;
font-size: 6px;
}
I haven't been able to find the way to access it, and I have tried any possible permutation (nothing works!):
document.getElementById("year1.subdomain-text").style.fontSize = "xx-large";
document.getElementById("year1").style.fontSize = "xx-large";
document.getElementById("subdomain-text").style.fontSize = "xx-large";
document.getElementById("#year1.subdomain-text").style.fontSize = "xx-large";
....
Any idea how to make it happen?
Thanks!
You are using .getElemenByID() which means you specify just the ID name no need to include the class name i.e. document.getElementById("year1").style.fontSize = "xx-large";
Example here :http://jsfiddle.net/Nb7wy/1770/
document.getElementById retrieves html element by its id attribute. In your case, it should be:
document.getElementById("year1")
If the id is unique, as expected, the class is not necessary for the selector in the css rule either. You could just remove the class from your css rule.
For other cases where combining selectors is needed, it can be used document.querySelector(css_selector) (instead of document.getElementById).
Working code:
<html>
<head>
<style>
#year1 .subdomain-text {
fill: #000;
font-size: 6px;
}
</style>
</head>
<body>
<p id="year1" class="subdomain-text">Texto prueba</p>
<input type="button" onclick="test();" />
<script>
function test() {
document.getElementById("year1").style.fontSize = "xx-large";
}
</script>
</body>
</html>
I am working on making a number list with each number on its individual div. So far I am able to remove the div with Javascript (on click), but I would like to enable JQuery so that I am able to add a class to a div and then remove all divs of that class with a button or something like that.
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=203">
<title>Lista Mundial</title>
<style>
.divContainer {
width: 35px;
height: 25px;
border: solid 1px #c0c0c0;
background-color: #e1e1e1;
font-family: verdana;
float: left;
}
.text {
font-size: 15px;
font-family: verdana;
color: black;
margin-top: 4px;
}
h4 {
font-family: Verdana;
color: black;
text-decoration: none;
}
</style>
</head>
<body>
<h4>Double click para borrar un numero</h4>
<script type="text/javascript">
for(var i = 1; i <= 639; i++){
var divTag = document.createElement("div");
divTag.id = i;
divTag.className = "divContainer";
document.body.appendChild(divTag);
divTag.ondblclick = function(){this.parentNode.removeChild(this)};
var pTg = document.createElement("p");
pTg.setAttribute("align", "center");
pTg.className = "text";
pTg.innerHTML = (i);
document.getElementById(i).appendChild(pTg);
}
</script>
</body>
</html>
http://jsfiddle.net/ramonfabrega/AZSy8/
For simplicity, I just tried hiding the div's clicked, but JQuery does not seem to work. So something must be off.
Two issues:
1) jQuery wasn't loaded.
2) You were trying to bind the click event on an invalid selector (divTag instead of div)
Here's an updated fiddle: http://jsfiddle.net/LFC3A/2/
Regarding #2 - jQuery allows you to select an element multiple ways. The most common is to use a selector. The majority of selectors jQuery supports are from CSS 1 - 3, though jQuery supports some of its own custom selectors (such as div:eq, div:gt, and so on...) Check out the selectors page here: http://api.jquery.com/category/selectors/
Now, if your markup was:
<body>
<divTag>My Custom Div Tag</divTag>
<div>My regular DIV</div>
</body>
Then your original fiddle would have worked. In fact, here's an updated fiddle demonstrating that: http://jsfiddle.net/FpMAw/ (I updated your createElement to return a custom element, divTag)
The other way of accessing jQuery is by passing it a DOM element. Something like:
var $body = $(document.body) is equivalent to var $body = $('body')
If you reference that, you now have a jQuery object with a bunch of useful helper methods. So, in our previous example, we can now do:
$body.css('color', 'red')
Hopefully this helps explain a bit more why it didn't work. If you have any other questions, feel free to ask :)
Fiddle Demo
you are not including jQuery library in the fiddle
change $('divTag') to $('div')
Read $( "element" )
$(document).ready(function () {
$('div').click(function () {
$(this).hide();
});
});
Start Learning
jQuery API Documentation
This will create and add a click handler at the same time.
$('<div>').click(function(e){ this.addClass('active');})