Kliensoldali webprogramozás
Horváth Győző
Egyetemi docens
1117 Budapest, Pázmány Péter sétány 1/c., 4.725-as szoba
horvath.gyozo@inf.elte.hu
<form action="" method="get">
<input type="password" name="password1" value="secret1" data-show-password>
<input type="password" name="password2" value="secret2" data-show-password>
<button>Submit</button>
</form>
<style></style>
class ShowPassword {
// ...
}
document.querySelectorAll('[data-show-password]').forEach(
el => new ShowPassword(el))
<show-password>
<input type="password" name="password1" value="secret">
</show-password>
<input type="password" name="password1" value="secret" is="show-password">
Referenciatípusok
Destructuring és spread operátorok
// const reference, content dynamic
const x = []
x.push(10)
// copy reference
const x1 = [1, 2, 3]
const x2 = x1
x2[1] = 20
console.log(x1) // --> [1, 20, 3]
// shallow copy
const x3 = [1, 2, 3]
const x4 = x3.slice()
// const x4 = x3.concat()
x4[1] = 20
console.log(x3) // --> [1, 2, 3]
const m1 = [
[1, 2, 3],
[4, 5, 6],
]
const m2 = m1.concat()
m2[0][1] = 20
console.log(m1[0]) // --> [1, 20, 3]
// deep copy
const m3 = [
[1, 2, 3],
[4, 5, 6],
]
const m4 = JSON.parse(JSON.stringify(m3))
m4[0][1] = 20
console.log(m3[0]) // --> [1, 2, 3]
const numbers = [1, 2, 3, 4, 5];
const a = numbers[0];
const b = numbers[1];
// instead destructuring
const [a, b] = numbers;
// default values
const [a = 10, b = 20] = [100] // a:100, b:20
// rest
const [a, b, ...rest] = numbers; // --> rest:[3, 4, 5]
// swapping variables
[a, b] = [b, a]
// ignoring
const [a,,b] = numbers; // a:1, b:3
// spread
const a = [1, 2, 3];
const b = [9, ...a, 10]; // b:[9, 1, 2, 3, 10]
// const reference, content dynamic
const o = {}
o.field1 = 12
// copy reference
const o1 = { field1: 2 }
const o2 = o1
o2.field1 = 20
console.log(o1) // --> { field1: 20 }
// shallow copy
const o3 = { field1: 2 }
const o4 = {}
for (let key in o3) {
o4[key] = o3[key]
}
// Object.assign(o4, o3)
o4.field1 = 20
console.log(o3) // --> { field1: 2 }
const n1 = {
field1: { subfield1_1: 1 },
field2: { subfield2_1: 2 },
}
const n2 = Object.assign({}, n1)
n2.field2.subfield2_1 = 20
console.log(n1.field2) // --> { subfield2_1: 20 }
// deep copy
const n3 = {
field1: { subfield1_1: 1 },
field2: { subfield2_1: 2 },
}
const n4 = JSON.parse(JSON.stringify(n3))
n4.field2.subfield2_1 = 20
console.log(n3.field2) // --> { subfield2_1: 2 }
const o = {
a: 42,
b: 28,
}
const a = o.a
const b = o.b
// instead destructuring
const {a, b} = o;
// renaming
const {a: c, b: d} = o;
// default values
const {a = 10, b = 20} = {a: 42};
const {a: c = 10, b: d = 20} = {a: 42};
// rest
const o = {
a: 42,
b: 28,
c: 12
};
const {a, ...rest} = o; // rest={b:28, c:12}
// nested objects
const david = {
kor: 4,
cim: {
iranyitoszam: '1241',
varos: 'Budapest',
utca: 'Egyszervolt utca',
hazszam: 63
}
};
const { cim: { utca }} = david
// Függvénydeklaráció
function multiply(a, b) {
return a * b
}
// Névtelen függvénykifejezés
const multiply = function(a, b) {
return a * b
}
// Nevesített függvénykifejezés
const multiply = function multiply(a, b) {
return a * b
}
// Arrow function szintaxis
const multiply = (a, b) => a * b
// Function konstruktor: dinamikus függvények, alapvetően kerülendő
const multiply = new Function('a, b', 'return a * b');
Alapértelmezett értékek
function multiply(a = 1, b = 1) {
return a * b
}
multiply() // 1
multiply(10) // 10
multiply(10, 8) // 80
Paraméterek száma, rest paraméterek
function multiply(a, b, ...rest) {
console.log(a, b, rest)
return a * b
}
multiply() // undefined undefined [], NaN
multiply(1) // 1 undefined [], NaN
multiply(1, 2) // 1 2 [], 2
multiply(1, 2, 3) // 1 2 [3], 2
Paraméter destructuring, alapértelmezett értékekkel
function multiply([a = 1, b = 1, ...rest]) {
console.log(a, b, rest)
return a * b
}
multiply([]) // 1 1 [], 1
multiply([10]) // 10 1 [], 10
multiply([10, 20]) // 10 20 [], 200
multiply([10, 20, 30]) // 10 10 [30], 200
Paraméter destructuring, alapértelmezett értékekkel
function multiply({a = 1, b: _b = 1, c = 1, ...rest} = {}) {
console.log(a, _b, c, rest)
return a * _b
}
multiply() // 1 1 1 {}, 1
multiply({}) // 1 1 1 {}, 1
multiply({ a: 10 }) // 10 1 1 {}, 10
multiply({ a: 10, b: 20 }) // 10 20 1 {}, 200
multiply({ a: 10, d: 20 }) // 10 1 1 { d: 20 }, 10
Paraméter destructuring, extrém példa :)
function f([a, b] = [1, 2], {x: c} = {x: a + b}) {
return a + b + c
}
f() // 6
Spread szintaxis
function multiply(a, b) {
return a * b
}
multiply(...[10, 20])
function outer() {
let a = 10, b = 20
function inner() {
console.log(a, b)
b = 30
}
console.log(a, b) // 10 20
inner() // 10 20
console.log(a, b) // 10 30
}
outer()
console.log(a, b) // ReferenceError
Függvény literálformái (ez nem függvénydeklaráció)
// Névtelen függvénykifejezés
function (par1, par2) {
}
// Nevesített függvénykifejezés
function fn(par1, par2) {
}
// Arrow function expression
(par1, par2) => {
statements
}
// Arrow function expression
(par1, par2) => return_value
Értékadás → Névtelen függvénykifejezés
const multiply = 8
const multiply = function(a, b) {
return a * b
}
Objektum adattagja → metódus
const obj = {
multiply: 8
}
const obj = {
multiply: function (a, b) {
return a * b
}
}
“Használva” → önkioldó függvény
(8)
(function multiply(a, b) {
return a * b
})(2, 3)
// Önkioldó függvény definiálása (megj: semmit nem csinál...)
(function() {
const x = [1, 2, 3]
function multiply(a, b) {
return a * b
}
}());
// Önkioldó függvény paraméterezése
(function(a, b) {
console.log(a + b)
}(1, 2)) // 3
// Önkioldó függvény visszatérési értéke
var ize = (function(str) {
return str;
}('bize'));
console.log(ize); // 'bize'
Felfedő modul minta → ES6 modul alapja
const stack = (function () {
const elems = [];
const push = function (e) {
elems.push(e);
return this;
};
const pop = function () {
return elems.pop();
};
const top = function () {
return elems[size()-1];
};
const size = function () {
return elems.length;
};
return { push, pop, top, size };
})();
stack.push(10)
Függvény mint paraméter → callback minta
const f = function (p) {
return p
}
f(8)
const f = function (fv, a, b) {
return fv(a, b);
}
f(function (a, b) {
return a * b
}, 2, 3)
// Saját függvény
function execOperator(a, b, op) {
if (op) {
return op(a, b)
}
}
execOperator(3, 4, (a, b) => a * b)
// Tömbfüggvény
[1, 2, 3].map(e => e * 2)
// Eseménykezelő
document.addEventListener('click', function (e) { /* ... */ })
// Időzítő
setTimeout(function () {
console.log('Something')
}, 1000)
Függvény mint visszatérési érték → függvénygenerátor
const f = function () {
return 8
}
const f = function () {
return function (a, b) {
return a * b
}
}
const f = function () {
return function (a, b) {
return a * b
}
}
// or
const f = () => (a, b) => a * b
// Usage
const g = f()
const m = g(3, 4) // 12
Mi van abban az esetben, ha a külső függvény egy lokális változójára hivatkozom? A külső függvény ugyanis már lefutott.
var setup = function () {
var lokalis = 1;
return function () {
console.log(lokalis);
};
};
var fv = setup();
fv(); // ???
function buildUri(scheme, domain, path) {
return `${scheme}://${domain}/${path}`
}
buildUri('https', 'twitter.com', 'favicon.ico') // https://twitter.com/favicon.ico
const buildHttpsTwitterUri = partial(buildUri, 'https', 'twitter.com')
buildHttpsTwitterUri('favicon.ico') // https://twitter.com/favicon.ico
const partial = function (func, ...args) {
return function (...args2) {
return func(...[...args, ...args2])
}
}
function partial(fn, ...args) {
return fn.bind(null, ...args);
}
(function() {
var i;
for (i = 0; i < 3; i += 1) {
setTimeout(function () {
console.log(i);
}, 500);
}
})();
//Output
// 3
// 3
// 3
var fgvObj = function () {
return 'Meghívtál, visszatértem';
};
//Saját adattagok hozzáadása
fgvObj.adat = 'Adat vagyok';
fgvObj.metodus = function () {
return this.adat;
};
console.log(fgvObj()); //'Meghívtál, visszatértem'
console.log(fgvObj.metodus()); //'Adat vagyok'
var myFunc = function (...args) {
const cachekey = JSON.stringify(args)
let result
if (!myFunc.cache[cachekey]) {
result = {};
// ... expensive operation ...
myFunc.cache[cachekey] = result;
}
return myFunc.cache[cachekey];
};
// cache storage
myFunc.cache = {};
call
, apply
,
bind
call()
és apply()
segítségével
megmondhatjuk, hogy a függvény this paramétere melyik objektumra
mutasson (első paraméterük)
apply()
2. paramétere argumentumok tömbjecall()
2., 3., stb. paramétere a függvény 1., 2., stb.
paramétere leszbind()
: a függvény kontextusát véglegesen a paraméterül
megadott objektumhoz kötithis
kontextusa//Setting the context of this with call and apply
let peter = {
name: 'Peter',
hello: function () {
return this.name
}
};
let julia = {
name: 'Julia',
hello: function () {
return this.name
}
};
peter.hello.call(julia) // "Julia"
this
kontextusa// Alapeset
class AppView {
constructor(appState) {
this.elem = document.querySelector('something')
this.elem.addEventListener('click', this.onClick)
}
onClick(e) {
// this === document.querySelector('something')
}
}
// Bind
class AppView {
constructor(appState) {
this.elem = document.querySelector('something')
this.elem.addEventListener('click', this.onClick.bind(this))
}
onClick(e) {
// this === appView
}
}
// Fat arrow
class AppView {
constructor(appState) {
this.elem = document.querySelector('something')
this.elem.addEventListener('click', e => this.onClick(e))
}
onClick(e) {
// this === appView
}
}
// Class property initializer
class AppView {
constructor(appState) {
this.elem = document.querySelector('something')
this.elem.addEventListener('click', this.onClick)
}
onClick = (e) => {
// this === appView
}
}
DOM absztrakció
$('p').hide();
$("#profilkep");
$(".adat");
$("li");
$("div.adat");
$("#profilkep, #adatok");
$("img[src=profil.jpg]");
$("ul li.adat b");
$("ul li:first");
$("b:contains('Végzettség:')");
children()
), leszármazottak
(find()
)parent()
, parents()
,
parentsUntil()
), legközelebbi ős
(closest()
)siblings()
, next()
,
nextAll()
, nextUntil()
, prev()
,
prevAll()
, prevUntil()
)// szülő kiválasztása
$("#adatok").parent();
// a következő elem kiválasztása
$("li").next();
// az előző elem kiválasztása
$("li").prev();
// az előző elem kiválasztása, de csak ha adat osztályú
$("li").prev(".adat");
// az elem felmenői közül az első, amelyikre igaz, hogy div
$("b:first").closest('div');
// az elem leszármazottai, melyekre igaz, hogy adat osztályúak
$("#adatok").find(".adat");
// a kiválasztott elemek közül az első
$("li").first();
// a kiválasztott elemek közül az utolsó
$("li").last();
// a kiválasztott elemek közül a harmadik
$("li").eq(2);
// az elem azon testvérei, melyek h1 típusúak
$("#adatok").siblings("h1");
is()
, has()
,
filter()
, first()
, last()
)add()
)//Szűrés
// az elem jQuery objektumához hozzáadjuk az összes li-t is
$("#adatok").add("li");
// a választásból kivesszük az összes li típusút
$("#adatok, li").not("li");
// a kiválasztott elemekből csak a képek maradnak
$(".adat").filter("img");
//Visszalépéses szelekció
$("#adatok") // hatókör: div#adatok
.find("li") // hatókör: div#adatok
.css({ padding: "5px" }) // hatókör: div#adatok li
.find("b") // hatókör: div#adatok li
.html('Csak a címeket hackoltam meg!!!') // hatókör: div#adatok li b
.end() // hatókör: div#adatok li b
.end() // hatókör: div#adatok li
.animate({ paddingTop: '+=100' }, 200);
var cimkek = [];
$("b").each(function() {
cimkek.push( $(this).text().replace(':','') );
});
cimkek.join(', ');
jQuery(HTMLString)
)clone()
)append()
,
prepend()
, after()
, before()
,
appendTo()
, prependTo()
,
insertAfter()
, insertBefore()
,
replace()
)remove()
, detach()
)html()
, text()
)attr()
)//Új jQuery objektumok létrehozása
var $a = $("<a class='12a' href='index.html'><b>Béla, Bulcsú</b></div>");
// Hasonló elemdefiníció attribútum-objektummal:
var $a = $("<a />", {
className: '12a',
href: 'index.html',
html: '<b>Béla, Bulcsú</b>'
});
//Attribútum és tartalom módosítása
$("<a />")
.addClass('12a')
.attr('href', 'index.html')
.html('<b>Béla, Bulcsú</b>')
.appendTo($("body"));
$("body").find('.12a').attr('href'); // => 'index.html'
$("body").find('.12a').html(); // => '<b>Béla, Bulcsú</b>', HTML tartalom
$("body").find('.12a').text(); // => 'Béla, Bulcsú', tartalom tag-ek nélkül
//jQuery objektumok beillesztése és törlése
$a.appendTo($("body")); // $a beillesztése a body végére
$a.prependTo($("body")); // $a beillesztése a body elejére
$a.insertAfter($("#adatok")); // $a beillesztése az adatok id-jű div után
$a.insertBefore($("#adatok")); // $a beillesztése az adatok id-jű div elé
$a.remove(); // $a elem törlése
//Ugyanez másik szemszögből
$("body").append($a); // A body végére illeszti a $a-t
$("body").prepend($a); // A body elejére illeszti a $a-t
$("#adatok").after($a); // Az adatok id-jű div után illeszti a $a-t
$("#adatok").before($a); // Az adatok id-jű div elé illeszti a $a-t
css()
addClass()
, removeClass()
,
toggleClass()
, hasClass()
animate()
show()
, hide()
, toggle()
fadeIn()
, fadeOut()
,
fadeToggle()
slideDown()
, slideUp()
,
slideToggle()
height()
: számított magasságinnerHeight()
: magasság + bélésouterHeight()
: magasság + bélés + keret (+margó true
paraméter esetén)width()
, innerWidth()
,
outerWidth()
: ld. a height
függvényeitposition()
: a szülőobjektumhoz viszonyított
elhelyezkedés (top, left)offset()
: a dokumentumhoz viszonyított elrendezés (top,
left)scrollTop()
, scrollLeft()
$obj.on('type', fn)
: közvetlen hozzárendelés$obj.on('type', 'selector', fn)
: delegálás$('table').on('click', 'td', function (e) {
console.log(this);
console.log(e);
});
//Névterezés
$('table').on('click.myGame', 'td', function () {});
$('table').off('click.myGame', 'td', function () {});
//Még jobb
$('table').off('click.myGame').on('click.myGame', 'td', function () {});
$.ajax()
$.get()
$.post()
$.getJSON()
$.getScript()
$elem.load()
load()
A letöltött tartalmat a kiválasztott elembe helyezi.
// Alap használat
$("#hirek").load("hirlista.html");
// Oldaltöredék betöltése
$("#hirek").load("hirlista.html #content");
// Kérés küldése paraméterekkel
$("#hirek").load("hirlista.php", { honnan: 0, mennyit: 10 });
// Callback függvény hívása a kommunikáció végén
$("#hirek3").load("hirlista.php", { honnan: 0, mennyit: 10 }, function() {
alert('Betöltöttem a hírlistát!');
});
get
, post
// GET kérés a szervernek
$.get("hirlista.php", { honnan: 0, mennyit: 10 });
// POST kérés a szervernek
$.post("hirlista.php", { honnan: 0, mennyit: 10 });
// JSON kommuniáció
$.getJSON("hirlista.php", { formatum: 'json' });
// Dinamikus szkriptbetöltés
$.getScript("jquery_ui.js");
$.ajax()
$.ajax({
url: 'hirlista.php',
data: { mettol: 0 },
type: 'POST',
dataType: 'html',
});
jqXHR
objektum metódusai
done()
fail()
always()
$.post("hirlista.php", { honnan: 0, mennyit: 10 })
.done(function (data) {
console.log(data);
})
.fail(function (error) {
console.log(error);
});
serializeArray()
metódus
$('#registration form').submit(function(e) {
// A form elküldésének megakadályozása
e.preventDefault();
var $this = $(this);
$.post('regisztracio.php', $this.serializeArray(), function(data) {
$this.html(data);
});
});
// jQuery
$('ul li')
// Native
document.querySelectorAll('ul li')
Array.from(document.querySelectorAll('ul li'))
[...document.querySelectorAll('ul li')]
// Native $ function
const $ = document.querySelectorAll.bind(document)
// or
function $(selector) {
return Array.from(document.querySelectorAll(selector))
// return [...document.querySelectorAll(selector)]
}
// jQuery
$('p').html('Apple')
// Native
Array.from(document.querySelectorAll('p'))
.map(el => el.innerHTML = 'Apple')
// jQuery
$('p')
.html('Apple<span>1</span><span>2</span>')
.css('color', 'red')
.find('span')
// Native
Array.from(document.querySelectorAll('p'))
.map(el => (el.innerHTML = 'Apple<span>1</span><span>2</span>', el))
.map(el => (el.style.color = 'red', el))
.flatMap(el => Array.from(el.querySelectorAll('span')))
.map(el => (console.log(el), el))
// Native
const paragraphs = Array.from(document.querySelectorAll('p'))
paragraphs.forEach(el => {
el.innerHTML = 'Apple<span>1</span><span>2</span>'
el.style.color = 'red'
const spans = Array.from(el.querySelectorAll('span'))
spans.forEach(span => console.log(span))
})
return this
const s = new Stack()
s
.push(10)
.push(20)
.log()
.clear()
.log()
class Stack {
constructor() {
this.items = []
}
push(e) {
this.items.push(e)
✒>return this<✒
}
log() {
console.log(this.items)
✒>return this<✒
}
clear() {
this.items = []
✒>return this<✒
}
}