• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Drugi poziom rozwijanego menu JS

Object Storage Arubacloud
0 głosów
408 wizyt
pytanie zadane 18 stycznia 2019 w JavaScript przez Helio Dor Nowicjusz (230 p.)
edycja 18 stycznia 2019 przez Helio Dor

Witam serdecznie, problem polega na niepożądanym efekcie rozwijania/zwijania menu drugiego poziomu. Mam na myśli, iż działa rozwijanie, lecz tylko gdy nacisnę na 'a' - a w tym 'a' mam również 'i' i 'p'. A naciśnięcie na 'i' lub 'p' powinno też rozwijać menu, lecz tego nie robi. Mam nadzieje, że znajdzie się ktoś chętny, kto przede wszystkim wytłumaczyłby mi błąd, który popełniłem :) Niżej wrzucam zawartość HTML, CSS i JS

 

UWAGA!

By burger się pokazał, trzeba wejść w tryb programisty i przełączyć na mobile, sory za niewygode..

 

<!DOCTYPE html>
<html lang="pl">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Poradniki do Shakes and Fidget</title>
    <link href="https://fonts.googleapis.com/css?family=Roboto:400,700&amp;subset=latin-ext" rel="stylesheet">
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/"
        crossorigin="anonymous">
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <div class="main-wrapper">
        <header>
            <input class="burger-check" id="burger-check" type="checkbox"><label for="burger-check" class="burger"></label>
            <div class="wrapper"></div>
            <nav id="navigation1" class="navigation">
                <div class="minilogo">
                    <img src="images/logo.png" alt="">
                </div>
                <div class="national">
                    <div class="en"><a href="indexen.html"><img src="images/en.png" alt=""></a></div>
                </div>
                <ul>
                    <li><a href="#"><i class="fas fa-home"></i><p>Strona główna</p></a></li>
                    <li>
                        <a href="#" class="mainmenu">
                            <i class="fas fa-chevron-right"></i>
                            <p>Klaser</p>
                            <i class="fas fa-plus"></i>
                        </a>
                        <ul class="hidemenu hidesubmenu">
                            <li>Zadania</li>
                            <li>Przedmioty</li>
                        </ul>
                    </li>
                    <li><a href="#" class="mainmenu"><i class="fas fa-chevron-right"></i><p>Poradniki</p><i class="fas fa-plus"></i></a>
                        <ul class="hidemenu hidesubmenu">
                            <li>Twierdza</li>
                            <li>Pupile</li>
                            <li>Podziemia</li>
                        </ul>
                    </li>
                    <li><a href="#" class="mainmenu"><i class="fas fa-chevron-right"></i><p>Profesje</p><i class="fas fa-plus"></i></a>
                        <ul class="hidemenu hidesubmenu">
                            <li>Mag</li>
                            <li>Wojownik</li>
                            <li>Zwiadowca</li>
                            <li>Zabójca</li>
                            <li>Paladyn</li>
                        </ul>
                    </li>
                    <li><a href="#" class="mainmenu"><i class="fas fa-chevron-right"></i><p>Dodatkowe informacje</p><i
                        class="fas fa-plus"></i></a>
                        <ul class="hidemenu hidesubmenu">
                            <li>o twierdzy</li>
                            <li>o pupilach</li>
                            <li>o podziemiach</li>
                        </ul>
                    </li>
                </ul>
            </nav>
        </header>
    </div>
    <script src="js/main.js"></script>
</body>

</html>
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

html {
    text-align: left;
    font-size: 20px;
    font-family: 'Roboto', arial, sans-serif;
    max-width: 1600px;
    margin: 0 auto;
    background-color: black;
}

.clearfix::after {
    content: "";
    display: block;
    clear: both;
}

header {
    background-color: black;
    height: 15vh;
    color: white;
    width: 82vw;
    display: block;
    overflow: hidden;
    background-image: url(images/logo.png);
    background-position: top;
    background-size: contain;
    max-width: 100%;
    background-repeat: no-repeat;
    margin: 0 0 0 18vw;
}

div.header {
    background-color: black;
    line-height: 20px;
}


/* MENU BURGER NOWE */

.main-wrapper.menu-active {
    overflow: hidden;
    width: 100vw;
    height: 100vh;
}

.burger {
    position: absolute;
    top: 7vh;
    left: 7vw;
    display: block;
    border: 0;
    background: unset;
    outline: 0;
    padding: 0;
    cursor: pointer;
    border-bottom: 4px solid currentColor;
    width: 28px;
    transition: border-bottom 1s ease-in-out;
    -webkit-transition: border-bottom 1s ease-in-out;
}

.burger::-moz-focus-inner {
    border: 0;
    padding: 0;
}

.burger:before {
    content: "";
    display: block;
    border-bottom: 4px solid currentColor;
    width: 100%;
    margin-bottom: 5px;
    transition: -webkit-transform 0.5s ease-in-out;
    transition: transform 0.5s ease-in-out;
    transition: transform 0.5s ease-in-out, -webkit-transform 0.5s ease-in-out;
    -webkit-transition: -webkit-transform 0.5s ease-in-out;
}

.burger:after {
    content: "";
    display: block;
    border-bottom: 4px solid currentColor;
    width: 100%;
    margin-bottom: 5px;
    transition: -webkit-transform 0.5s ease-in-out;
    transition: transform 0.5s ease-in-out;
    transition: transform 0.5s ease-in-out, -webkit-transform 0.5s ease-in-out;
    -webkit-transition: -webkit-transform 0.5s ease-in-out;
}

.burger-check {
    display: none;
}

.burger-check:checked~.burger {
    top: 50vh;
    left: 87vw;
    width: 12vw;
    height: 12vw;
    border-bottom: 4px solid transparent;
    transition: border-bottom 0.8s ease-in-out;
    -webkit-transition: border-bottom 0.8s ease-in-out;
    z-index: 100;
}

.burger-check:checked~.burger:before {
    transform: rotate(-405deg) translateY(1px) translateX(-3px);
    -webkit-transform: rotate(-405deg) translateY(1px) translateX(-3px);
    transition: -webkit-transform 0.5s ease-in-out;
    transition: transform 0.5s ease-in-out;
    transition: transform 0.5s ease-in-out, -webkit-transform 0.5s ease-in-out;
    -webkit-transition: -webkit-transform 0.5s ease-in-out;
}

.burger-check:checked~.burger:after {
    transform: rotate(405deg) translateY(-4px) translateX(-5px);
    -webkit-transform: rotate(405deg) translateY(-4px) translateX(-5px);
    transition: -webkit-transform 0.5s ease-in-out;
    transition: transform 0.5s ease-in-out;
    transition: transform 0.5s ease-in-out, -webkit-transform 0.5s ease-in-out;
    -webkit-transition: -webkit-transform 0.5s ease-in-out;
}

.navigation {
    overflow: auto;
    max-height: 0;
    z-index: 0;
}

.burger-check:checked~.navigation {
    position: absolute;
    display: block;
    top: 0;
    left: 0;
    height: 100vh;
    width: 85%;
    max-height: 100vh;
    transition: max-height 0.5s ease-in-out;
    background-color: #494e52;
    z-index: 2;
    border: 3px solid #5b6269;
}

.navigation ul li:nth-child(1) {
    background-color: #32363a;
}

a {
    color: inherit;
}

small {
    font-size: 14px;
}

.navigation ul {
    display: none;
}

.burger-check:checked~.navigation ul {
    position: relative;
    display: block;
    width: 100%;
    margin: 15vh 0 0 0;
    padding: 0;
}

li a {
    text-decoration: none;
}

li a:hover {
    text-decoration: underline;
}

.wrapper {
    top: 0;
    left: 85vw;
    max-height: 0;
    z-index: 0;
}

.wrapper.active {
    position: absolute;
    display: block;
    top: 0;
    left: 85vw;
    height: 100vh;
    width: 15vw;
    max-height: 100vh;
    background-color: black;
    opacity: .7;
    z-index: 1;
    transition: max-height 0.5s ease-in-out;
}

.minilogo img {
    width: 100%;
    height: 100%;
}

.minilogo {
    display: none;
}

.minilogo.active {
    position: absolute;
    display: block;
    width: 60vw;
    height: 25vw;
    top: 1vh;
    left: 12vw;
}

ul li a p {
    vertical-align: middle;
    padding-left: 5px;
    text-transform: none;
    display: inline;
    font-size: 18px;
    line-height: 18px;
    overflow: hidden;
    white-space: pre-wrap;
}

ul li a {
    position: relative;
    display: inline-block;
    bottom: 0;
    width: 100%;
    min-height: 30px;
    padding: 10px 20px;
    color: #fff;
}

ul li a .fas {
    text-align: right;
    vertical-align: middle;
}

ul li {
    position: relative;
    display: list-item;
    width: 100%;
    list-style-type: none;
    padding: 5px 0;
    border-bottom: 1px solid #5b6269;
    list-style: none;
    font-size: 20px;
    line-height: 20px;
}

.burger-check:checked~.navigation ul.hidemenu {
    position: relative;
    display: block;
    width: 100%;
    margin: 0;
    padding: 0;
}

ul li:last-child {
    border-bottom: none;
}

.burger-check:checked~.navigation ul.hidemenu.hidesubmenu {
    display: none;
}


.fa-chevron-right {
    color: #ff4d4d;
}

.fa-plus {
    position: absolute;
    top: 3vh;
    right: 5vw;
}

.national {
    position: relative;
    display: block;
    width: 83vw;
    height: 10vh;
    top: 10vh;
    left: 0;
}

.pl {
    position: absolute;
    width: 15vw;
    height: 15vw;
    background-color: unset;
    top: 3vh;
    left: 34vw;
}

.en {
    position: absolute;
    width: 15vw;
    height: 15vw;
    background-color: unset;
    top: 3vh;
    left: 34vw;
}

.pl img {
    width: 100%;
    height: 100%;
}

.en img {
    width: 100%;
    height: 100%;
}

@media (min-width: 360px) {}

@media (min-width: 411px) {}

@media (min-width: 768px) {
    li a {
        font-size: 36px;
    }

    .burger {
        top: 11vh;
        left: 10vw;
    }

    .burger-check:checked~.navigation ul {
        margin: 20vh 0 0 10vw;
    }
}


@media (orientation: landscape) {
    li {
        line-height: 1.3em;
    }

    .burger {
        top: 25vh;
        left: 9vw;
    }

    .burger-check:checked~.navigation ul {
        margin: 40vh 0 0 10vw;
    }
}

@media (orientation: landscape) and (min-width: 568px) {
    .burger {
        top: 18vh;
    }
}

@media (orientation: landscape) and (min-width: 812px) {
    .burger {
        top: 22vh;
    }

    .burger-check:checked~.navigation ul {
        margin: 50vh 0 0 10vw;
    }
}

@media (min-width: 1024px) {
    .burger {
        display: none;
    }
}

@media (min-width: 1400px) {
    display: none;
}
const parent = document.querySelector('ul');
parent.addEventListener('click', function (ev) {
    const t = ev.target;

    if (t.classList.contains('mainmenu'))
        t.nextElementSibling.classList.toggle('hidesubmenu');
});

 

komentarz 18 stycznia 2019 przez pablop76 VIP (123,180 p.)
Pusta strona po odpaleniu tego co podałeś, więc nie wiem czy ktoś ma tyle czasu żeby przejrzeć gdzie jest błąd.

1 odpowiedź

0 głosów
odpowiedź 18 stycznia 2019 przez Helio Dor Nowicjusz (230 p.)
Mogę podać cały kod strony. Ja bardziej stawiam, by coś zmienić w JS np. "t.nextElementSibling.classList.toggle('hidesubmenu');" może się da ustawić, by zmienić klasę na ostatnim elemencie w tej samej linii, a nie na następnym ? A w tym kodzie wystarczy ustawić, by pokazywało się od razu menu a nie po kliknięciu burgera. Zaraz postaram się zmienić kod.
1
komentarz 18 stycznia 2019 przez pablop76 VIP (123,180 p.)
Ale chodzi o to że jest pusta strona, nie ma nic. Burgera również. Nie ma w co kliknąć. Jak masz jakieś obrazki i ikonki podpięte względnie to oczywiste, że ja tego nie zobaczę i nie będę tego szukał. Czy to może font awesome czy inne.
komentarz 18 stycznia 2019 przez Helio Dor Nowicjusz (230 p.)
.burger-check:checked~.navigation to wystarczy zmienić na samo .navigation

 

Jak nie pomoże to zrobię od nowa kody do wrzutki, sory za problem
1
komentarz 18 stycznia 2019 przez pablop76 VIP (123,180 p.)

Nie pomaga.Dużo klas dla burgera, którego wcale nie ma w html. Po ich usunięciu robi się trochę jaśniej ale jest trochę bałaganu 

.navigation {
    overflow: auto;
    max-height: 0;
    z-index: 0;
}

.navigation ul {
    display: none; 
}

Przecież to ukrywa cala nawigację!!!

I jak mamy to przetestować? 

komentarz 18 stycznia 2019 przez Helio Dor Nowicjusz (230 p.)
edycja 18 stycznia 2019 przez Helio Dor
Edytowałem pierwszy post, trzeba przełączyć na rozdzielczość mobile :/
1
komentarz 19 stycznia 2019 przez pablop76 VIP (123,180 p.)
edycja 19 stycznia 2019 przez pablop76
if (t.classList.contains('mainmenu'))
        t.nextElementSibling.classList.toggle('hidesubmenu');

działa tylko dla <a> bo tylko <a> ma klasę mainmenu

<a href="#" class="mainmenu">
  <i class="fas fa-chevron-right"></i>
  <p>Klaser</p>
  <i class="fas fa-plus"></i>
</a>

 

komentarz 19 stycznia 2019 przez Helio Dor Nowicjusz (230 p.)
Ale jak dam klase 'mainmenu' dla np. pierwszego 'i' to doda klase 'hidesubmenu' następnemu elementowi tego samego poziomu, czyli doda dla 'p' a ma dać klasę dla UL i tu jest problem.
1
komentarz 19 stycznia 2019 przez pablop76 VIP (123,180 p.)
edycja 19 stycznia 2019 przez pablop76

Trzeba pomyśleć o innym rozwiązaniu 

Może coś w tym kierunku

const li = document.querySelectorAll('ul li');
li.forEach(function (el) {
    el.addEventListener("click", function (e) {
        e.preventDefault();
        this.querySelector("ul").classList.toggle('hidesubmenu');
    })
});

 

komentarz 20 stycznia 2019 przez Helio Dor Nowicjusz (230 p.)
Dokładnie nie inicjonowałem, ale zauważyłem błąd, gdy kliknę później w coś z rozwijanego menu, to menu się od razu zwija
1
komentarz 20 stycznia 2019 przez pablop76 VIP (123,180 p.)

Jest to spowodowane propagacją zdarzenia. Należało by ją zablokować.

const li = document.querySelectorAll('ul li');
li.forEach(function (e) {
    e.addEventListener("click", function (e) {
        e.preventDefault();
        e.stopPropagation();
        this.querySelector("ul").classList.toggle('hidesubmenu');
    })
});

 

komentarz 20 stycznia 2019 przez Helio Dor Nowicjusz (230 p.)
Jupi prawdopodobnie problem rozwiązany, później to wrzucę w całą stronę, ale skrypt prawdopodobnie działa poprawnie, teraz tylko chciałbym, byś wytłumaczył mi ten skrypt (bo chce się rozwijać, nie kopiować skrypty) np. co to ta propagation itd dzięki wielkie:)
komentarz 21 stycznia 2019 przez Helio Dor Nowicjusz (230 p.)
Jednak jest problem.. main.js:60 Uncaught TypeError: Cannot read property 'classList' of null
    at HTMLLIElement.<anonymous> (main.js:60)

W tych li chcę podciągnąć 'a' ale linki nie działają, bo każde kliknięcie prowadzi do zmiany class, a nie działa link. Jakiś pomysł ?
1
komentarz 21 stycznia 2019 przez pablop76 VIP (123,180 p.)

Można nadać klasę dla elementów, które rozwijają menu w celu ich identyfikacji i wyzwolić toggle tylko wtedy gdy element kliknięty posiada tą klasę, a w innym przypadku zezwolić na domuślną akcję linków usuwając prevenDefault();

np nadać klasę .dropdown dla li, które odpowiadają za rozwijanie i wtedy js mógłby wyglądać tak

const li = document.querySelectorAll('ul li');
li.forEach(function (el) {
    el.addEventListener("click", function (e) {
        e.stopPropagation();
        if (this.className == "dropdown") {
            this.querySelector("ul").classList.toggle('hidesubmenu');
        }
    })
});

 

komentarz 22 stycznia 2019 przez Helio Dor Nowicjusz (230 p.)

Super działa jak na razie :) Problem jest za to na podstronie.. Mianowicie po kliknięciu w menu przekierowuje na odpowiednią podstronę, nie działają skrypty js na podstronie a błąd w consoli jest taki 

Uncaught TypeError: Cannot read property 'addEventListener' of null
    at main.js:15

W 15 linijce jest tylko deklarowana zmienna const - a błąd jest chyba spowodowany tym, iż na podstronie nie ma tego elementu, którego deklaruje. Teraz jak mam to rozwiązać? Bo aż nie chcę mi się wierzyć, iż do każdego podstrony muszę robić osobne pliki js. Proszę o pomoc w rozwiązaniu tego problemu.

1
komentarz 22 stycznia 2019 przez pablop76 VIP (123,180 p.)
Ja nie znam całej mechaniki na Twojej stronie, więc nie wiem co Ci nie działa. Menu jest na każdej podstronie więc i skrypt musi być na każdej.
komentarz 23 stycznia 2019 przez Helio Dor Nowicjusz (230 p.)

i skrypt do menu działa ale mam również np. taki prosty skrypt do różnych zdarzeń

 

    const exitBurger = document.querySelector(".burger");
    const wrapper = document.querySelector(".wrapper");
    const minilogo = document.querySelector(".minilogo");
    const mainWrapper = document.querySelector(".main-wrapper");

    exitBurger.addEventListener("click", function () {
    wrapper.classList.toggle("active")
    minilogo.classList.toggle("active")
    mainWrapper.classList.toggle("menu-active")
})

i od 15 linijki kodu js nic nie działa, a dodam iż w 15 linijce jest pierwsza funkcja ale od przycisku, którego nie ma na podstronie i dalszej części kodu nie czyta przez ten błąd. Teraz przetestowałem i napisałem w html zamiast odczytanie main.js to ten kod co u góry i działa. Pytanie tylko czy do każdej podstrony, na której nie ma wszystkich funkcji, musi być osobny kod js ? 

1
komentarz 23 stycznia 2019 przez pablop76 VIP (123,180 p.)
Jak funkcja jest w 15 linijce?
komentarz 23 stycznia 2019 przez Helio Dor Nowicjusz (230 p.)
const przycisk = document.querySelector(".expend");
const przyciskexit = document.querySelector(".expendexit");
const przycisk2 = document.querySelector(".expend2");
const przyciskexit2 = document.querySelector(".expendexit2");
const przycisk3 = document.querySelector(".expend3");
const przyciskexit3 = document.querySelector(".expendexit3");


const exitBurger = document.querySelector(".burger");
const wrapper = document.querySelector(".wrapper");
const minilogo = document.querySelector(".minilogo");
const mainWrapper = document.querySelector(".main-wrapper");

const toTopButton = document.querySelector(".toTopButton");


przycisk.addEventListener("click", function () {
    przycisk.classList.toggle("active")
})



przyciskexit.addEventListener("click", function () {
    przycisk.classList.toggle("active")
})



przycisk2.addEventListener("click", function () {
    przycisk2.classList.toggle("active")
})



przyciskexit2.addEventListener("click", function () {
    przycisk2.classList.toggle("active")
})

przycisk3.addEventListener("click", function () {
    przycisk3.classList.toggle("active")
})



przyciskexit3.addEventListener("click", function () {
    przycisk3.classList.toggle("active")
})



exitBurger.addEventListener("click", function () {
    wrapper.classList.toggle("active")
    minilogo.classList.toggle("active")
    mainWrapper.classList.toggle("menu-active")
})

const li = document.querySelectorAll('ul li');
li.forEach(function (el) {
    el.addEventListener("click", function (e) {
        e.stopPropagation();
        if (this.className == "dropdown") {
            this.querySelector("ul").classList.toggle('hidesubmenu');
        }
    })
});

toTopButton.addEventListener("click", function () {
    window.scrollBy(0, -1 * window.pageYOffset);
})

Wiem, że pełno bezsensownych funkcji i że je powinienem jakoś porobić na [i] w ifach, ale dopiero zaczynam się uczyć js i robię takie podstawowe funkcje

1
komentarz 23 stycznia 2019 przez pablop76 VIP (123,180 p.)
edycja 23 stycznia 2019 przez pablop76

Dodaj sprawdzenie przed wywołaniem metody, czy obiekt n, na którym chcesz ją wywołać istnieje.

if(n){
 n.addEventListener(()=>{})
}

 

komentarz 23 stycznia 2019 przez Helio Dor Nowicjusz (230 p.)

kombinuje i nie umiem tego użyć :/ 

If(przycisk) {
    przycisk.addEventListener("click", function () {
        przycisk.classList.toggle("active")
    })
}

Jakaś podpowiedź? 

1
komentarz 23 stycznia 2019 przez pablop76 VIP (123,180 p.)

Nie kopiuj tylko, sprawdź co robi dana funkcja. Skopiowsłeś If z błędem. Instrukcja powinna być napisana małą literą.

komentarz 23 stycznia 2019 przez Helio Dor Nowicjusz (230 p.)
oj wybacz faktycznie.. a to taką funkcję mam na każdym skrypcie robić ? Czy da się jakoś uogólnić?

@Edit

Znaczy czy można robić ifa w ifie itd albo jest jakaś lepsza metoda ?
komentarz 24 stycznia 2019 przez pablop76 VIP (123,180 p.)
Może warto rozdzielić skrypt i stosować tam gdzie jest potrzebny? Oczywiście nie może być  rozdrobniony. Duża ilość żądań spowalnia działanie strony.
komentarz 24 stycznia 2019 przez Helio Dor Nowicjusz (230 p.)
edycja 24 stycznia 2019 przez Helio Dor
Ale jak to rozdzielić ? Przepraszam, dopiero wchodzę w świat JS i może mam głupie pytania, ale staram się ZROZUMIEĆ kody, nie je kopiować :)

BTW

Przyjrzałem się dogłębniej Twojemu skryptowi ciut wyżej, przeanalizowałem, zmodyfikowałem i pewną funkcję na stronie zmieniłem pod ten kod i z ok. 35-40 linijek zrobiło się 6 :) Dziękuje za pomoc :)

Podobne pytania

0 głosów
3 odpowiedzi 1,077 wizyt
0 głosów
1 odpowiedź 176 wizyt
pytanie zadane 16 czerwca 2018 w HTML i CSS przez przemek11 Nowicjusz (150 p.)
0 głosów
2 odpowiedzi 684 wizyt
pytanie zadane 24 stycznia 2017 w HTML i CSS przez R.orlinski Mądrala (5,490 p.)

92,631 zapytań

141,498 odpowiedzi

319,869 komentarzy

62,011 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...