ES6, var vs let

Przykład użycia let oraz var

for (var i = 0; i < 5; ++i) {
  setTimeout(function () {
    console.log(i); // Wynik 5 razy 5 :)
  }, 100);  
}
for (let i = 0; i < 5; ++i) {
  setTimeout(function () {
    console.log(i); // Wynik 0, 1, 2, 3, 4
  }, 100);  
}

Żeby to wykonać bez let można by zrobić tak jak poniżej.

for (var i = 0; i < 5; ++i) {
    (function(n) {
        setTimeout(function(){
            console.log(n); // Wynik 0, 1, 2, 3, 4
        }, 100);
    }(i));
}

Object.keys, values, entries

Poniżej kilka przykładów jak dobrać się do obiektów 🙂

var codes = {
  reset: [0, 0],
  bold: [1, 22]
};

Object.keys(codes).forEach(function (key) {
  console.log(key); // "reset", "bold"
});

Object.values(codes).forEach(function (key) {
  console.log(key); // [0,0] [1,22]
});

Object.entries(codes).forEach(function (key) {
  console.log(key); // ["reset", [0, 0]] ["bold", [1, 22]]
});

Użycie operator spread syntax czyli […]

W ES6 pojawiła się nowa funkcjonalność nazywana „Spread syntax” operacje na tablicach czy obiektach. Poniżej kilka przykładów użycia. W ES5 jest to concat.

W ES6

const boys = ["Jaś", "Grześ", "Adaś"];
const girls = ["Małgosia", "Ania", "Czesia"];

let people = [...boys, ...girls];

console.log(people); // ["Jaś", "Grześ", "Adaś", "Małgosia", "Ania", "Czesia"]

W ES5

const boys = ["Jaś", "Grześ", "Adaś"];
const girls = ["Małgosia", "Ania", "Czesia"];

let people = boys.concat(girls);

console.log(people); // ["Jaś", "Grześ", "Adaś", "Małgosia", "Ania", "Czesia"]

Inny przykład:

const divs = document.querySelector('div');

// użycie call
[].foreach.call(divs, function(div) {
   console.log(div);
});

// użycie spread
[...divs].foreach(div => {
    console.log(div)
}

show/hide scroll React

Ukrywanie buttona scroll-top jeżeli wysokość przekroczy 200 oraz dodanie obsługi zdarzenia na onClick i wywołanie funkcji scrollToTop i metody scrollIntoView.

export default BackToTop extends Component {
    state = {
        scrolling: false,
        scrollPosHeight: 200
    }

    componentDidMount() {
        console.log('componentDidMount()');
        window.addEventListener('scroll', this.hideShowButtonTop);
    };

    componentWillUnmount() {
        console.log('componentWillUnmount()');
        window.removeEventListener('scroll', this.hideShowButtonTop);
    };

    hideShowButtonTop = () => {
        const scrollPos = window.pageYOffset || document.documentElement.scrollTop;
        const myDiv = document.querySelector('.back-to-top');
        (scrollPos > this.state.scrollPosHeight && scrollPos !== 0) ? myDiv.style.display = 'block' : myDiv.style.display = 'none';
    }

    scrollToTop() {
        const logoView = document.getElementById('logo');
        logoView.scrollIntoView({
            behavior: 'smooth'
        })
    }

    render() {
        return (
            <div onClick={this.scrollToTop} className="back-to-top"></div>
        )
    };
};

forEach, filter, map, sort, reduce

Trochę operacji na tablicach 🙂

const employments = [
  { name: 'Andrzej', category: 'Finanse', start: 1982, end: 2017 },
  { name: 'Agnieszka', category: 'Sprzedaż', start: 1992, end: 2008 },
  { name: 'Mariola', category: 'Samochody', start: 1991, end: 2007 }
];

const digits = [23, 22, 10, 1, 5, 54, 25, 24, 21];

Chcemy pobrać imię pracownika.
Najpierw w starym stylu.

for (let i = 0; i < employments.length; i++) {
  console.log(employments[i].name); // Andrzej, Agnieszka, Mariola
}

forEach – teraz pobierzemy przez forEach

employments.forEach(function(employment) {
  console.log(employment.name);
});

Możemy to zrobić jeszcze szybciej prze ES6

employments.forEach(employment => {
  console.log(employment.name);
});

filter – użyjemy teraz filter aby pobrać liczbę 21 lub większą

Najpierw w starym stylu.

let digitNumber = [];
for(let i = 0; i < digits.length; i++) {
  if(digits[i] >= 21) {
    digitNumber.push(digits[i]);
  }
}

console.log(digitNumber); // 23, 22, 54, 25, 24, 21

Teraz krócej 🙂

const digitNumber = digits.filter(function(digit) {
  if(digit >= 21) {
    return true;
  }
});

console.log(digitNumber);

I jeszcze krócej.

const digitNumbers = digits.filter(digit => digit >= 21);

console.log(digitNumbers);

A może by tak wyszukać osoby po kategorii np. Technologie

const retailEmployments = employments.filter(function(employment) {
  if(employment.category === 'Samochody') {
    return true;
  }
});

console.log(retailEmployments); // { category: "Samochody", end: 2007, name: "Mariola", start: 1991 }

A może da się jeszcze szybciej

const retailEmployments = employments.filter(employment => employment.category === 'Samochody');

console.log(retailEmployments);
const eightiesEmployments = employments.filter(employment => (employment.start >= 1980 && employment.start < 1990));

console.log(eightiesEmployments); // { category: "Finanse", end: 2017, name: "Andrzej", start: 1982 }

map – użyjmy map
Utworzymy tablice imion.

const employmentsNames = employments.map(function(employment) {
  return employment.name;
});

console.log(employmentsNames); // Andrzej, Agnieszka, Mariola

Możemy połączyć to z template.

const testEmployments = employments.map(function(employment) {
  return `${employment.name} [${employment.start} - ${employment.end}]`;
});

console.log(testEmployments); // ["Andrzej [1982 - 2017]", "Agnieszka [1992 - 2008]", "Mariola [1991 - 2007]"]

Lub nawet krócej.

const testEmployments = employments.map(
  employment => `${employment.name} [${employment.start} - ${employment.end}]`
);

console.log(testEmployments);

Możemy wielokrotnie użyć map.

const testDigits = digits
  .map(digit => Math.sqrt(digit))
  .map(digit => digit * 3);

console.log(testDigits); // [14.387494569938157, 14.071247279470288, 9.486832980505138, 3, 6.708203932499369, 22.045407685048602, 15, 14.696938456699067, 13.74772708486752]

sort – teraz przechodzimy do sort

const sortEmployments = employments.sort(function(ec1, ec2) {
  if(ec1.start > ec2.start) {
    return 1;
  } else {
    return -1;
  }
});

console.log(sortEmployments);
/*
{
  category: "Finanse",
  end: 2017,
  name: "Andrzej",
  start: 1982
}, [object Object] {
  category: "Samochody",
  end: 2007,
  name: "Mariola",
  start: 1991
}, [object Object] {
  category: "Sprzedaż",
  end: 2008,
  name: "Agnieszka",
  start: 1992
}
*/

Wersja skrócona.

const sortEmployments = employments.sort((a, b) => (a.start > b.start ? 1 : -1));

console.log(sortEmployments);

reduce – użyjmy reduce

let digitSum = 0;
for(let i = 0; i < digits.length; i++) {
  digitSum += digits[i];
}

console.log(digitSum); // 185

Inna wersja reduce.

const digitSum = digits.reduce(function(total, digit) {
  return total + digit;
}, 0);

console.log(digitSum); // 185
const digitSum = digits.reduce((total, age) => total + age, 0);

console.log(digitSum); // 185

I na koniec łączenie wszystkich metod.

const combined = digits
 .map(digit => digit * 2)
 .filter(digit => digit >= 40)
 .sort((a, b) => a - b)
 .reduce((a, b) => a + b, 0);

console.log(combined); // 338

Odczyt atrybutów elementu w obsłudze zdarzena w javascript

<select name="banner_url" onchange="var idx=this.selectedIndex;
var value=this.options[idx].getAttribute('banner_url');
var element = document.getElementById('img_banner_url');
element.setAttribute('src', value); element.style.display='block'">


!{section name=ii loop=$data}!
    !{capture assign=banner_url}!http://kody.wig.pl/!{/capture}!
    <option value="!{$banner_url}!" banner_url="!{$banner_url}!">
    !{$data[ii].name}! ---> !{$banner_url}!</option>
!{/section}!

</select>
<br><br>

<img src="" id="img_banner_url" style="display:none">

Template strings w Javascript ES6, ES2015

Ile razy człowiek się męczył aby zwykły formularz ze zmiennymi zapisać w js 😉
Z pokonaniem tego problemu sięgamy po template string. Używamy tego znaku (`) znajduje się on nad tabulatorem z angielskiego backtick, po naszemu odwrócony apostrof a zmienne umieszczamy w takiej formie ${zmienna}
Wszystko razem można zapisać `${zmienna}`

A teraz małe przykłady.

Powiedzmy że mamy sobie obiekt.

var person = {
    name: 'Greg',
    job: 'front-end',
    city: 'Warszawa'
}

A html to:

var html =
'<div class="person">'+
    '<h1>' + person.name + '</h1>' +
    '<h2>' + person.job + ' ' + person.city + '</h2>' +
'</div>';

A nie prościej tak?:

const html = `
    <div class="person">
        <h1>${person.name}</h1>
        <h2>${person.job} ${person.city}</h2>
    </div>
`;

Oczywiście można te zmienne mnożyć, dodawać odejmować itd. 🙂

const x = 1;
const y = 2;
const result = `${ x + y }`;

console.log(result); // 1 + 2 = 3

Taki zapis jak pokazałem niesamowicie skraca nam pracę i mniej błędów popełniamy, ale jest jeden problem i to poważny a zwie się on IE (Internet Explorer) 🙂
Niestety nasz kod musi być jeszcze przez jakiś czas wstecz kompatybilny ze starymi przeglądarkami, dlatego w swojej pracy używam gulp, webpacka a czasami je również łączę.

Małe wytłumaczenie:
1. Gulp to, oparty o platformę Node.js, system do automatyzacji pracy. Głównym jego zadaniem jest więc zautomatyzowanie wielu czynności, jakie musi wykonać programista podczas swojej pracy. Przykład użycia – zerknij tutaj
2. Webpack to narzędzie, które rozwiązuje problem dzielenia kodu na moduły i pozwala łatwo zarządzać zależnościami występującymi między nimi.

Więcej poszukajcie sobie w google bo naprawdę warto znać te technologie.

Wracając do naszego problemu z IE i nie tylko, ja użyłem biblioteki gulp-bable
Najpierw instalacja z linii komend biblioteki gulp-babel

npm install --save-dev gulp-babel babel-core babel-preset-es2015

Teraz dodajemy do gulpfile.js naszą bibliotekę oraz task

const gulp = require('gulp');
const babel = require('gulp-babel');


gulp.task('js', function () {
    return gulp.src('src/**/*.js')
        .pipe(babel({
            "presets": ["es2015"]
        }))
        .pipe(gulp.dest('dist'));
});

Taki kod w pliku template.js który chcemy zmienić na es2015 umieszczamy w folderze src

const person = {
    name: 'Greg',
    job: 'front-end',
    city: 'Warszawa'
}

const html = `
    <div class="person">
        <h1>${person.name}</h1>
        <h2>${person.job} ${person.city}</h2>
    </div>
`;

console.log(html);

const x = 1;
const y = 2;
const result = `${ x + y }`;

console.log(result);

Po uruchomieniu taska js w folderze dist otrzymujemy js zmieniony na es205, wynik poniżej

'use strict';

var person = {
    name: 'Greg',
    job: 'front-end',
    city: 'Warszawa'
};

var html = '\n    <div class="person">\n        <h1>' + person.name + '</h1>\n        <h2>' + person.job + ' ' + person.city + '</h2>\n    </div>\n';

console.log(html);

var x = 1;
var y = 2;
var result = '' + (x + y);

console.log(result);

I ten kod załączamy do naszej strony będzie działać również ze starszymi przeglądarkami.

Oczywiście to tylko pobieżne poruszenie tego tematu, więcej w google 😉

Pasek postępu przewijania strony.

Dobrych praktyk UX nigdy za wiele. Warto czasami do strony dodać coś co nawet w najmniejszym stopniu zwiększy czas przebywania usera na stronie.

Najpierw pobieramy o ile przewinęliśmy stronę od lewego górnego rogu window.pageYOffset,
później pobieramy wysokość okna bez uwzględniania toolbarów i scrollbarów window.innerHeight, następnie pobieramy document.body.clientHeight czyli wysokość całego okna.

const ProgressBar = function () {

    window.addEventListener('scroll', () => {
        let ws = window.pageYOffset,
            wh = window.innerHeight,
            dh = document.body.clientHeight;
        let scrollPercent = (ws / (dh-wh)) * 100;
        const progressBar = document.querySelector('#progress');
        if(ws > 50) {
            progressBar.setAttribute('style','width:'+ scrollPercent + '%');
        } else {
            progressBar.removeAttribute('style');
        }
    });

}();

Jeżeli przewiniemy o więcej niż 50px wtedy do id progress dodajemy style width, jeżeli jest mniejsze to usuwamy atrybut style z id.

Dodajemy też css aby to zadziałało.

.bar {
    position: fixed;
    background: rgb(255, 0, 0);
    height: 10px;
    width: 0;
    top: 0;
}

Działający przykład można znaleźć tutaj – działający przykład

Rozwijane menu

Najpierw budujemy strukturę html. Wszystko jest robione w scss, js w wersji ES6 i gulp
Do ES6 używam gulp-babel aby przekonwertować js do es2015

<div class="container">

    <div id="accorions">
        <div class="accordion">Section 1</div>
        <div class="panel">
            <p>Sauerkraut can be garnished with cored avocado, also try decorateing the cake with hollandaise sauce.
                Asparagus can be marinateed with crushed lettuce, also try flavoring the loaf with salsa verde.
                with lobsters drink cream.</p>
        </div>

        <div class="accordion">Section 2</div>
        <div class="panel">
            <p>Aww, raid me parrot, ye dead landlubber! Swashbuckling, coal-black whales cowardly vandalize a mighty, gutless scabbard.
                Scabbards sing on booty at port degas! Small, dead tobaccos darkly fight a black, lively dagger.
                seashells fall with love.</p>
        </div>

        <div class="accordion">Section 3</div>
        <div class="panel">
            <p>Turbulence at the bridge was the adventure of mineral, lowered to a gravimetric mermaid.
                Moon at the universe was the mystery of coordinates, feeded to a real planet.
                wisely handle a processor.</p>
        </div>
    </div>

</div>

Dodajemy trochę css, na początku importuję normalize

@import "normalize";

.container {
    max-width: 500px;
    margin: 200px auto;

}

.accordion {
    background-color: #eee;
    color: #444;
    cursor: pointer;
    padding: 18px;
    width: 100%;
    border: none;
    text-align: left;
    outline: none;
    font-size: 15px;
    transition: 0.4s;
    &:after {
        content: '\002B';
        color: #777;
        font-weight: bold;
        float: left;
        margin-right: 5px;
    }
    &:hover {
        background-color: #ddd;
    }
    &.active {
        background-color: #ddd;
        &:after {
            content: "\2212";
        }
    }
}

.panel {
    padding: 0 18px;
    background-color: white;
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.2s ease-out;
    li {
        list-style: none;
    }
}

Teraz najważniejsza część, dodajemy js

"use strict";

class Accordion {

    constructor(options) {
        this.options = options;
    }

    creatAccordion() {
        let acc = this.getAccordionName();

        [...acc].forEach((el, i) => {
            el.addEventListener('click', el => {
                if(this.options.type === true && !this.hasClass(el, this.options.activeName)) {                
                    this.removeActiveAndPanelHeight();
                }
                el.target.classList.toggle(this.options.activeName);
                let panel = el.target.nextElementSibling;
                if (panel.style.maxHeight) {
                    panel.style.maxHeight = null;
                } else {
                    panel.style.maxHeight = panel.scrollHeight + 'px';
                }
            })
        })

    }

    getAccordionName() {
        return document.getElementsByClassName(this.options.accordionName);
    }

    hasClass(element, cla) {
        return (' ' + element.className + ' ').indexOf(' ' + cla + ' ') > -1;
    }

    removeActiveAndPanelHeight() {
        const currentActive = document.querySelectorAll("." + this.options.activeName);
        const heightPanel = document.querySelectorAll("." + this.options.panelName);

        [...currentActive].forEach(ca => {
            ca.classList.remove(this.options.activeName);
        });

        [...heightPanel].forEach(hp => {
            hp.removeAttribute("style");
        });
    }
}

const options = {
    'accordionName': 'accordion',
    'activeName': 'active',
    'panelName': 'panel',
    'type': true
};

const accordion = new Accordion(options);
accordion.creatAccordion();

Przykład można zobaczyć pod tym adresem

Ostatni parametr w options czyli ‚type’: true lub false umożliwia sterowaniem zwijania menu, true powoduje że każde kliknięcie na menu zwija inne rozwinięte zaś false pozostawia rozwinięte każde kliknięcie.
Oczywiście options można przenieść do html aby z tego miejsca sterować nie musi być ten obiekt umieszczony w js.

Całość włącznie z gulpem można zobaczyć na github