Übung 08 - Modular Web
8.1 Web-Komponenten erstellen
Einkaufsliste
import { render, html } from 'https://unpkg.com/lit-html?module';
class ShoppingList extends HTMLElement {
constructor(){
super();
let liste = [];
const addItem = () => {
let item = document.getElementById('item').value
if(item !== ""){
liste.push(item);
document.getElementById('item').value = "";
render( this.template(), this );
}
}
const clearall = () =>{
liste = [];
render( this.template(), this );
}
const myKeyup = (ev) => {
if (ev.key === "Enter") {
addItem();
document.getElementById('item').value = "";
}
}
const deleteItem = (parent) => {
liste.splice(parent, 1)
render( this.template(), this );
}
this.template = () => html`<h1>Einkaufsliste</h1>
<label for="item">Enter a new item: </label><input id="item" type="text" @keyup="${e => myKeyup(e)}">
<button id="add" @click="${addItem}">Add item</button>
<button id="clear" @click="${clearall}">Clear all</button>
<ul id="liste">${liste.map(((i, index) => html`<li id="${index}">${i} <button @click="${() => deleteItem(index)}">Delete</button></li>`))}</ul>`;
}
connectedCallback(){
render( this.template(), this );
}
}
customElements.define('shopping-list', ShoppingList);
Rednerliste
Diese Komponente ist defekt und stört die Funktionalität der Seite; deshalb wurde sie nicht eingebunden.
Sie kann aber in einem neuen Tab (s. Hyperlink im Akkordeontitel) aufgerufen werden.
Sie kann aber in einem neuen Tab (s. Hyperlink im Akkordeontitel) aufgerufen werden.
import {render, html} from 'https://unpkg.com/lit-html?module';
class Rednerliste extends HTMLElement {
constructor() {
super();
let liste = [];
let starttime;
let elapsedtime = 0;
let timerinterval;
let running;
const timeToString = (time) => {
let diffInHrs = time / 3600000;
let hh = Math.floor(diffInHrs);
let diffInMin = (diffInHrs - hh) * 60;
let mm = Math.floor(diffInMin);
let diffInSec = (diffInMin - mm) * 60;
let ss = Math.floor(diffInSec);
let diffInMs = (diffInSec - ss) * 100;
let ms = Math.floor(diffInMs);
let formattedMM = mm.toString().padStart(2, "0");
let formattedSS = ss.toString().padStart(2, "0");
let formattedMS = ms.toString().padStart(2, "0");
return `${formattedMM}:${formattedSS}:${formattedMS}`;
}
const start = (id) => {
if (running !== undefined) {
stop(running);
}
running = id;
starttime = Date.now() - liste[id].time;
timerinterval = setInterval(function printtime() {
elapsedtime = Date.now() - starttime;
document.getElementById(id).innerHTML = timeToString(elapsedtime)
}, 10);
const btn = document.getElementById(id).nextElementSibling;
btn.style.display = "none";
btn.nextElementSibling.style.display = "inline";
}
const stop = (id) => {
console.log("stop", document.getElementById(id).nextElementSibling)
clearInterval(timerinterval);
liste[id].time = elapsedtime;
const btn = document.getElementById(id).nextElementSibling;
btn.style.display = "inline";
btn.nextElementSibling.style.display = "none";
}
const addItem = async () => {
console.log("addItem")
let redner = {name: document.getElementById('item').value, time: 0}
liste.push(redner);
document.getElementById('item').value = "";
if (liste.length !== 0) {
if (running !== undefined) {
stop(running);
}
render(this.template(), this);
console.log("nach rendern")
start(liste.length - 1);
}
}
const clearall = () => {
if(running !== undefined){
stop(running);
running = undefined;
}
liste = [];
render(this.template(), this)
}
const myKeyup = (ev) => {
if (ev.key === "Enter") {
addItem();
document.getElementById('item').value = "";
}
}
this.template = () => html`<h1>Rednerliste</h1>
<label for="item">Neuer Redner </label><input id="item" @keyup="${ev => myKeyup(ev)}" type="text">
<button @click="${addItem}">Add item</button>
<button @click="${clearall}">Clear all</button>
<ul id="liste">
${liste.map(((i, index) => html`
<li>${i.name} <span id="${index}">${timeToString(i.time)}</span>
<button @click="${() => start(index)}">Start!</button>
<button @click="${() => stop(index)}" style="display: none">Stop!</button>
</li>`))}
</ul>`;
}
connectedCallback() {
render(this.template(), this);
}
}
customElements.define('redner-liste', Rednerliste);
Tabellenkalkulation
<script type="module">
import {html, render} from 'https://unpkg.com/lit-html?module';
class TabellenCalc extends HTMLElement {
constructor() {
super();
let formula;
const createTable = () => {
var reihen = document.getElementById("reihen").value;
let itemtemplates = [];
for (let i = 1; i <= reihen; i++) {
itemtemplates.push(html`<tr><td>${toAbc(i)} = </td><td contenteditable="true" id="${toAbc(i)}2" @blur="${() => calc(false)}"></td>`);
}
itemtemplates.push(html`<tr><td>Sum = </td><td contenteditable="true" id="sumfield" @blur="${() => calc(true)}"></td></tr>`);
this.tablehtml = () => html`<table id="table">${itemtemplates}</table>`
render(this.tablehtml(), document.getElementById("tablewrapper"))
calc();
}
const calc = (b) => {
if (b) formula = document.getElementById("sumfield").innerHTML.toLowerCase();
if (formula) {
const f = formula.slice(1, 4);
const z = parseInt(document.getElementById(formula.slice(5, 7)).innerHTML);
const w = parseInt(document.getElementById(formula.slice(8, 10)).innerHTML);
document.getElementById("sumfield").innerHTML = eval(f)(z, w);
}
}
const toAbc = (i) => {
return ((i % 26) + 9).toString(36);
}
const sum = (x, y) =>{
return x + y;
}
const mul = (x, y) => {
return x * y;
}
const sub = (x, y) => {
return x - y;
}
const div = (x, y) => {
return x / y;
}
this.template = () => html`<h1>Tabellenkalkulation mit contentEditable</h1>
<p>Moegliche Funktionen: sum, sub, mul, div nach dem Schema =SUM(A2,B2)</p>
<h3>Wie gross soll die Tabelle sein?</h3>
<label for="reihen">Reihen: </label><input id="reihen" min="1" required type="number">
<button @click="${createTable}" type="submit">Erstelle Tabelle</button>
<br><br>
<div id="tablewrapper"></div>`;
}
connectedCallback() {
render(this.template(), this);
}
}
customElements.define('tabellen-comp', TabellenCalc);
</script>
<style>
table, td {
border: 1px solid black;
}
table {
width: 25%;
border-collapse: collapse;
}
td {
width: 50%;
height: 16px;
text-align: right;
}
</style>
Statistik-Balkendiagramm
<script type="module">
import {LitElement, svg, css} from 'https://unpkg.com/lit-element/lit-element.js?module';
customElements.define('statistik-comp', class extends LitElement {
constructor() {
super();
this.data = {
"spd": {
name: "SPD",
sitze: 206,
color: "red"
},
"cdu/cdsu": {
name: "CDU/CSU",
sitze: 197,
color: "black"
},
gruen: {
name: "Die Gruenen",
sitze: 118,
color: "green",
},
fdp: {
name: "FDP",
sitze: 92,
color: "yellow",
},
afd: {
name: "AfD",
sitze: 82,
color: "#4d2600",
},
linke: {
name: "Die Linke",
sitze: 39,
color: "#e60e98",
},
franktionslos: {
name: "franktionslos",
sitze: 2,
color: "grey"
}
}
this.y = 30
}
static get properties() {
return {
data: {type: Object},
y: {type: Number}
}
}
static get styles(){
return css`
.bar{
animation-name: barAnim;
animation-duration: 1.5s;
animation-iteration-count: 1;
animation-timing-function: ease;
animation-play-state: running;
}
@keyframes barAnim {
0%{
width: 0;
}
}
`
}
render() {
return svg`<svg height="500" id="dia" width="600" xmlns="http://www.w3.org/2000/svg">
<line x1="99" y1="25" x2="99" y2="240" stroke-width="2" stroke="#85adad" /><text x="95" y="260">0</text>
<line x1="200" y1="25" x2="200" y2="240" stroke-width="2" stroke="#85adad" /><text x="192" y="260">50</text>
<line x1="300" y1="25" x2="300" y2="240" stroke-width="2" stroke="#85adad" /><text x="288" y="260">100</text>
<line x1="400" y1="25" x2="400" y2="240" stroke-width="2" stroke="#85adad" /><text x="388" y="260">150</text>
<line x1="500" y1="25" x2="500" y2="240" stroke-width="2" stroke="#85adad" /><text x="488" y="260">200</text>
<text font-family='Arial' font-size="12" x="350" y="280">Sitze im deutschen Parlament</text>
${Object.values(this.data).map((d, index) => svg`<text font-family='Arial' font-size='14' x='10' y=${this.y+(30*index) + 15}>${d.name}</text>
<rect class="bar" fill='${d.color}' height='20' width='${d.sitze * 2}' x='100' y='${this.y+(30*index)}'/>
<text font-family='Arial' font-size='12' font-weight="bold" x='${110 + d.sitze * 2}' y='${this.y +(30*index)+ 15}'>${d.sitze}</text>`)}
</svg>`;
}
})
</script>
Bezier-Animation
<script type="module">
import {LitElement, svg, css} from 'https://unpkg.com/lit-element/lit-element.js?module';
customElements.define('bezier-comp', class extends LitElement {
constructor() {
super();
}
static get styles() {
return css`
.draggable {
cursor: move;
}
.static {
cursor: not-allowed;
}`
}
render() {
return svg`<svg height="100%" id="svg" style="margin-left: auto; margin-right: auto; display: block;" width="100%">
<circle class="draggable" cx="220" cy="60" fill="blue" id="blue" r="20"
@mousedown="${evt => {this.startDrag(evt)}}" @mousemove="${evt => {this.drag(evt)}}" @mouseup="${_ => {this.endDrag()}}" @mouseleave="${_ => {this.endDrag()}}"></circle>
<circle class="draggable" cx="102" cy="233" fill="red" id="red" r="20"
@mousedown="${evt => {this.startDrag(evt)}}" @mousemove="${evt => {this.drag(evt)}}" @mouseup="${_ => {this.endDrag()}}" @mouseleave="${_ => {this.endDrag()}}"></circle>
<circle class="draggable" cx="50" cy="110" fill="green" id="green" r="20"
@mousedown="${evt => {this.startDrag(evt)}}" @mousemove="${evt => {this.drag(evt)}}" @mouseup="${_ => {this.endDrag()}}" @mouseleave="${_ => {this.endDrag()}}"></circle>
<g id="lines">
</g>
</svg>`;
}
updated(){
this.draw();
}
draw() {
const green = this.shadowRoot.getElementById('green');
const blue = this.shadowRoot.getElementById('blue');
const red = this.shadowRoot.getElementById('red');
let path = `M${red.getAttribute("cx")},${red.getAttribute("cy")} L${green.getAttribute("cx")},${green.getAttribute("cy")}
L${blue.getAttribute("cx")},${blue.getAttribute("cy")} M${red.getAttribute("cx")},${red.getAttribute("cy")}
Q${green.getAttribute("cx")},${green.getAttribute("cy")} ${blue.getAttribute("cx")},${blue.getAttribute("cy")}`
this.shadowRoot.getElementById("lines").innerHTML = `<path class="static" d="${path}" stroke="black" stroke-width="3" fill="none"/>`
}
selectedElement; offset;
startDrag(evt) {
console.log("drag")
if (evt.target.classList.contains('draggable')) {
this.selectedElement = evt.target;
this.offset = this.getMousePosition(evt);
this.offset.x -= parseFloat(this.selectedElement.getAttribute("cx"));
this.offset.y -= parseFloat(this.selectedElement.getAttribute("cy"));
}
}
drag(evt) {
console.log("drag")
if (this.selectedElement) {
evt.preventDefault();
const coord = this.getMousePosition(evt);
this.selectedElement.setAttribute("cx", coord.x - this.offset.x);
this.selectedElement.setAttribute("cy", coord.y - this.offset.y);
}
this.draw();
}
endDrag(evt) {
this.selectedElement = null;
this.draw();
}
getMousePosition(evt) {
const CTM = this.shadowRoot.querySelector('svg').getScreenCTM();
return {
x: (evt.clientX - CTM.e) / CTM.a,
y: (evt.clientY - CTM.f) / CTM.d
};
}
})
</script>
8.2 LitElement Menü-Komponente
Anwendung durch: <menu-comp alignment="block" listelements='["block_1", "block_2", "block_3"]'></menu-comp>
<script type="module">
import {LitElement, html, css} from 'https://unpkg.com/lit-element/lit-element.js?module';
customElements.define('menu-comp', class extends LitElement {
constructor() {
super();
this.listelements = [];
this.alignment = "";
this.buttonstyle = "";
}
static get properties() {
return {
listelements: {type: JSON},
alignment: {type: String},
buttonstyle: {type: String}
}
}
static get styles() {
return css`
button {
background-color: #6a709f;
color: black;
border-top-color: white;
border-left-color: white;
border-bottom-color: #9a9a9a;
border-right-color: #9a9a9a;
border-radius: 20px;
font-weight: bold;
padding: 0.3em 2em;
margin: 1em;
}
button:hover {
background-color: antiquewhite;
}`
}
render() {
return html`
${JSON.parse(this.listelements).map((e) => html`
<button style="display:${this.alignment}; margin: 10px">${e}</button>`)}
`
}
})
</script>
8.3 LitElement WWW-Navigator
Footer-Component<script type="module"> import {LitElement, html, css} from 'https://unpkg.com/lit-element/lit-element.js?module'; customElements.define('footer-comp', class extends LitElement { constructor() { super(); } static get styles() { return css`footer { background: black; }` } render() { return html` <footer> <span style="font-size: 30px">Footer:</span> <a href="">Sitemap</a> <a href="">Home</a> <a href="">News</a> <a href="">Contact</a> <a href="">About</a></footer> `; } }) </script>
Header-Component<script type="module"> import {LitElement, html, css} from 'https://unpkg.com/lit-element/lit-element.js?module'; customElements.define('header-comp', class extends LitElement { constructor() { super(); } static get styles() { return css` header { background-color: #c14f4f; text-align: left; } h1 { text-align: center; margin: 0; } button { background-color: #6a709f; color: black; border-top-color: white; border-left-color: white; border-bottom-color: #9a9a9a; border-right-color: #9a9a9a; border-radius: 20px; font-weight: bold; padding: 0.3em 2em; margin: 1em; } button:hover { background-color: antiquewhite; }` } render() { return html` <header> <h1>WWW-Navigator</h1> <button id='html' @click="${() => topnav('html')}">HTML</button> <button id='css' @click="${() => topnav('css')}">CSS</button> <button id='javascript' @click="${() => topnav('javascript')}">JavaScript</button> <button>Other</button> </header>` } }) <script>
Content-Component<script type="module"> import {LitElement, html, css} from 'https://unpkg.com/lit-element/lit-element.js?module'; customElements.define('content-comp', class extends LitElement { constructor() { super(); } static get styles() { return css` .content { display: flex; flex-direction: column; overflow-y: auto; height: 100vh; } .left-sidebar { background-color: #c28281; } .left-sidebar button { display: block; } .right-sidebar { text-align: left; background-color: #c28281; } .container { display: flex; width: 100%; flex-direction: column; height: 100vh; } main { background-color: #6a9ebd; font-size: 1.5rem; color: black; } main a { font-size: 1.5rem; color: black; } button { background-color: #6a709f; color: black; border-top-color: white; border-left-color: white; border-bottom-color: #9a9a9a; border-right-color: #9a9a9a; border-radius: 20px; font-weight: bold; padding: 0.3em 2em; margin: 1em; } button:hover { background-color: antiquewhite; } a { color: white; font-size: small; } @media all and (min-width: 768px) { .content { flex-direction: row; flex-wrap: wrap; } main { flex: 5; order: 2; } .left-sidebar { order: 1; flex: 1; font-size: 2em; } .right-sidebar { flex: 1; order: 3; font-size: 2em; } }` } render() { return html` <content class="content"> <aside class="left-sidebar" style="display: block"></aside> <main id="main"></main> <aside class="right-sidebar"> <h6>External resources:</h6> <div id="urls"></div> </aside> </content>` } }) </script>
navigator.html<header-comp></header-comp> <content-comp></content-comp> <footer-comp></footer-comp>