In this codelab, you'll learn how to build elements using LitElement. You'll create a simple LitElement element, a toggle button. The finished button will look something like this:
You'll be able to use it with simple markup like this:
<icon-toggle></icon-toggle>
This project introduces you to most of the key concepts in working with LitElement.
Don't worry if you don't understand everything. Each of the concepts presented here is described in detail in the LitElement documentation.
To start the tutorial, you will need some software:
You'll also need to have some basic skills and knowledge:
Git is a version control tool.
git --version
If all is well, Git tells you its version info:
If you don't see a Git version number git version 2.19.1
at this point, you may need to refer to the official Git installation instructions.
Node is a JavaScript runtime environment. npm is a package manager for Node. They will both be installed when you install Node.
npm install npm@latest -g
node -v
is v10.14.2
at this point
npm -v
is 6.4.1
at this point
If you don't see version numbers for Node and npm, you may need to refer to the official installation instructions on the npm website.
Clone this repo:
git clone https://github.com/Polymer-Japan/litelement-first-element.git
Change directory to your local repo and install dependencies with npm
cd litelement-first-elements npm install
Fetching the components may take some time if your internet connection is slow.
When the installation finishes, your project folder should look something like this:
The main file you'll work with is icon-toggle.js
, which contains the definition for your custom element.
Cool - you made an app! It's that easy. The app doesn't do much yet, but let's take a look at it.
npm start
The Polymer development server starts up, and opens the demo in a new browser tab. You'll see some text where the icon toggles should appear. It doesn't look very interesting, but it shows everything is working.
Next, you'll create a simple element that displays an icon.
In this step, you'll learn about:
Open icon-toggle.js
in the root on the your Editor. This file contains the skeleton of a custom element.
Start by taking a look at the existing code:
import { LitElement, html } from 'lit-element';
import '@polymer/iron-icons/iron-icons.js';
import '@polymer/iron-icon/iron-icon.js';
Key information:
import
is an ES6 Module import. This works fine for importing application-specific elements and modules.iron-icons
and iron-icon
that you'll use later in this step.Next is the definition of the element itself:
class IconToggle extends LitElement {
render() {
return html`
<style>
/* local DOM styles go here */
:host {
display: inline-block;
}
</style>
<!-- local DOM goes here -->
<span>Not much here yet.</span>
`;
}
}
Key information:
LitElement
.render
function on the element's class.render
must return an instance of TemplateResult
for lit-html. Use the html
helper function to generate an TemplateResult
instance from a JavaScript template literal. (You can import the html
helper from the lit-element.js
module.)<style>
element inside the render
lets you define styles that are scoped to the Shadow DOM, so they don't affect the rest of the document.:host
pseudo-class matches the custom element you're defining (in this case, the <icon-toggle>
). This is the element that contains or hosts the Shadow DOM tree.
window.customElements.define('icon-toggle', IconToggle);
Key information:
customElements.define
method.Now that you're familiar with the basic layout of the element, add something useful to its shadow DOM template.
Find the <span>
below the local DOM goes here
comment:
<!-- local DOM goes here -->
<span>Not much here yet.</span>
`;
Replace the <span>
and its contents with the <iron-icon>
tag below:
<!-- local DOM goes here -->
<iron-icon icon="polymer">
</iron-icon>
`;
Key information:
<iron-icon>
element is a custom element that renders an icon. Here it's hard-coded to use an icon named "polymer". There are a number of new CSS selectors to work with shadow DOM. The icon-toggle.js
file already includes a :host
selector, discussed earlier, to style the top-level <icon-toggle>
element.
To style the <iron-icon>
element, add the following CSS inside the <style>
tag after the existing content:
<style>
/* local styles go here */
:host {
display: inline-block;
}
iron-icon {
fill: rgba(0,0,0,0);
stroke: currentcolor;
}
:host([pressed]) iron-icon {
fill: currentcolor;
}
</style>
Key information:
<iron-icon>
tag uses an SVG icon. The fill
and stroke
properties are SVG-specific CSS properties. They set the fill color and the outline color for the icon, respectively.:host()
function matches the host element if the selector inside the parentheses matches the host element. In this case,[pressed]
is a standard CSS attribute selector, so this rule matches when the icon-toggle
has a pressed
attribute set on it.Your custom element definition should now look like this:
import { LitElement, html } from 'lit-element';
import '@polymer/iron-icons/iron-icons.js';
import '@polymer/iron-icon/iron-icon.js';
/**
* `icon-toggle`
* Get started creating custom elements with LitElement
*
* @customElement
* @demo demo/index.html
*/
class IconToggle extends LitElement {
render() {
return html`
<style>
/* local styles go here */
:host {
display: inline-block;
}
iron-icon {
fill: rgba(0,0,0,0);
stroke: currentcolor;
}
:host([pressed]) iron-icon {
fill: currentcolor;
}
</style>
<!-- local DOM goes here -->
<iron-icon icon="polymer">
</iron-icon>
`;
}
}
window.customElements.define('icon-toggle', IconToggle);
To display message according to the value in the host element. Please add constructor
method for setting the initial value to isFav
property.
class IconToggleDemo extends LitElement {
constructor() {
super();
this.isFav = false;
}
Reload the demo. You should see the toggle buttons show up with the hard-coded icon.
You'll notice that one toggle is styled as pressed, because the pressed
attribute is set in the demo. But click all you want, the button won't toggle yet; there's no code to change the pressed
property.
Right now, the element doesn't do much. In this step, you'll give it a basic API, allowing you to configure the icon from markup, using an attribute, or from JavaScript, using a property.
First, a bit of data binding. Find the <iron-icon>
element and change the value of the icon
attribute from "polymer"
to "${this.icon}
".
<!-- local DOM goes here -->
<iron-icon icon="${this.icon}">
</iron-icon>
Key information:
icon
is a property you'll define on the toggle button element. It doesn't have a default value yet.icon="${this.icon}"
assignment is a data binding. It links your element's icon
property with the <iron-icon>
's icon
property. You could now use your element and set the toggleIcon
property in markup or using JavaScript, as shown in the following examples. (You don't need to add this code to your project.)
<icon-toggle icon="favorite"></icon-toggle>
var myToggle = document.querySelector('icon-toggle');
myToggle.icon = "favorite";
Next, add a declaration for the icon
property.
Add the following static get properties
function to the IconToggle class:
class IconToggle extends LitElement {
static get properties() {
return {
icon: String,
};
}
Key information:
properties
getter to the element's class. The getter should return an object containing property declarations.String
).The properties
object also supports several more features. Add the following lines to add support for the pressed
property:
constructor() {
super();
this.pressed = false;
}
static get properties() {
return {
icon: String,
pressed: {
type: Boolean,
reflect: true
}
};
}
Key information:
constructor
method.reflect
property tells LitElement to Configure reflection to attributes. This lets you style the element using an attribute selector, like icon-toggle[pressed]
.Now your element has pressed
and icon
properties working.
Reload the demo, and you should see star and heart icons instead of the hard-coded icon from the previous step:
If you're curious about where the stars and hearts come from, you can take a peek at demo/icon-toggle-demo.js
and see lines like this:
<icon-toggle toggle-icon="star"></icon-toggle>
<icon-toggle toggle-icon="star" pressed></icon-toggle>
Of course, a button isn't a button if you can't click it. To toggle the button, add an event listener. To add event listeners on the iron-icon
element, add the @click
attribute to the element:
<iron-icon icon="${this.icon}" @click="${this.toggle}">
Key information:
toggle
method a click event (@event
) when the user clicks on a target with a mouse or finger.Add a handler called by the event listener.
toggle() {
this.pressed = !this.pressed;
}
attributeChangedCallback(name, oldval, newval) {
super.attributeChangedCallback(name, oldval, newval);
if(name === 'pressed') this.dispatchEvent(new CustomEvent('pressed-changed', { detail: this.pressed }));
}
Key information:
attributeChangedCallback
allows you to monitor changes in attribute values. When the value changes, it fire a pressed-changed
event and notifies the owner of the icon-toggle
element that the state has changed.Save the icon-toggle.js
file and reload the demo again. You should be able to press the button and see it toggle between its pressed and unpressed states.
You now have a button that's basically functional. But it's stuck using the existing text color for both pressed and unpressed states. What if you want to get a little flashier?
Shadow DOM helps prevent users from styling your element's internals by accident. Using custom properties, your element can expose a specific set of user-styleable properties.
You apply a custom property inside your element using the `var` function.
background-color: var(--my-custom-property, defaultValue);
Where --my-custom-property is a custom property name, always starting with two dashes (--
), and defaultValue is an (optional) CSS value that's used if the custom property isn't set.
Edit your element's <style>
tag and replace the existing fill
and stroke
values with custom properties:
<style>
/* local styles go here */
:host {
display: inline-block;
}
iron-icon {
fill: var(--icon-toggle-color, rgba(0,0,0,0));
stroke: var(--icon-toggle-outline-color, currentcolor);
}
:host([pressed]) iron-icon {
fill: var(--icon-toggle-pressed-color, currentcolor);
}
</style>
Because of the default values, you can still style the <icon-toggle>
just by setting color
, but now you have other options. Open up demo/icon-toggle-demo.js
and set the new properties:
<style>
:host {
font-family: sans-serif;
--icon-toggle-color: lightgrey;
--icon-toggle-outline-color: black;
--icon-toggle-pressed-color: red;
};
</style>
Reload the demo again to get colorful.
That's it — you're finished. You've created an element that has a basic UI, API, and custom styling properties.
If you have any trouble getting the element working, check out the finished version.