KMI/WEBA Webové aplikace: Seminář 06

JavaScript

Jazyk JavaScript (JS) je základní webová technologie, která umožňuje manipulovat s webovou stránkou na straně klienta (přesněji řečeno s DOM). Později ukážeme, že toho umí mnohem více, nyní se zaměříme na základy klientského JavaScriptu.

// rozšíření syntaxe (budeme potřebovat později)

// proměnné
var x = true;
x = false;
console.log(x);

// konstantní proměnné
const x = true;
x = false; // způsobí chybu

// operátor let, lexikální rozsah platnosti na úrovni bloku
var x = 42;

if (x) {
  var x = 0;
  console.log(x); // vypíše 0
}

console.log(x); // vypíše 0!

// varianta s let
var x = 42;

if (x) {
  let x = 0; // omezení rozsahu platnosti na blok
  console.log(x); // vypíše 0
}

console.log(x); // vypíše 42

// analogicky pro cyklus for

// template string

// old way
console.log(fisrtName + ", " + lastName);

// new way
console.log(`${fisrtName}, ${lastName}`); // pozor na znak `


// funkce
// výchozí parametr funkce
function show(x = 42) {
  console.log(x);
}

show(); // 42

// šipkový operátor (anonymní funkce)
const log = x => console.log(x);
log(42);

// při vrácení objektů je třeba navíc ()
const user = (firstName, lastName) => ({givenname: firstName, surname: lastName});
console.log(user("Samantha", "Carter"));

// object literal enhancement (nový způsob vytváření objektů)

var givenname = "Samantha";
var surname = "Carter";

const user = {
  givenname,
  surname,
  log() {
    console.log(`${this.givenname}, ${this.surname}`);
  }
};

user.log();

// destrukturalizace objektu
const blah = {test1: 42, test2: 43};

var {test1} = blah;
console.log(test1);

Praktické ukázky

Změna vzhledu

// změna vlastnosti
const element = document.querySelector(".box"); // vrací první nalezený
element.style.color = "red";

// přidání a odebrání třídy
element.classList.add("box--marked");
element.classList.remove("box--marked");

// více elementů
var color = "blue";

const elements = document.querySelectorAll(".box");
elements.forEach(e => e.style.color = color);

Validace formuláře

Základní validace je poskytována na úrovni prohlížeče. Například atribut required. Tato validace je poskytována formou API a JS se na toto API umí napojit. Uvažme následující vstupní pole.

<form method="post" action="">
  <label for="mail">E-mail address:</label>
  <input type="email" id="mail" name="mail">

  <button type="submit">Submit</button>
</form>

Napojení na validaci je ukázáno v kódu níže.

const email = document.getElementById("mail");
const form = document.querySelector("form");

// validace při psaní
email.addEventListener("input", e => {
  if (email.validity.typeMismatch) {
    email.setCustomValidity("I am expecting an e-mail address!");
    email.reportValidity();
  } else {
    email.setCustomValidity("");
  }
});

// validace při odeslání
form.addEventListener("submit", e => {
  if (!email.value) {
    showError(email);
    event.preventDefault(); // zabránění odeslání formuláře
  }
});

// pomocná funkce
function showError(e) {
  e.style.backgroundColor = "#ffcccb"; // vestvěné hlášky sami zmizí
  e.setCustomValidity("E-mail have to be entered!");
  e.reportValidity();
}

// poznámka:  lze snadno rozšířit, třeba přidáním span s display: none za input a například
// e.nextElementSibling.style.display="inline";

Zcela analogicky je možné vytvářet validaci vlastní.

Modální okno s potvrzením

Následující kódy ukazují možné řešení vyvolání modálního okna, které obsahuje žádost o potvrzení akce. Uvedené řešení je jedno z mnoha možných. Zcela univerzání řešení neexistuje.

<!-- tlačítka -->
<button class="button button--delete" data-action="users/delete/1">Delete forever</button>
<button class="button button--delete" data-action="users/delete/2">Delete forever</button>
<button class="button button--delete" data-action="users/delete/3">Delete forever</button>

<!-- modální dialogové okno -->
  <dialog id="dialog">
  <p id="dialog__text">Do you really want to delete item <span id="dialog__item-to-delete">X</span>? This action cannot be undone.</p>
  <a href="" id="dialog__confirm-link">yes</a>
  <a href="#" onclick="closeDeleteDialog()">no</a>
</dialog>
// najdeme všechny delete tlačítka
const deleteButtons = document.querySelectorAll(".button--delete");
  
// navázání události
deleteButtons.forEach(b => b.addEventListener("click", e => {
  const dialog = document.getElementById("dialog");
  const action = b.dataset.action; // data z data-* atributu
  
  // parsování id z data atributu
  const item = document.getElementById("dialog__item-to-delete");
  item.innerHTML = action.split("/").pop();;
  
  // změna linku
  const link = document.getElementById("dialog__confirm-link");
  link.setAttribute("href", action);
  
  dialog.showModal();
}));
  
// zavření dialogového okna  
function closeDeleteDialog() {
  const dialog = document.getElementById("dialog");
  dialog.close();
}

Mizející notifikace

// najdeme všechny notifikace
const notifications = document.querySelectorAll(".notification");

// po 3s je odstraníme
setTimeout(() => {notifications.forEach(n => n.style.display="none")}, 3000);

// nebo fadeout efekt
var opacity = 1;
var fadeOutInterval = setInterval(() => {
  opacity = opacity - 0.01;
  notifications.forEach(n => {
    n.style.opacity = opacity;
    if (opacity <= 0) {
      n.style.display = "none";
      clearInterval(fadeOutInterval);	
    }
  })}, 30);


// případně hezčí a čistější řešení pomocí rekurze
const fadeOutEffect = (opacity = 1, delay = 30, decrement = 0.01) => {
  const notifications = document.querySelectorAll(".notification");

  if (opacity > 0) {
    notifications.forEach(n => n.style.opacity = opacity);
    setTimeout(() => fadeOutEffect(opacity - decrement, delay, decrement), delay);	
  }
  else {
    notifications.forEach(n => n.style.display = "none");
  }
};

fadeOutEffect();

// poznámka: stejně tak je možné funkci spustit přes IIFE (() => {})();

Kam dál

Možnosti JS jsou velmi rozsáhlé. V případě jednoduchých webových stránek se velmi často opakují běžné uživatelské prvky. Například rozklikávací menu, modální okna, úpravy UI dle požadavků, fotogalerie, slideshow a další. Dodejme, že existuje řada JS knihoven, které usnadňují vytvaření, nebo přímo obsahují uvedené prvky. Jedna z nejuniverzálnějších knihoven je knihovna jQuery. Ta již není tak populární, jako před několika lety, ale stále se hojně používá. Současným trendem je využívání menších jednoúčelových knihoven a celkové odlehčení JS, pokud je to možné.

Zadání

  1. úkol 1

    V administračním rozhraní z minulých kapitol přidejte: validaci na straně klienta; dotaz při mazaní; a notifikaci o úspěšném vložení, která automaticky zmizí.