Webbutvecklingsdagbok (Webbutveckling, 120 hp)

Permalänk
Medlem
Skrivet av medbor:

Väldigt många är inte vana med självstudier och ansvar, det är en helt annan sak att sitta motiverad själv och lära sig än att lyssna på föreläsningar. Många klarar inte av omställningen att behöva lära sig själva och leta information. Paradoxalt är det just dessa egenskaper som är viktiga för att kunna jobba med programmering

Verkar vara så, minns i början av utbildningen när flera i klassen var upprörda över att läraren inte delade ut konkreta uppgifter/saker att jobba på under självstudiedagarna. Jag menade då att det är väl bara att söka rätt på relevanta grejer, bygg små projekt och öva. Gör vad fan som helst som är relevant. Det känns som att essensen i dylika utbildningar trots allt är praktikperioderna samt att få öva lite på att jobba i grupp, saker som är svårare att göra/simulera på egen hand alltså.
Dessutom så är väl "lärarna" inga lärare egentligen utan konsulter och de är ibland dessutom aningen bristfälliga rent kunskapsmässigt.

Permalänk

Konvertera Epoch till JS?

Fråga för att få en knuff i rätt riktning: I kommande projektuppgiften (betygsättande biten i kursen) för Introduktion till JavaScript-programmering så har jag upptäckt att vid ett API-anrop så får jag tillbaka datum i JSON-formatet:

"/Date(1665352920000)/"

I XML-formatet ser det annars ut så här:

<starttimeutc>2022-10-09T22:00:00Z</starttimeutc> <endtimeutc>2022-10-09T22:02:00Z</endtimeutc>

Jag ska arbeta med JSON-data så jag måste få tillbaka det i JSON-format.

Detta verkar vara JavaScripts JSON-konvertering av ASP.NET:s datumtyp? I sin tur ska detta gå att konvertera till användbart datum enligt denna hemsida: https://www.epochconverter.com/ Hemsidan rekommenderar att använda JavaScript-objektet Date() vilket har jag gjort i tidigare inlämningsuppgift i kursen.

När jag går in på dokumentationen för Date() (https://www.w3schools.com/jsref/jsref_obj_date.asp) så hittar jag ingen metod som verkar omvandla just Date(1665352920000) till exempelvis 2022-10-10 10:27.

Det är just timmar och minuter jag är ute efter så om jag kan konvertera till åååå-mm-dd hh:mm så bör jag i nästa steg kunna plocka ut timmar och minuter? Jag får tillbaka två JSONs med start- respektive sluttider för olika radioprogram så därför är jag bara intresserad av timmar och minuter.

Så kan någon hänvisa mig till någon metod eller flera metoder för att konvertera

"/Date(1665352920000)/"

till

let convertedEpoch = Date("YYYY-MM-DD HH:MM")

eller dylikt så jag senare kan plocka ut timmar och minuter med övriga Date()-metoder!

EDIT: Jag fick till det. Var en hel del steg att gå och nu har jag kvar att fixa så det alltid är dubbelsiffrigt för timmar och minuter. Och nu fick jag till det tror jag:

* Här under börjar du skriva din JavaScript-kod */ // Några variabler const channel132URL = 'hemligt'; // Hämta data let fetchChannelData = fetch(channel132URL) .then((resp) => resp.json()) .then((data) => { clog(data); let getSchedule = data.schedule; clog(getSchedule); // Deklarerar och tilldelar array för start- & slutdatum let [startTime, endTime] = [ getSchedule[0].starttimeutc, getSchedule[0].endtimeutc, ]; let test = dateConverter(startTime, endTime); clog(test); }) .catch(); let startEpochTime = '/Date(1665352920000)/'; // PLACEHOLDER-variabel let endEpochTime = '/Date(1665354900000)/'; // PLACEHOLDER-variabel const clog = console.log; // Kortar ned console.log-funktionen /* Funktionen dateConverter behövs för erhållna datum är i epoktid och måste omvandlas till timmar & minuter. Funktionen returnerar i formatet starttid HH:MM - sluttid HH:MM */ function dateConverter(epochStart, epochEnd) { // Börja med att ta bort tecknen i början och slutet av tidssträngarna let epochTime1 = epochStart.substring(6); // Ta bort första 6 tecken let epochTime2 = epochTime1.slice(0, -2); // Ta bort sista 2 tecken let epochTime3 = epochEnd.substring(6); // Ta bort första 6 tecken let epochTime4 = epochTime3.slice(0, -2); // Ta bort sista 2 tecken // Omvandla nummersträngar till vanliga nummer med parseInt() och omvandla dessa med Date()-objektet let convertEpochTimeStart = new Date(parseInt(epochTime2)); let convertEpochTimeEnd = new Date(parseInt(epochTime4)); // Hämta timmar och gör det dubbelsiffrigt let getHStart = convertEpochTimeStart.getHours(); if (getHStart < 9) { getHStart = '0' + getHStart; } let getHEnd = convertEpochTimeEnd.getHours(); if (getHEnd < 9) { getHEnd = '0' + getHEnd; } // Hämta minuter och gör det dubbelsiffrigt let getMStart = convertEpochTimeStart.getMinutes(); if (getMStart < 9) { getMStart = '0' + getMStart; } let getMEnd = convertEpochTimeEnd.getMinutes(); if (getMEnd < 9) { getMEnd = '0' + getMEnd; } // Returnera i formatet HH:MM - HH:MM return `${getHStart}:${getMStart} - ${getHEnd}:${getMEnd}`; }

Fråga: Kan jag snygga till detta ytterligare för att uppnå DRY? Jag tänker att parametrarna

epochStart, epochEnd

skulle kunna representeras som en textarray och sedan går jag igenom samma steg med dem fast som en kort loop?

EDIT: T.ex. har jag snyggat till nu när jag börjat lära mig om destructuring assignments där jag gjort om:

let startTime = getSchedule[0].starttimeutc; let endTime = getSchedule[0].endtimeutc;

till:

let [startTime, endTime] = [ getSchedule[0].starttimeutc, getSchedule[0].endtimeutc, ];

Då är frågan om det går att göra något liknande med där samma array där båda elementen nyttjas av metoderna substring(6) och slice(-2) inuti funktionen dateConverter()? 🤔

Mvh,
WKL.

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk
Medlem
Skrivet av WebbkodsLärlingen:

Fråga för att få en knuff i rätt riktning: I kommande projektuppgiften (betygsättande biten i kursen) för Introduktion till JavaScript-programmering så har jag upptäckt att vid ett API-anrop så får jag tillbaka datum i JSON-formatet:

"/Date(1665352920000)/"

I XML-formatet ser det annars ut så här:

<starttimeutc>2022-10-09T22:00:00Z</starttimeutc> <endtimeutc>2022-10-09T22:02:00Z</endtimeutc>

Jag ska arbeta med JSON-data så jag måste få tillbaka det i JSON-format.

Detta verkar vara JavaScripts JSON-konvertering av ASP.NET:s datumtyp? I sin tur ska detta gå att konvertera till användbart datum enligt denna hemsida: https://www.epochconverter.com/ Hemsidan rekommenderar att använda JavaScript-objektet Date() vilket har jag gjort i tidigare inlämningsuppgift i kursen.

När jag går in på dokumentationen för Date() (https://www.w3schools.com/jsref/jsref_obj_date.asp) så hittar jag ingen metod som verkar omvandla just Date(1665352920000) till exempelvis 2022-10-10 10:27.

Det är just timmar och minuter jag är ute efter så om jag kan konvertera till åååå-mm-dd hh:mm så bör jag i nästa steg kunna plocka ut timmar och minuter? Jag får tillbaka två JSONs med start- respektive sluttider för olika radioprogram så därför är jag bara intresserad av timmar och minuter.

Så kan någon hänvisa mig till någon metod eller flera metoder för att konvertera

"/Date(1665352920000)/"

till

let convertedEpoch = Date("YYYY-MM-DD HH:MM")

eller dylikt så jag senare kan plocka ut timmar och minuter med övriga Date()-metoder!

EDIT: Jag fick till det. Var en hel del steg att gå och nu har jag kvar att fixa så det alltid är dubbelsiffrigt för timmar och minuter. Och nu fick jag till det tror jag:

* Här under börjar du skriva din JavaScript-kod */ // Några variabler const channel132URL = 'hemligt'; // Hämta data let fetchChannelData = fetch(channel132URL) .then((resp) => resp.json()) .then((data) => { clog(data); let getSchedule = data.schedule; clog(getSchedule); // Deklarerar och tilldelar array för start- & slutdatum let [startTime, endTime] = [ getSchedule[0].starttimeutc, getSchedule[0].endtimeutc, ]; let test = dateConverter(startTime, endTime); clog(test); }) .catch(); let startEpochTime = '/Date(1665352920000)/'; // PLACEHOLDER-variabel let endEpochTime = '/Date(1665354900000)/'; // PLACEHOLDER-variabel const clog = console.log; // Kortar ned console.log-funktionen /* Funktionen dateConverter behövs för erhållna datum är i epoktid och måste omvandlas till timmar & minuter. Funktionen returnerar i formatet starttid HH:MM - sluttid HH:MM */ function dateConverter(epochStart, epochEnd) { // Börja med att ta bort tecknen i början och slutet av tidssträngarna let epochTime1 = epochStart.substring(6); // Ta bort första 6 tecken let epochTime2 = epochTime1.slice(0, -2); // Ta bort sista 2 tecken let epochTime3 = epochEnd.substring(6); // Ta bort första 6 tecken let epochTime4 = epochTime3.slice(0, -2); // Ta bort sista 2 tecken // Omvandla nummersträngar till vanliga nummer med parseInt() och omvandla dessa med Date()-objektet let convertEpochTimeStart = new Date(parseInt(epochTime2)); let convertEpochTimeEnd = new Date(parseInt(epochTime4)); // Hämta timmar och gör det dubbelsiffrigt let getHStart = convertEpochTimeStart.getHours(); if (getHStart < 9) { getHStart = '0' + getHStart; } let getHEnd = convertEpochTimeEnd.getHours(); if (getHEnd < 9) { getHEnd = '0' + getHEnd; } // Hämta minuter och gör det dubbelsiffrigt let getMStart = convertEpochTimeStart.getMinutes(); if (getMStart < 9) { getMStart = '0' + getMStart; } let getMEnd = convertEpochTimeEnd.getMinutes(); if (getMEnd < 9) { getMEnd = '0' + getMEnd; } // Returnera i formatet HH:MM - HH:MM return `${getHStart}:${getMStart} - ${getHEnd}:${getMEnd}`; }

Fråga: Kan jag snygga till detta ytterligare för att uppnå DRY? Jag tänker att parametrarna

epochStart, epochEnd

skulle kunna representeras som en textarray och sedan går jag igenom samma steg med dem fast som en kort loop?

EDIT: T.ex. har jag snyggat till nu när jag börjat lära mig om destructuring assignments där jag gjort om:

let startTime = getSchedule[0].starttimeutc; let endTime = getSchedule[0].endtimeutc;

till:

let [startTime, endTime] = [ getSchedule[0].starttimeutc, getSchedule[0].endtimeutc, ];

Då är frågan om det går att göra något liknande med där samma array där båda elementen nyttjas av metoderna substring(6) och slice(-2) inuti funktionen dateConverter()? 🤔

Mvh,
WKL.

Du skulle ju kunna göra en funktion som tar in datum i konstigt format och returnerar datum i vettigt format, sen skickar du in båda dina datum till sagda funktion

Permalänk
Skrivet av Jaevel:

Du skulle ju kunna göra en funktion som tar in datum i konstigt format och returnerar datum i vettigt format, sen skickar du in båda dina datum till sagda funktion

Japp, mer eller mindre TROR jag att jag löste det med:

function dateConverter(epochStart, epochEnd) { /* Längst inuti Date()-funktionen så börja med att kapa 6 tecken i början & 2 tecken i slutet för start- respektive sluttidens textsträngar. Dessa omvandlas sedan med hjälp av parseInt()-funktionen till heltal vilket i sin tur förvandlas till standarformatet för tid med hjälp av Date()-objektet. */ let convertEpochTimeStart = new Date(parseInt(epochStart.slice(6, -2))); let convertEpochTimeEnd = new Date(parseInt(epochEnd.slice(6, -2))); // Hämta sedan timmar och gör det dubbelsiffrigt för start- & sluttid let getHStart = convertEpochTimeStart.getHours(); if (getHStart < 9) { getHStart = '0' + getHStart; } let getHEnd = convertEpochTimeEnd.getHours(); if (getHEnd < 9) { getHEnd = '0' + getHEnd; } // Hämta minuter och gör det dubbelsiffrigt för start- & sluttid let getMStart = convertEpochTimeStart.getMinutes(); if (getMStart < 9) { getMStart = '0' + getMStart; } let getMEnd = convertEpochTimeEnd.getMinutes(); if (getMEnd < 9) { getMEnd = '0' + getMEnd; } // Returnera till sist i formatet HH:MM - HH:MM return `${getHStart}:${getMStart} - ${getHEnd}:${getMEnd}`; }

Just nu har jag ett nytt problem som verkar ha med scope (icke-deklarerade variabler) men jag fattar inte:

// FELMEDDELANDET ÄR: Uncaught TypeError: getChannels[i] is undefined loadChannels http://127.0.0.1:5500/js/main.js:40 loadChannels http://127.0.0.1:5500/js/main.js:39 promise callback*loadChannels http://127.0.0.1:5500/js/main.js:28 init http://127.0.0.1:5500/js/main.js:21 EventHandlerNonNull* http://127.0.0.1:5500/js/main.js:18 // Funktion som hämtar tillgängliga kanaler function loadChannels() { fetch(channelsURL) .then((resp) => resp.json()) .then((data) => { let getChannels = data.channels; // Loopa igenom alla tillgängliga kanaler for (i = 0; i < getChannels.length; i++) { // Positionera sig vid ul-elementet let channelUlPos = document.getElementById('mainnavlist'); // Template Literal för att skriva ut varje länks namn, färg, hover-text, och händelsehanterare. let channelliEl = document.createElement('li'); channelUlPos.appendChild(channelliEl); channelliEl.innerHTML += `<a href="${getChannels[i].scheduleurl}" style="color:#${getChannels[i].color}";" title="${getChannels[i].tagline}">${getChannels[i].name}</a>`; channelliEl.addEventListener('click', () => { fetchChannelData(getChannels[i].scheduleurl); }); } }); }

Jag har definierat getChannels som gör att jag kan loopa igenom innehåll i JSON-fil och sedan är det addEventListener för <li> element som inte vill fungera. När jag klickar på den inuti webbläsaren så påstår den plötsligt att getChannels-variabeln inte existerar längre?

Det jag vill göra är att när man klickar på en länk/li-element så kmr den skicka vidare dess URL (getChannels[i].scheduleurl) till funktionen fetchChannelData() som tar en parameter (channelURL) och då radar upp massa data. Detta fungerar redan om jag bara skickar exakt samma URL-sträng till den när sidan först laddas.

getChannels deklareras just utanför for-loopen och då borde for-loopen få tillgång till den variabeln?

Jag provade även:

channelliEl.addEventListener( 'click', fetchChannelData(getChannels[i].scheduleurl) );

Men då säger den: "Uncaught (in promise) SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data" Och då undrar jag om den inte tolkar [i]getChannels.scheduleurl som en sträng?

EDIT: OJ! Jag tror jag vet vad det kan vara. När man anropar denna API så ska man skriva &format=json i slutet av URL och det har jag just nu inte i länkarna som hämtas från databasen. Ska testa det först! (testade nu, nä, det blir samma problem. Hm):

fetchChannelData(`${getChannels[i].scheduleurl}&format=json`)

Jag förstår inte hur getChannels blir out of scope (provade även deklarera den som en var) så den klassas som ej deklarerad inuti addEventListener?

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk
Medlem
Skrivet av WebbkodsLärlingen:

När jag går in på dokumentationen för Date() (https://www.w3schools.com/jsref/jsref_obj_date.asp) så hittar jag ingen metod som verkar omvandla just Date(1665352920000) till exempelvis 2022-10-10 10:27.

Själv föredrar dokumentationen jag på mozilla.org, w3schools kan vara litet bristfällig ibland. Detta är vad de förra har att säga om Date. Fungerar följande som du tänkt dig?

> console.log(new Date(1665354900000)); Mon Oct 10 2022 00:35:00 GMT+0200 (Central European Summer Time)

Annars brukar det ge mycket att googla på typ "javascript epoch to date".

Visa signatur

Bra, snabbt, billigt; välj två.

Ljud
PC → ODAC/O2 → Sennheiser HD650/Ultrasone PRO 900/...
PC → S.M.S.L SA300 → Bowers & Wilkins 607

Permalänk
Skrivet av Phod:

Själv föredrar dokumentationen jag på mozilla.org, w3schools kan vara litet bristfällig ibland. Detta är vad de förra har att säga om Date. Fungerar följande som du tänkt dig?

> console.log(new Date(1665354900000)); Mon Oct 10 2022 00:35:00 GMT+0200 (Central European Summer Time)

Annars brukar det ge mycket att googla på typ "javascript epoch to date".

Japp, jag fick till det för drygt någon timme sedan med följande kod:

let convertEpochTimeStart = new Date(parseInt(epochStart.slice(6, -2))); let convertEpochTimeEnd = new Date(parseInt(epochEnd.slice(6, -2))); // Hämta sedan timmar och gör det dubbelsiffrigt för start- & sluttid let getHStart = convertEpochTimeStart.getHours(); if (getHStart < 9) { getHStart = '0' + getHStart; } let getHEnd = convertEpochTimeEnd.getHours(); if (getHEnd < 9) { getHEnd = '0' + getHEnd; } // Hämta minuter och gör det dubbelsiffrigt för start- & sluttid let getMStart = convertEpochTimeStart.getMinutes(); if (getMStart < 9) { getMStart = '0' + getMStart; } let getMEnd = convertEpochTimeEnd.getMinutes(); if (getMEnd < 9) { getMEnd = '0' + getMEnd; } // Returnera till sist i formatet HH:MM - HH:MM return `${getHStart}:${getMStart} - ${getHEnd}:${getMEnd}`;

Mitt nya problem just nu är att en addEventListener som jag lägger till till varje skapade <li>-element via en loop under en <ul> inte fungerar som den ska. Vad den ska göra är att bara skicka vidare en URL-sträng till en funktion (via getChannels[i].scheduleurl, scheduleurl är en textsträng från JSON) som sedan tar den URL-strängen som URL att fetcha/hämta data ifrån.

Men jag får felmeddelandet att helt plötsligt är [i]getChannels.scheduleurl inte deklarerad trots att innan addEventListener till <li>-elementet så har jag redan skrivit ut andra saker: [i]getChannels..name och [i]getChannels.color bland annat.

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk
Medlem
Skrivet av WebbkodsLärlingen:

Just nu har jag ett nytt problem som verkar ha med scope (icke-deklarerade variabler) men jag fattar inte:
[...]

Jag gissar på att getChannels inte innehåller vad du förväntar dig i funktionen du skickar in i addEventListener. Har du lärt dig använda debuggern i din browser?

Det första jag skulle göra i det här läget är att sätta en breakpoint på följande rader:

let getChannels = data.channels;

och

fetchChannelData(getChannels[i].scheduleurl);

Sedan kör till det stannar och kolla vad data, data.channels och getChannels innehåller. Då klarnar det säkert.

Edit: Kolla också om variabeln i har det värde du förväntar dig i funktionen du hakar på din event listener.

Visa signatur

Bra, snabbt, billigt; välj två.

Ljud
PC → ODAC/O2 → Sennheiser HD650/Ultrasone PRO 900/...
PC → S.M.S.L SA300 → Bowers & Wilkins 607

Permalänk
Skrivet av Phod:

Jag gissar på att getChannels inte innehåller vad du förväntar dig i funktionen du skickar in i addEventListener. Har du lärt dig använda debuggern i din browser?

Det första jag skulle göra i det här läget är att sätta en breakpoint på följande rader:

let getChannels = data.channels;

och

fetchChannelData(getChannels[i].scheduleurl);

Sedan kör till det stannar och kolla vad data, data.channels och getChannels innehåller. Då klarnar det säkert.

Edit: Kolla också om variabeln i har det värde du förväntar dig i funktionen du hakar på din event listener.

Det är så fantastiskt vad lite sömn kan göra!

När jag vaknade så funderade jag på just att felmeddelandet var "not delcared" vilket jag tolkade som att koden inte såg

getChannels[i].scheduleurl

som en faktisk deklarerad och tilldelad variabel. Så då provade jag nu:

// Lagrar särskild länk som skickar lämpligt API-anrop till API:n let channelLink = `${getChannels[i].scheduleurl}&format=json&pagination=false`; channelliEl.addEventListener('click', () => { fetchChannelData(channelLink);

Jag skapade alltså variabeln channelLink som lagrar en template literal som innehåller all info som måste skickas till API:n (format=json och ingen paginering, dvs., skicka allt i samma JSON). Nu fungerar det som tänkt!

Det jag vill förstå nu är _varför_ addEventListener inte tolkar den som deklarerad variabel och _varför_ det ändå går att skriva ut den som en del i innerHTML. T.ex.:

channelliEl.innerHTML += `<a href="${getChannels[i].scheduleurl}&format=json&pagination=false" style="color:#${getChannels[i].color};" title="${getChannels[i].tagline}">${getChannels[i].name}</a>`;

Har du någon aning om det? Har det något med att skilja på "deklarerade strängvariabler" och "strängar från data" eller vad man ska kalla det för?

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk

Fråga: Tänker jag rätt här nu angående att inte introducera för många addEventListeners? T.ex. skriver jag att init()-funktionen körs första gången man startar sidan och då skapar händelsehanterare. Dessa bör ju INTE skapas om så fort man klickar på logotypen längst uppe åt vänster som bara ska ladda om sidan (via loadFront()-funktionen).

// Kör init()-funktionen när allt annat laddats fram först window.onload = init; function init() { loadChannels(10); // Hämta 10 radiokanaler standardChannelCount(); // Tilldela "Max antal:" värdet 10 fetchChannelData(startchannelURL); // Ladda fram första radiokanalen (P1) // Två händelsehanterare: Första är att ändra antalet radiokanaler "Max antal:" // Andra är att ladda om sidan när man klickar på logotypen ("DT084G - Projekt") document .getElementById('numrows') .addEventListener('change', changeNumrows, false); document.getElementById('logo').addEventListener('click', loadFront, false); } // När man klickar på logotypen högst uppe åt vänster på sidan // Notera att loadFront INTE skapar nya händelsehanterare eftersom dessa redan finns initierade function loadFront() { loadChannels(10); // Hämta 10 radiokanaler standardChannelCount(); // Tilldela "Max antal:" värdet 10 fetchChannelData(startchannelURL); }

Nytt problem! Och löst:

if (numberOfChannels > getChannels.length) { getChannels.length = getChannels.length; document.getElementById('numrows').value = getChannels.length; } else { getChannels.length = numberOfChannels; }

Problemet var att den alltid skrev ut minst ett till <li> element fast tomt innehåll eftersom det finns inget i JSON längre att klistra in via template literal. Då skapade den ett innehållslöst <li>-element vilket bara såg fult ut. Nu har jag gjort så den bara får hämta som flest antalet faktiska kanaler som finns.

Är det en klassisk lösning på när man vill skriva ut maxantalet av data från en JSON trots att en användare kanske försöker ange att den ska hämta mer än vad som finns?

Jag har oxå löst så att om man anger ett antal lägre än 1 så kommer den skriva in 1 och visa minst en kanal. Skriver man in högre än maxantalet tillgängliga kanaler så radar den bara upp antalet som faktiskt finns och skriver ut maxantalet i samma fält där man skrev ett högre.

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk
Medlem
Skrivet av WebbkodsLärlingen:

Det är så fantastiskt vad lite sömn kan göra!

När jag vaknade så funderade jag på just att felmeddelandet var "not delcared" vilket jag tolkade som att koden inte såg

getChannels[i].scheduleurl

som en faktisk deklarerad och tilldelad variabel. Så då provade jag nu:

// Lagrar särskild länk som skickar lämpligt API-anrop till API:n let channelLink = `${getChannels[i].scheduleurl}&format=json&pagination=false`; channelliEl.addEventListener('click', () => { fetchChannelData(channelLink);

Jag skapade alltså variabeln channelLink som lagrar en template literal som innehåller all info som måste skickas till API:n (format=json och ingen paginering, dvs., skicka allt i samma JSON). Nu fungerar det som tänkt!

Det jag vill förstå nu är _varför_ addEventListener inte tolkar den som deklarerad variabel och _varför_ det ändå går att skriva ut den som en del i innerHTML. T.ex.:

channelliEl.innerHTML += `<a href="${getChannels[i].scheduleurl}&format=json&pagination=false" style="color:#${getChannels[i].color};" title="${getChannels[i].tagline}">${getChannels[i].name}</a>`;

Har du någon aning om det? Har det något med att skilja på "deklarerade strängvariabler" och "strängar från data" eller vad man ska kalla det för?

Om vi kollar på din tidigare kod:

for (i = 0; i < getChannels.length; i++) { channelliEl.addEventListener('click', () => { fetchChannelData(getChannels[i].scheduleurl); }); }

Så först har du en for-loop med en loop-variabel. I den loopen skapar du upp funktioner (den kursiva delen), och i de funktionerna använder du loop-variablen.

Du har skapat något som kallas för ett closure, som beskrivs så här: A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment).

Så variabeln i i funktionerna du skapar upp refererar till din loop-variabel i scope:t utanför. När du kört färdigt loopen har du skapat ett antal funktioner, som inte körts än, och variabeln i har värdet getChannels.length.

Nu tänker du dig att i ska vara 0, 1, 2... o.s.v. i de funktioner du har skapat för dina event listeners, men så är det inte. Alla funktionerna du skapat refererar till samma i, den variabel som du hela tiden ökat värdet på. När dina event listeners sedan körs så är i för högt, och du får felmeddelandet "getChannels[i] is undefined" eftersom det inte finns så många värden i arrayen getChannels.

Du har löst problemet på det enklaste sättet att lösa det här på, genom att introducera en variabel i for-loopen. Genom att göra på det sättet får du en ny variabel för varje iteration, och de funktioner du skapar kan alltså hämta rätt värde. Ett annat sätt att lösa det här på är så här:

for (i = 0; i < getChannels.length; i++) { const ii = i; channelliEl.addEventListener('click', () => { fetchChannelData(getChannels[ii].scheduleurl); }); }

Har du kört debuggern i din browser för att testa din kod? Jag skulle verkligen rekommendera det, för det här känns invecklat i början och det är svårt att förstå vad som egentligen händer. Men om du ser vad dina variabler har för värde när koden körs är det mycket lättare att förstå.

Visa signatur

Bra, snabbt, billigt; välj två.

Ljud
PC → ODAC/O2 → Sennheiser HD650/Ultrasone PRO 900/...
PC → S.M.S.L SA300 → Bowers & Wilkins 607

Permalänk
Medlem
Skrivet av WebbkodsLärlingen:

Fråga: Tänker jag rätt här nu angående att inte introducera för många addEventListeners? T.ex. skriver jag att init()-funktionen körs första gången man startar sidan och då skapar händelsehanterare. Dessa bör ju INTE skapas om så fort man klickar på logotypen längst uppe åt vänster som bara ska ladda om sidan (via loadFront()-funktionen).

Precis, du ska inte skapa flera event listeners på samma element. Tar du bort element ska event listeners kopplade till dem tas bort automatiskt i moderna browsers.

Visa signatur

Bra, snabbt, billigt; välj två.

Ljud
PC → ODAC/O2 → Sennheiser HD650/Ultrasone PRO 900/...
PC → S.M.S.L SA300 → Bowers & Wilkins 607

Permalänk

Uppdatering om AddEventListener: Jag fick svar av läraren nu i videoformat där han berättade varför jag haft det problem jag haft med addEventListener. Dess innehåll körs endast vid klick och då försöker den alltså ta del av variabeldata som inte längre finns kvar efter loopens körning.

Så genom att skapa en variabel som tar data från JSON-datan precis innan addEventListener så kan man skicka med det som argument vilket då "magiskt" (tycker jag just nu) lagrar JSON-datan inuti addEventListener. Men jag förstår inte varför det inte kan lagra data från en variabel som lagrade alla JSON-data direkt efter lyckat fetch()-anrop? 🤔

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk

En sak jag börjat lära mig med projektuppgiften är att kod som måste upprepas kan göras det med en funktion.

Ta exempelvis följande kod som hämtar timmar och minuter från mottaget datumobjekt vilket ska göras för start- och sluttid:

function dateConverter(epochStart, epochEnd) { let convertEpochTimeStart = new Date(parseInt(epochStart.slice(6, -2))); let convertEpochTimeEnd = new Date(parseInt(epochEnd.slice(6, -2))); //Hämta sedan timmar och gör det dubbelsiffrigt för start- & sluttid let getHStart = convertEpochTimeStart.getHours(); if (getHStart < 10) { getHStart = '0' + getHStart; } let getHEnd = convertEpochTimeEnd.getHours(); if (getHEnd < 10) { getHEnd = '0' + getHEnd; } // Hämta minuter och gör det dubbelsiffrigt för start- & sluttid let getMStart = convertEpochTimeStart.getMinutes(); if (getMStart < 10) { getMStart = '0' + getMStart; } let getMEnd = convertEpochTimeEnd.getMinutes(); if (getMEnd < 10) { getMEnd = '0' + getMEnd; } // Returnera till sist i formatet HH:MM - HH:MM return `${getHStart}:${getMStart} - ${getHEnd}:${getMEnd}`; }

Koden optimerades/refactoring(?) nu så det bara blev:

function dateConverter(epochStart, epochEnd) { let convertEpochTimeStart = new Date(parseInt(epochStart.slice(6, -2))); let convertEpochTimeEnd = new Date(parseInt(epochEnd.slice(6, -2))); // En inre funktion då samma sak ska göras två ggr = DRY! function getHoursandMinutes(getHaM) { //Hämta timmar & minuter, gör båda dubbelsiffriga för start- & sluttid let getHours = getHaM.getHours(); let getMinutes = getHaM.getMinutes(); if (getHours < 10) { getHours = '0' + getHours; } if (getMinutes < 10) { getMinutes = '0' + getMinutes; } // Returnera i formatet HH:MM return `${getHours}:${getMinutes}`; } // Returnera i formatet HH:MM - HH:MM return `${getHoursandMinutes(convertEpochTimeStart)} - ${getHoursandMinutes( convertEpochTimeEnd )}`; }

Jag tycker också det ser fränt ut att returvärdet i en funktion är returvärdet från en annan funktion inuti en Template Literal!

Fråga: Jag försökte optimera det ytterligare med inspiration från Destructuring Assignment men det blir inte som jag tänkt mig:

if ([getHours, getMinutes] < 10) { [getHours, getMinutes] = ['0' + getHours, '0' + getMinutes]; }

Testar inte if-satsen om getHours är mindre än 10 och sedan om getMinutes är mindre än 10? Eller är det inuti {} som det blir tokigt? Bör jag göra annorlunda där? Eller blir if-satsen dåligt genomtänkt då den kanske försöker uppfylla att både getHours och getMinutes ska vara lägre än 10 innan den ens gör något inuti if-satsen?

Går det ens att optimera min ovanstående funktionskod ytterligare? Jag tänker for-loop men då blir det kanske för mkt kod igen?

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk
Medlem

@webbkodslärlingen
Inte bara, funktioner används också för att skapa standarder i större projekt, t.ex om datum ska visas likvärdigt vare sig du är inne på förstasidan eller profilinställningar, för att ha en gemensam URL-hantering o.s.v.

Det är även så att du flyttar områden som kan ge upphov till fel till en gemensam plats för projektet, tänk typ bibliotek. Skulle något vara buggigt i en funktion är det lättare att lokalisera felet om det visar sig att samma fel uppkommer på olika ställen som kör samma sak. Reparerar du det i funktionen så har du löst buggarna på alla involverade sidor. Besparingen av felsökningstid samt frustration är ovärderlig senare i projektet.

Så det finns många stora fördelar med att skriva olika avgränsade avsnitt som funktioner.

Permalänk
Skrivet av Marida:

@webbkodslärlingen
Inte bara, funktioner används också för att skapa standarder i större projekt, t.ex om datum ska visas likvärdigt vare sig du är inne på förstasidan eller profilinställningar, för att ha en gemensam URL-hantering o.s.v.

Det är även så att du flyttar områden som kan ge upphov till fel till en gemensam plats för projektet, tänk typ bibliotek. Skulle något vara buggigt i en funktion är det lättare att lokalisera felet om det visar sig att samma fel uppkommer på olika ställen som kör samma sak. Reparerar du det i funktionen så har du löst buggarna på alla involverade sidor. Besparingen av felsökningstid samt frustration är ovärderlig senare i projektet.

Så det finns många stora fördelar med att skriva olika avgränsade avsnitt som funktioner.

Tack för svaret!

Du menar om att använda och skicka runt objekt mellan funktioner i samband med att jag nämnde React och dess konstanta hantering av props:-egenskapen? Jag förstår då hur "tidssmart" det är att göra det funktionellt baserat så att samma funktionsfel på flera ställen även innebär samma lösning för alla ställen när man fixar funktionen ifråga!

En sak jag upptäckte nu är att jag har:

// Kortar ned massa variabler som upprepas/används flitigt i koden const clog = console.log; const infoDivConst = document.getElementById('info'); let btnCreateConst = document.createElement('button'); let i; // För iterationer

Men btnCreateConst går inte för jag tror det blir så att samma btnCreateConst bara skapar en faktisk knapp. När jag tilldelade fyra olika knappar btnCreateConst så blev det bara en knapp i slutändan. Däremot fungerar det att använda infoDivConst på några platser där jag vill placera mig vid elementet med id info.

Eller är document.createElement() en method i objektet document medan document.getElementById() är en property?

Jag måste dessvärre ha det så här:

let btnDecreaseEl = document.createElement('button'); let btnIncreaseEl = document.createElement('button'); let darkModeEl = document.createElement('button'); let tableModeEl = document.createElement('button');

Tänker jag rätt här med varför det inte fungerade som jag tänkte med btnCreateConst?

Exempelvis detta fungerar utmärkt om än kanske överdrivet med att försöka korta ned koden?:

const similarButtonStyles = `width: 85px; height: 22px`; // DRY-implementering! const SBS = similarButtonStyles; // Förkortat namn och hänvisning vill vad förkortningen syftar på btnDecreaseEl.style.cssText = SBS; btnIncreaseEl.style.cssText = SBS; darkModeEl.style.cssText = SBS; tableModeEl.style.cssText = SBS;

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk
Medlem
Skrivet av WebbkodsLärlingen:

Tack för svaret!

Du menar om att använda och skicka runt objekt mellan funktioner i samband med att jag nämnde React och dess konstanta hantering av props:-egenskapen? Jag förstår då hur "tidssmart" det är att göra det funktionellt baserat så att samma funktionsfel på flera ställen även innebär samma lösning för alla ställen när man fixar funktionen ifråga!

En sak jag upptäckte nu är att jag har:

// Kortar ned massa variabler som upprepas/används flitigt i koden const clog = console.log; const infoDivConst = document.getElementById('info'); let btnCreateConst = document.createElement('button'); let i; // För iterationer

Men btnCreateConst går inte för jag tror det blir så att samma btnCreateConst bara skapar en faktisk knapp. När jag tilldelade fyra olika knappar btnCreateConst så blev det bara en knapp i slutändan. Däremot fungerar det att använda infoDivConst på några platser där jag vill placera mig vid elementet med id info.

Eller är document.createElement() en method i objektet document medan document.getElementById() är en property?

Jag måste dessvärre ha det så här:

let btnDecreaseEl = document.createElement('button'); let btnIncreaseEl = document.createElement('button'); let darkModeEl = document.createElement('button'); let tableModeEl = document.createElement('button');

Tänker jag rätt här med varför det inte fungerade som jag tänkte med btnCreateConst?

Exempelvis detta fungerar utmärkt om än kanske överdrivet med att försöka korta ned koden?:

const similarButtonStyles = `width: 85px; height: 22px`; // DRY-implementering! const SBS = similarButtonStyles; // Förkortat namn och hänvisning vill vad förkortningen syftar på btnDecreaseEl.style.cssText = SBS; btnIncreaseEl.style.cssText = SBS; darkModeEl.style.cssText = SBS; tableModeEl.style.cssText = SBS;

Gör en funktion annars som returnerar en knapp om du nu vill ha lite kortare kod.
Gör en jQuery-ish 😅

const $ = (el) => document.createElement(el); const btn = $('button');

Inte speciellt vettigt men men 😁

Permalänk

Är det någon som vågar spekulera varför "Tabelläge"-knappen inte tilldelas någon [event] i FireFox-webbläsaren när man kör den via universitets server där man har egna filer?: http://studenter.miun.se/~maka2207/dt084g/Projekt/ Den visar inte heller kanalerna uppe åt höger i FireFox vars samma kanaler visas under "Bläddra via kanal". Det är en <li> lista i "Bläddra via kanal" medan uppe åt höger så är det innerHTML += ``; i en loop.

Jag har varit inne på en annans samma projektsida på skolservern som fungerar i FireFox. Dock använde den personen XMLHttpRequest medan jag använder fetch. Personen använder även innerHTML += "" + ""; för rullgardinsmenyn uppe åt höger. Men det ska väl fungera ändå med fetch()? Eller är fetch() känt att vara långsammare, buggigare än XMLHttpRequest av något skäl? Eller har jag implementerat fetch() på felaktigt sätt? Den används till två saker:

1) Hämta kanalerna i "Bläddra via kanal" (skriver samma i rullgardinsmenyn uppe åt höger)
2) hämtar varje kanal man klickar på från den listan.

I Chrome på skolservern märker jag också att den visar samma alert('Byt till en annan kanal du vill spela upp direkt!'); när man går från en kanal ur rullgardinsmenyn till "Välj en kanal att spela upp". Men det kanske är på grund av långsam skolserver? För nu försvann det plötsligt när jag skriver detta inlägg. Detta verkar vara långsam skolserver. Ibland kmr man knappt in när man loggar in där via FileZilla för att ladda upp sina egna skolfiler.

Jag blir bara besviken på mig själv som suttit 30+ timmar allt som allt med projektet hittills och vad som är jobbigast av allt är att jag inte förstår varför det inte fungerar, eller när det buggar eller varför det buggar i vissa webbläsare men inte andra. Och det känns som om man sitter och stoppar in massa "kod" i en "svart låda" och så ploppar det ut rätt eller fel utan att man förstår riktigt varför... <-- Fortfarande lite sant trots edit och struket!

Det känns som om jag måste börja om all kod för att jag inte tänkt till med fetch(), addEventListeners till knapparna och allt?! T_T Och så ska man skriva projektrapporten också där jag i princip inte ens kan beskriva varför det fungerar det som fungerar och vice versa. <-- Fortfarande lite sant trots edit och struket!

Som det ser ut just nu i klassen så har jag ingen bra uppfattning vilken anan som klarat det så galant - och vet varför - som skulle kunna tipsa mig om vad det är "som är fel i koden" min. Den som orkar att skumma igenom 700+ rader kod så varsågod!
Direktlänk till källkoden: http://studenter.miun.se/~maka2207/dt084g/Projekt/js/main.js

Ge gärna feedback på kodstruktur, lösningsval, med mera, så jag kan bättra mig! Detta är trots allt den riktiga stora första uppgiften inom JS så då är det lika bra att jag lär mig av de mer erfarna så jag lär mig "best practice" inom webbspråken.

EDIT: Jaha, jag hade inte rensat cache/offline-data i webbläsaren så den visade en äldre version på skolservern. Inte undra på! 😂😂😂

En sak som jag dock har problem med är att ljuduppspelaren uppdateras och inte sömlöst fortsätter spela när man går mellan sidor. En snubbes version gör inte det utan fortsätter spela. Jag vet att min version fetchar om vid varje ny sida men hur ska den kunna hämta all info om en given kanal då varje egen kanals innehåll är en egen JSON-fil? 🤔

Men skulle följande kunna fungera för smidigare upplevelse?
1. Fetch() läser in två stora JSON-strängar: en för alla kanaler, och en för alla 52s kanalers innehåll eller blir det för maffig JSON att läsa in och är dålig praxis rent allmänt? Samtidigt tänker jag att den då inte nödvändigtvis hämtar senaste information varje gång man klickar på respektive kanal som endast visar data utifrån nutid fram till slutet på dagen för kanalen.

Vad jag inte verkar förstå är:
1) Var och när ska man egentligen initiera och skapa sina addEventListeners så de fungerar utan att göra skumma saker och så att de får ta del av globala variabler?

2) Vad är bäst praxis med fetch() för att hämta JSON-data utan att det blir för många onödiga anrop men ändå "realtiddata" så gott som det går?

3) I en av de viktigaste funktionerna function init() så får jag inte till så den kan kontrollera mot localStorage för att sedan göra vissa saker rätt. T.ex. så kan den inte visa senaste visade sida i Tabelläge för något med scope, closure som jag inte hajat riktigt. Dock kan den visa rätt Mörk-/Ljusläge och rätt textstorlek.

4) Hur applicerar jag CSS via JS till <table>-elementet? Man får INTE röra CSS-fil eller HTML-filer i JS-kursen. Endast JS skall användas för att manipulera DOM. Jag försöker applicera CSS mot td och tr via

tdElement.style.cssText=`padding:5px;`;

men får inte till som jag tänkt.

5) Vad blir konsekvenserna med att lägga alla addEventListeners direkt globalt (utanför funktioner)? Jag ska fråga läraren det så jag kanske lär mig något för en gångs skull!

Jag är evigt tacksam för alla småknuffar/svar/tips jag fått här!

Mvh,
WKL.

P.S. Jag är dock lite smått stolt över tabellen för att skriva ut <table> i DOM är

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk

2 Veckor Kvar I 2 Första Kurser!!!

Datateknik GR (A), Webbutveckling I, 7,5 hp
Datateknik GR (A), Introduktion till programmering i JavaScript, 7,5 hp
Nu är det bara 2 veckor kvar i de två första kurserna i Webbutvecklingsprogrammet 120 hp!!!

Jag är i princip färdig med kodbyggandet till projektuppgiften i Introduktionskursen till JavaScript-programmering:
http://studenter.miun.se/~maka2207/dt084g/Projekt/

Hemsidan har först två obligatoriska funktioner (enligt projektuppgiften):
- Hämta kanallista från SR och lista upp dessa under "Bläddra via kanal"
- Hämta vald programtablå från SR för vald kanal under "Bläddra via kanal"

Hemsidan har även två valfria funktioner (föreslagna i projektuppgiften):
- Spela upp vald kanal vald från rullgardinsmeny uppe åt höger på sidan
- Välja antalet hur många kanaler som ska visas samtidigt (Max antal:-fältet)

Hemsidan har dessutom följande extra funktionaliteter:
- localStorage() är implementerat och lagrar vald uppspelad kanal, valt mörk-/ljusläge, valt tabell-/tablåläge, samt vald textstorlek, när användaren lämnar hemsidan
- Justera textstorleken för sändningstid och sändningsbeskrivning (ej i tabelläge)
- Växla mellan tablå- och tabelläge
- Växla mellan mörk- och ljusläge
- Varje kanal under "Bläddra via kanal" har sin logotyp vänster om sitt namn
- "Max antal:"-fältet har flyttats upp så den inte är i vägen när kanaler läggs till
- Uppspelningen för vald kanal har flyttats upp så den är precis ovanför pågående sändning för vald kanal för enklare användning
- I både tabell- och tablåläge visas längden för alla sändningar inklusive pågående sändning
- Högst upp i varje kanals programinformation framgår vilken kanal det är (t.ex. "P1 | JUST NU:")

Förhoppningsvis kan dessa extra funktioner/"kvalitetsförbättringar" kombinerat med en välgrundad projektrapport leda till åtminstone ett B eller ett C. Att få A tvekar jag lite på. Då behövs nog fetchande av fler saker än enbart kanaler och programtablåer.

Jag börjar få ont om tid då jag fortfarande har kvar tre praktiska inlämningsuppgifter i Webbutvecklig I-kursen. Men det ordnar sig!

Kika gärna på källkoden:
http://studenter.miun.se/~maka2207/dt084g/Projekt/js/main.js och ge feedback på dess struktur, kommentering, valda funktioner för att lösa/implementera ovannämnda problem/funktioner.

Jag kommer att be lärarna vid inlämning av Projektrapporten att få en gedigen feedback om vad jag gjort rätt och vad jag bör förbättra inom min allmänna Webbprogrammering.

Mvh,
WKL.

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk

Slutbetyg för Datateknik GR (A), Introduktion till programmering i JavaScript - DT084G

Jag har nu fått slutbetyget i första JavaScript-kursen

Tack för all hjälp jag fått här!

Jag blev några dagar försenad med HTML&CSS-kursen och i den tror jag att jag som bäst kommer att kunna få C, troligen D eller E.

Nu har jag påbörjat:
Datateknik GR (A), Grafisk teknik för webb - DT200G
Datateknik GR (B), Webbanvändbarhet - DT068G

Första kursen handlar om att skapa webboptimerade bilder i olika filtyper (inklusive de relativt nya filtyperna WebP och Avif), samt optimerad video för webben (vanlig användarvänlig video och en videobakgrund i HTML).

Andra kursen handlar om att göra webbplatser användbara och därmed tillgängliga och vice versa. Dessa löper samtidigt, precis som två föregående kurser, och det blir nu mest bara HTML & CSS och i princip ingen JS alls.

Mvh,
WKL.

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk
Medlem

Intressant att se hur det lärs ut i Sverige, jag pluggade utomlands och har gjort mycket självlärande. Det är bra att de börjar med grunderna (även om det kanske är lite outdated). I verkligheten är det som en helt annan värld kan jag säga, men samtidigt måste man kunna grunderna innan man lär sig om alla verktyg som används för att kunna jobba mer effektivt.

Permalänk
Skrivet av Anton341:

Intressant att se hur det lärs ut i Sverige, jag pluggade utomlands och har gjort mycket självlärande. Det är bra att de börjar med grunderna (även om det kanske är lite outdated). I verkligheten är det som en helt annan värld kan jag säga, men samtidigt måste man kunna grunderna innan man lär sig om alla verktyg som används för att kunna jobba mer effektivt.

Har du exempel på hur det kan vara annorlunda i verkligheten?

Andra året i utbildningen lärs ramverk ut (tydligen Vue när jag pratat med en som läser år 2 just nu, själv hade jag föredragit React). Versionhantering lärs också ut i någon framtida kurs. Gällande automatiska testverktyg för webbplatser vet jag inte, måste fråga programansvariga.

Mvh,
WKL.

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk
Medlem
Skrivet av WebbkodsLärlingen:

Har du exempel på hur det kan vara annorlunda i verkligheten?

Andra året i utbildningen lärs ramverk ut (tydligen Vue när jag pratat med en som läser år 2 just nu, själv hade jag föredragit React). Versionhantering lärs också ut i någon framtida kurs. Gällande automatiska testverktyg för webbplatser vet jag inte, måste fråga programansvariga.

Mvh,
WKL.

Största exemplet är väl att jag inte har rört en index.html eller en vanlig css fil sen jag började jobba Men det är helt enkelt för att man jobbar med React och Vue som du nämnde. Vilket du använder skiljer sig från företag till företag, jag personligen jobbar 90% med React när det kmr till utveckling (jobbar mycket med design också). Sen har du Bootstrap och SASS som vanliga verktyg när det kmr till CSS. Tailwind CSS börjar dock bli mer och mer aktuellt. Hur som helst hade jag rekommenderat att du lär dig React också, extremt värdefullt att kunna Men bli fullt bekväm med JavaScript först och främst!

Permalänk

Slutbetyg för Datateknik GR (A), Webbutveckling I - DT057G

Jag har nu fått slutbetyget i första HTML- & CSS-kursen!

Det blev bättre än väntat (B). Jag var inställd på som högst C för jag tänkte att rapporten var lite för lång och webbplatsen tycker jag inte var så särskilt märkvärd ur ett frontend-perspektiv.

En kontaktperson på detta forum som läser år 2 i samma distansutbildning har själv uttryckt att Webbutveckling I är lite av "en dagiskurs" inom webbutveckling sett till kursuppgifterna och lärarna.

Jag var inte ensam med att få just slutbetyget B utan det var även ett par andra från samma Discord-klassgrupp som fick samma slutbetyg. På tal om Discord-gruppen så är den relativt inaktiv nu och jag undrar om det beror på kommande Juletider och/eller antalet som nu gått in i väggen redan under de två första kurserna? 🤔

Projektuppgiften använde tillfälliga bilder så nedanstående kodkopior av html-, css- & js-källkoden kommer ej att visa några bilder. Lägg alla filer i samma rotmapp. Men utseendet och den responsiva funktionaliteten för hamburgarmenyn finns där för den som vill kopiera och klistra in koden för att se slutresultet!

Projektkod index.html

[code]
<!DOCTYPE html>
<html lang="sv">
<!-- START OF META DATA -->
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="./styles.css">
<!-- Signika Negative font from Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Signika+Negative&dis..." rel="stylesheet">
<!-- JS to Toggle Hamburger Menu-->
<script defer src="./main.js"></script>
<!-- TITLE-->
<title>CotonNorrland 2022 - Norrlands bästa hunduppfödare av Coton de Tuléar! | Startsida</title>
</head>
<!-- END OF META DATA -->
<!-- Start of BODY & MAIN -->
<body><main>
<!-- HEADER / LOGOTYPE -->
<header>
<!-- START OF NAV ELEMENT-->
<nav class="navbar">
<div class="brand-title" title="Logotyp | Klicka här för att komma till startsidan!"><a href="./index.html">CotonNorrland</a></div>
<a class="toggle-btn">
<span class="bar"></span>
<span class="bar"></span>
<span class="bar"></span>
</a>
<div class="navbar-links">
<ul>
<li title="Klicka här för att se bilder på tidigare valpkullar!"><a href="./pages/puppy-gallery.html">Valpgalleri</a></li>
<li title="Klicka här för att läsa mer om hundrasen Coton de tuléar!"><a href="./pages/about-coton-de-tulear.html">Hundras</a></li>
<li title="Klicka här för att läsa mer om hunduppfödaren CotonNorrland!"><a href="./pages/about-breeder.html">Om CotonNorrland</a></li>
<li title="Klicka här för att kontakta oss via e-post eller formulär!"><a href="./pages/contact-us.html">Kontakt</a></li>
</ul>
</div>
</nav>
<!-- END OF OF NAV ELEMENT-->
</header>
<!-- END OF HEADER / LOGOTYPE -->
<!-- Start of Div Container -->
<div id="container">
<section>
<article>
<h1 title="Välkomstrubrik på startsidan!">Välkommen till CotonNorrland!</h1>
<p>Vi anser oss vara Norrlands bästa hunduppfödare av hundrasen Coton de Tuléar! Läs mer om själva hundrasen Coton de Tuléar under "HUNDRAS" uppe till höger. Då får du reda på om denna hundras är rätt för dig!</p>
<img src="./images/index-coton-de-tulear-3-dogs.jpg" title="En bild på tre hundar av rasen Coton de tuléar!" alt="En bild på tre hundar av rasen Coton du tuléar!" class="image"/>
</article>
</section>
<section>
<article>
<h2>Vad erbjuder hunduppfödaren CotonNorrland?</h2>
<p>Hos CotonNorrland hittar du de allra finaste Coton de Tuléar-hundarna för både sällskaps- och utställningssyften. Läs mer om vad CotonNorrland erbjuder under "OM COTONNORRLAND" uppe till höger.</p>
<img src="./images/index-coton-de-tulear-3-dogs-2.jpg" title="En ytterligare bild på tre hundar av rasen Coton de tuléar!" alt="En ytterligare bild på tre hundar av rasen Coton du tuléar!" class="image"/>
</article>
</section>
<section>
<article>
<h2>Hur kommer du i kontakt med hunduppfödaren från CotonNorrland?</h2>
<p>Du kontaktar oss enklast genom "KONTAKT"-knappen uppe till höger på sidan.</p>
<img src="./images/index-coton-de-tulear-3-dogs-3.jpg" title="Återigen en ytterligare till bild på tre hundar av rasen Coton de tuléar!" alt="En ytterligare bild på hundar av rasen Coton du tuléar men denna gång endast två!" class="image"/>
</article>
</section>

<!-- End of Div Container & MAIN -->
</div></main>
<!-- FOOTER -->
<footer>
<p>©2022 CotonNorrland Alla rättigheter reserverade | Ansvarig utgivare: <a href="mailto:webmaster@cotonnorrland.se">CotonNorrland</a></p>
</footer>
<!-- END OF BODY -->
</body>
</html>
[code]

Dold text

Projektkod styles.css

[code]
/*
--------------------------------
GLOBAL CSS SOM GÄLLER ALLA SIDOR
--------------------------------
*/
/* Nollställer padding & margin för alla HTML-element
samt väljer typsnitt & border-box */
* {
padding: 0;
margin: 0;
font-family: 'Signika Negative', sans-serif;
box-sizing: border-box;
}
/* Gör header (vilket inkluderar <nav>-elementet) fast när man skrollar ned */
header{
position: sticky;
top: 0;
}
/* Standardiserade textstorlekar för diverse textelement */
h1{
margin-top: 20px;
font-size: 1.5rem;
}
h2{
margin-top: 20px;
font-size: 1.2rem;
}
p{
margin-top: 5px;
margin-bottom: 5px;
font-size: 1rem;
}
/* Centrera sidfotens text */
footer{
margin-top: 5px; /* Tryck bort sig uppifrån #container lite */
font-size: 0.7rem;
text-align: center;
}
/* HUVUDSAKLIG CONTAINER MED CSS GRID för att centrera allt inuti
container vilket kommer direkt efter <header> med <nav> inuti */
#container{
display: grid; /* Visa som Grid/fält */
grid-template-columns: repeat(3, 380px); /* 3 kolumner, 400 pixlar breda vardera */
justify-content: center; /* Centrera */
gap: 30px; /* 15 pixlar mellan kolumner */
padding: 12px; /* Tryck in innehåll så det blir "andningsutrymme*/
border-bottom: 1px solid #333;
}
/* EXKLUSIV CONTAINER FÖR puppy-gallery.html */
#container-puppy-gallery{
display: grid; /* Visa som Grid/fält */
grid-template-columns: repeat(3, 380px); /* 2 kolumner, 400 pixlar breda vardera */
justify-content: center; /* Centrera */
gap: 30px; /* 15 pixlar mellan kolumner */
padding: 12px; /* Tryck in innehåll så det blir "andningsutrymme*/
border-bottom: 1px solid #333;
}
/* EXKLUSIV CONTAINER FÖR about-breeder.html */
#container-about-breeder{
display: grid; /* Visa som Grid/fält */
grid-template-columns: repeat(1, 380px); /* 1 kolumn, 400 pixlar bred */
justify-content: center; /* Centrera */
gap: 30px; /* 15 pixlar mellan kolumner */
padding: 12px; /* Tryck in innehåll så det blir "andningsutrymme*/
border-bottom: 1px solid #333;
}
/* EXKLUSIV CONTAINER FÖR contact-us.html */
#container-contact-us{
display: grid; /* Visa som Grid/fält */
grid-template-columns: repeat(1, 380px); /* 1 kolumn, 400 pixlar bred */
justify-content: center; /* Centrera */
gap: 30px; /* 15 pixlar mellan kolumner */
padding: 12px; /* Tryck in innehåll så det blir "andningsutrymme*/
border-bottom: 1px solid #333;
}
/* EXKLUSIV CONTAINER FÖR puppy-gallery-1,-2,-3.html */
#container-puppy-gallery-subpage{
display: grid; /* Visa som Grid/fält */
grid-template-columns: repeat(2, 380px); /* 1 kolumn, 400 pixlar bred */
justify-content: center; /* Centrera */
gap: 30px; /* 15 pixlar mellan kolumner */
padding: 12px; /* Tryck in innehåll så det blir "andningsutrymme*/
border-bottom: 1px solid #333;
}

/* Följande två selektorer (kombinerat med .image{margin-top: auto;})
gör så att bilderna lägger sig längst ned i varje <article> oavsett
utrymme ovanvilket ser mycket proffsigt ut i stationär skärmbredd */
section{
display: flex;
flex-flow: row wrap;
justify-content: flex-start;
}
article{
display: flex;
flex-flow: column nowrap;
}
/* Bilderna "förbättras" genom att framstå som om
de är inramade för att öka deras uppelvda kvalité */
.image{
border: 20px solid #fff;
box-shadow: 0 5px 10px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
margin: 5px;
margin-top: auto; /* Placeras nu längst ned mot sidfot vid stationär skärmbredd */
width:350px;
height: 350px;
}
/* Så bilder i Valpgalleri visar på att de är klickbara */
#click-img1:hover, #click-img2:hover, #click-img3:hover{
outline: thick ridge #555;
}
/* Tryck in icke-numrerad listor */
article ul{
margin-left: 15px;
margin-top: 10px;
}
article ul li{ /* Utrymme mellan listobjekten */
margin-top: 3px;
}
/*
---------------------------------------
NAVIGERINGSMENYN = <nav> inuti <header>
---------------------------------------
*/
/* Responsiv design av <nav>-elementet
med hjälp av Flexbox och skapandet
av en "hamburgarmeny" som består av
3 span-element med class "bar" */
/*Logotypen: "CotonNorrland" */
/* CSS för logotypen som fungerar som länken till start */
.brand-title{
font-size: 2.5rem;
font-weight: bolder;
margin: 0.5rem;
} /* CSS för logotypen som länkelement */
.brand-title a{
text-decoration: none;
color: white;
} /* Hover-effekt för logotyplänken */
.brand-title:hover{
background-color: #555;
}
/* CSS-styling för hela navbar (dvs., hela <nav>-elementet) */
.navbar{
display: flex; /* Visa med flex */
justify-content: space-between; /* Jämnt utrymme mellan elementen */
align-items: center; /* Centrera elementen */
background-color: #333;
color: white;
}
.navbar-links ul { /* Visa listan flexibelt */
display: flex;
}
.navbar-links li { /* Ingen stil för listelementet */
list-style: none;
}
/* ej understrykt, endast stora bokstäver, vit färg, 1rem
padding, typsnittsstorlek 0.7rem & visa som block för länkar */
.navbar-links li a {
text-decoration: none;
text-transform: uppercase;
font-weight: bold;
color: white;
padding: 0.8rem;
font-size: 0.8rem;
display: block;
}
/* Färger för hover, besökt & aktiv länk */
.navbar-links li:hover{
background-color: #555;
}
.navbar-links li a:visited{
background-color: hsl(0, 80%, 33%);
}
.navbar-links li a:active{
background-color: hsl(111, 50%, 33%);
}
/*
---------------------------------------
HAMBURGARMENYN SOM NYTTJAR LITE JS!
---------------------------------------
*/
/* toggle-btn ansvarar över att visa hamburgarmenyn */
.toggle-btn{
position :absolute;
top: 1.3rem;
right: 1rem;
display: none; /* Den ska bara visas när skärmbredden blir mindre */
flex-direction: column; /* Visa som kolumn */
justify-content: space-between;
width: 30px;
height: 21px;
}
/* Själva hamburgarmenyn som är tre <span>-element avrundade i färgen vitt */
.toggle-btn .bar {
height: 3px;
width: 100%;
background-color: white;
border-radius: 20px;
}
/* hamburgarmenyn får pekfinger vid hover då den inte får det annars det
för har a href="#" så kmr hamburgarmenyn vid klick att föra en högst
upp på sidan*/
.toggle-btn:hover{
cursor: pointer;
}
/*
-------------------------------
UNIK/EXTRA CSS FÖR "OM KONTAKT"
-------------------------------
*/
/* CSS FÖR FORM, DVS FORMULÄRET */
/* Skapa luftutrymme runt formulärramen */
fieldset {
border-color: var(--secondary-bg-color);
border-radius: 10px;
margin-top: 10px; /* Tryck bort formulär från ovanstående paragraf */
padding: 20px;
min-width: 400px; /* Minsta bredd på formuläret */
font-weight: bold; /* Fetad text i formuläret */
}
/* Paragrafelementen, dvs., label-texterna i formuläret */
form p{
margin-top: 10px;
}
/* Strömlinjeformar alla label element */
form label {
display: inline-block; /* Visa som en rad med blockelement */
width: 100px; /* Gör denna 100 pixlar lång */
text-align: right; /* Justera label-texterna åt höger */
}
/* Tryck bort lite från label-texterna */
form input {
margin-left: 10px;
}
/* Knapparna i formuläret */
.button {
margin-top: 5px;
color: #333; /* Textfärgen i knapparna */
background-color:rgb(235, 235, 235); /* Bakgrundsfärg för knappar */
padding: 5px; /* Förstora knapparna lite inifrån */
border-radius: 5px; /* Skapar en 3D effekt pga. det */
}
.button:hover{ /* Förstora knapparna lite när mus förs över dem */
transform: scale(1.1);
}
textarea{ /* Lägg mer parallellt vertikelt med "Meddelande:" */
margin-left: 7px;
margin-top: 2px;
width: 288px;
}
input:hover{ /* När mus förs över input-fälten */
outline: thin groove #333;
}
/*
---------------------------------------
MEDIA QUERIES = Så webbplatsen surfar
bra på surfplattor och mobiltelefoner.
---------------------------------------
*/
/* Justera navigeringsmenyn för att se snyggt ut
på surfplattor där den nu flyttar ned */
@media (max-width: 1280px){
.navbar-links{
display: flex;
width: 100%; /* Sträck över hela sidans bredd */
}
/* Visa navbar som kolumn från början av flex */
.navbar{
flex-direction: column;
align-items: flex-start;
}
/* Ta upp hela bredden för <ul> annars kan <li> i nästa @media ej centreras */
.navbar-links ul{
width: 100%;
}
/* Kläm ihop länkarna pga. display: block */
.navbar-links li a {
padding: 0.6rem 1rem;
}
/* Denna visar menyknapparna via JS-skriptet */
.navbar-links.active {
display: flex;
}
#container{ /* Visa allt i form av två kolumner, 450 pixlar breda vardera */
display: grid;
grid-template-columns: repeat(2, 380px);
}
#container-puppy-gallery{
display: grid; /* Visa som Grid/fält */
grid-template-columns: repeat(2, 380px); /* 2 kolumner, 450 pixlar breda vardera */
}
}
/* Justera för att se snyggt ut på mobiltelefoner. Siffran
är vald är för att annars "klipper Om CotonNorrland till"
två rader så då kan den lika gärna gå till kolumnläge.*/
@media (max-width: 900px){
/* Visa med flex */
.toggle-btn{
display: flex;
}
.navbar-links{
display:none; /* Dölj länkarna som standard, aktiveras sedan med JS-kod */
width: 100%; /* Sträck över hela sidans bredd */
}
/* Visa navbar som kolumn från början av flex */
.navbar{
flex-direction: column;
align-items: flex-start;
}
/* Visa <ul> som kolumn */
.navbar-links ul{
flex-direction: column;
}
/* Centrera <li>-elementen, dvs., länkarna */
.navbar-links li {
text-align: center;
}
/* Kläm ihop länkarna lite i mobilläge pga.
display: block och förstora texten */
.navbar-links li a {
padding: 0.4rem 1rem;
font-size: 1rem;
}
#container{ /* Visa allt nu i 1 kolumn, 400 pixlar brett */
display: grid;
grid-template-columns: repeat(1, 380px);
}
#container-puppy-gallery, #container-puppy-gallery-subpage{
display: grid; /* Visa som Grid/fält */
grid-template-columns: repeat(1, 380px); /* 1 kolumn, 400 pixel bred */
}
}

[code]

Dold text

Projektkod main.js

[code]
// Simpelt JS som aktiverar .navbar-links när man klickar på hamburgarikonen
const toggleButton = document.getElementsByClassName('toggle-btn')[0]; // Markera hamburgarikonen
const navBarLinks = document.getElementsByClassName('navbar-links')[0]; // Placera sig vid navbar-links diven

/* När det klickas på hamburgarmenyn så lägger den till
all CSS-kod applicerad till klassen navbar-links*/
toggleButton.addEventListener('click', () =>{
navBarLinks.classList.toggle('active');
})
[code]

Dold text

Nu tågar jag vidare med Datateknik GR (B), Webbanvändbarhet, 7,5 hp och Datateknik GR (A), Grafisk teknik för webb, 7,5 hp där det är färre delmoment i båda kurserna.

Projektuppgiften i förstnämnda ovan är att skapa en användbar och tillgänglig webbplats för att kunna boka Färdtjänst. Detta är endast ur användbar- och tillgänglighetssynpunkt och inte rent funktionellt talat. Den handlar om att den ska kunna användas av så många som möjligt tack vare att den följer så många WCAG-riktlinjer som det bara går.

Projektuppgiften i sistnämnda ovan är att skapa en fiktiv webbshop med fokus på webboptimerad grafik (t.ex. AVIF och/eller WebP som primära bildformat samt webboptimerad video). Här får JavaScript användas för att hantera bildvisningar och bildspel. En produktsida ska även inkludera en film om produkten.

Jag gör även mitt bästa hädanefter att skriva på ett "begripligt" sätt. Då lever jag upp till förväntningarna med Webbanvändbarhetskursen!

På återseende!

Mvh,
WKL.
---------
✔️(B) HT2022 DT057G Datateknik GR (A), Webbutveckling I, 7,5 hp (distans)
✔️(A) HT2022 DT084G Datateknik GR (A), Introduktion till programmering i JavaScript, 7,5 hp (distans)
🚧 HT2022 DT068G Datateknik GR (B), Webbanvändbarhet, 7,5 hp (distans)
🚧 HT2022 DT200G Datateknik GR (A), Grafisk teknik för webb, 7,5 hp (distans)

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk

Pågående kurser just nu:
Datateknik GR (B), Webbanvändbarhet, 7,5 hp
Datateknik GR (A), Grafisk teknik för webb, 7,5 hp

Den 15:e januari 2023 är inlämningsdatum för båda projektuppgifterna.

Nästa två kurser börjar redan nästa dag och de är:
- Datateknik GR (A), Databaser, 7,5 hp (med "frågespråket" SQL här)
- Datateknik GR (B), Webbutveckling II, 7,5 hp (med nytt programmeringsspråk här: php)

Jag har skapat en separat tråd för Webbanvändbarhetskursens projektuppgift här:
https://www.sweclockers.com/forum/trad/1680894-skoluppgift-fa...

Den som vill får gärna testköra den och lösa "testuppgifterna" som jag angett i länken ovan.

Den andra kursen har jag inte ens påbörjat med ännu förutom några grafiska element:

Under den andra kursen har Lego varit vad jag bildbehandlat, filmat och även projektuppgiften kommer att involvera Lego.

Det blir en låstas-företagssida som säljer begagnat Lego i Norrland. Logotypens typsnitt är ett gratis typsnitt som även officiella Lego använder sig av. Lego-huvudet var gratis SVG-fil som jag sedan färglade och så hittade jag en YT-guide om hur man kunde få till "fluffet".

Det är en cirkel som fått högt Roughen-värde så det blivit "spikigt" och sedan drog jag ut den och gjorde två kopior till. Först hade jag bara gjort dussintals kopior i mindre storlekar men då blev SVG-filen väldigt stor. Och då en genomgående tanke med bildbehandling för webben är att "ha så hög kvalitet till så låg filstorlek som möjligt" så blev det att tänka om det hela.

Jag har fått fler lärdomar inom CSS, främst "Outer and Inner Values" vilket verkar handla om huruvida display-attributet påverkar föräldraelement eller barnelement. Denna förståelse saknade jag i tidigare kurser vilket gjorde mig helt förvirrad och förbannad när jag inte fick till flex och grid. Jag förstod inte att Flex och Grid handlar om hur barnelement ska visas och har ingen direkt påverkan på deras föräldraelement.

Inom JS har jag dock inte kommit så långt ännu. Jag måste sätta mig in i att loopa igenom DOM istället för att endast förlita mig på querySelector och dylikt. Det nästa är att lära sig objekthanteringen inom JS då jag knappt förstår grejen med listor i objekt i JS.

Fråga: Centrerade flex-items med wrap påslaget verkar ändå ibland inte hamna centrerat på nästa rad. Är detta på grund av någon marginal eller padding som följer med på nästa rad? Eller har det något med hur webbläsaren beräknar pixlarna? Jag använder inte flex-grow, flex-shrink eller flex-basis för jag förstår inte de attributen ännu. Jag använder mest flex-direction, justify-content, align-items, gap, och flex-wrap.

En annan lärdom jag fått inom CSS med Färdtjänst-projektet är att nu förstår jag varför CSS-ramverk som TailwindCSS och liknande finns. Det är verkligen mer angeläget att kunna skriva css-koden direkt i form av korta klassnamn än att behöva kolla vad det nu var för klass som gjorde si och så och vilka som krockar med vilka ju djupare man går i DOM-trädet!

På återseende!

Mvh,
WKL.
---------
✔️(B) HT2022 DT057G Datateknik GR (A), Webbutveckling I, 7,5 hp (distans)
✔️(A) HT2022 DT084G Datateknik GR (A), Introduktion till programmering i JavaScript, 7,5 hp (distans)
🚧 HT2022 DT068G Datateknik GR (B), Webbanvändbarhet, 7,5 hp (distans)
🚧 HT2022 DT200G Datateknik GR (A), Grafisk teknik för webb, 7,5 hp (distans)

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk

Inlämnad projektuppgift: HT2022 DT068G Datateknik GR (B), Webbanvändbarhet, 7,5 hp (distans)

Jag har nu lämnat in projektuppgiften för distanskursen DT068G Datateknik GR (B), Webbanvändbarhet, 7,5 hp (distans).

Den slutgiltiga webbplatsen finns här för den som vill provköra den: https://maka2207-fardtjanast.netlify.app/logga-ut.html

Observera som sagt att den endast simulerar funktionalitet och mycket är enbart "placeholders". På "Boka resa"-webbsidan får du dock återkoppling om du inte har gjort rätt i din bokning. Detta är gjort enbart för att visa (lärarna) att jag förstått processen och vad som är viktigt att ha med för att kunna höja Webbanvändbarhetsnivån.

I början av den kursen så var jag väldigt "anti" webbanvändbarhet för jag tyckte att det var så uppenbart. Det var inte förrän jag fick kolla på andras webbplatser inom samma ämne - Boka Färdtjänst På nätet - som jag insåg hur mycket val av tydliga ikoner, färger och tydlig information faktiskt tillför till användarupplevelsen.

Det var lite "Du värderar bara något först när du förlorat det"-stuk av det hela. I mina slutsatser i inlämningsrapporten nämnde jag samma sak och att det är ett matnyttigt "webbverktyg" i verktygslådan som blivande Fullstackutvecklare vilket detta distansbaserade Webbutvecklingsprogram syftar på att göra en till.

Jag har dock - som sagt tidigare - knappt påbörjat den andra kursen (DT068G Datateknik GR (B), Webbanvändbarhet, 7,5 hp (distans)) så möjligen blir det några dagar försenad inlämning. De två nästa kurserna är också som tidigare sagt mycket mer tungt kodfokuserade: PHP (Webbutveckling II) och MySQL (Databaser).

Förhoppningsvis kommer jag att kunna uppdatera oftare i de två kommande kurserna som är mer kodtunga och inte lika lattjolajban som de två nuvarande.

Nu ska jag fira lite med Bounty-kakor och sedan hoppa på nästa projektuppgift! 🥳

På återseende!

Mvh,
WKL.
---------
✔️(B) HT2022 DT057G Datateknik GR (A), Webbutveckling I, 7,5 hp (distans)
✔️(A) HT2022 DT084G Datateknik GR (A), Introduktion till programmering i JavaScript, 7,5 hp (distans)
🚧 (Inväntar slutbetyg) HT2022 DT068G Datateknik GR (B), Webbanvändbarhet, 7,5 hp (distans)
🚧 HT2022 DT200G Datateknik GR (A), Grafisk teknik för webb, 7,5 hp (distans)

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk

Slutbetyg för HT2022 DT068G Datateknik GR (B), Webbanvändbarhet, 7,5 hp (distans) & Två nya kurser börjat!

Jag har nu fått slutbetyg i HT2022 DT068G Datateknik GR (B), Webbanvändbarhet, 7,5 hp (distans)!

Alla 18 separata HTML-filer med tonvis av SVG-filer och övrig "webbanvändbarhet" lönade sig visst.

Jag har dock inte samma aningar om kursen HT2022 DT200G Datateknik GR (A), Grafisk teknik för webb, 7,5 hp (distans) då grafisk design - webbdesign i ett nötskal - är långt ifrån min starka sida.

Projektuppgiften till den kursen blev försenad inlämning så den kommer att rättas tidigast först 12 februari 2023. Jag tog mig i kragen och gick in och lade till fler <source srcset=""> under <picture> för att optimera bildbehandlingen (fler olika upplösningar).

Jag var lite crazy och gjorde för hela 28 olika upplösningar, från 200 pixlar i bredd upp till hela 4096 pixlar i bredd (även fast en produktbildsamling inte ens är så bred). Tack vare massbildomvandlingsprogrammet XnConvert så kunde jag snabbt konvertera bilderna i .PNG och .WEBP.

Får jag C i denna kurs så är jag oerhört nöjd för webbdesign är verkligen inte mina starka sida!

Nu börjar Webbutveckling II och Databaser i skarpt läge. Webbutveckling II innebär introduktion till php-webbserverspråket och Databaser innebär introduktion till relationsbaserat frågespråk SQL med databasen mySQL/MariaDB.

Projektuppgiften i Webbutveckling II är att antingen skapa en php-baserad bloggportal eller en portföljsida där man ska kunna registrera sig och visa upp sina . Båda valen ska använda sig av en databas (varav den parallella databaskursen just nu) samt vedertagna säkerhetsåtgärder med php.

Jag funderar på en bloggportal där man kan registrera sig (aktivera med slumpad engångsskapad aktiveringslänk), logga in, posta & redigera blogginlägg (jag tänkte koda ihop en enklare variant av färdiga CkEditorJS som finns där ute), samt kunna kommentera på andras skapade bloggar.

Hursomhelst så återkommer jag med vilka planer som faktiskt är rimliga att kunna implementera i denna kurs utifrån tillgänglig tid och mina php-kunskaper jag lyckas samla på mig.

På återseende!

Mvh,
WKL.
---------
✔️(B) HT2022 DT057G Datateknik GR (A), Webbutveckling I, 7,5 hp (distans)
✔️(A) HT2022 DT084G Datateknik GR (A), Introduktion till programmering i JavaScript, 7,5 hp (distans)
✔️(A) HT2022 DT068G Datateknik GR (B), Webbanvändbarhet, 7,5 hp (distans)
🚧(Inväntar slutbetyg) HT2022 DT200G Datateknik GR (A), Grafisk teknik för webb, 7,5 hp (distans)
🚧(Pågående) VT2023 Datateknik GR (B), Webbutveckling II, 7,5 hp (distans)
🚧(Pågående) VT2023 Datateknik GR (A), Databaser, 7,5 hp (distans)

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk
Medlem

Om du inte redan är inne på det så kör PDO när det gäller databasgrejerna.

Permalänk
Skrivet av ChrisDev:

Om du inte redan är inne på det så kör PDO när det gäller databasgrejerna.

Du syftar på detta? https://www.php.net/manual/en/class.pdo.php

Från Moment 4 (vi är i Moment 2) som behandlar databasanslutningar så ska vi lära oss tre olika sätt:
- mysqli - enkelt, snabbt och lättarbetat. Används för att ansluta mot MySQL-servrar.
- Objektorienterad mysqli - samma som ovan, fast vi istället skapar instanser av klasser och använder dess metoder för anslutning etc.
- PDO (PHP Data Objects) - till skillnad från mysqli kan vi med PDO även ansluta mot andra databasservrar än MySQL.

Vad anser du om de två övriga anslutningsmetoderna mysqli och OO-mysqli?

Mvh,
WKL.

Visa signatur

<WKL:"En kodrad i taget!";/>

Permalänk
Medlem
Skrivet av WebbkodsLärlingen:

Du syftar på detta? https://www.php.net/manual/en/class.pdo.php

Från Moment 4 (vi är i Moment 2) som behandlar databasanslutningar så ska vi lära oss tre olika sätt:
- mysqli - enkelt, snabbt och lättarbetat. Används för att ansluta mot MySQL-servrar.
- Objektorienterad mysqli - samma som ovan, fast vi istället skapar instanser av klasser och använder dess metoder för anslutning etc.
- PDO (PHP Data Objects) - till skillnad från mysqli kan vi med PDO även ansluta mot andra databasservrar än MySQL.

Vad anser du om de två övriga anslutningsmetoderna mysqli och OO-mysqli?

Mvh,
WKL.

Var längesen jag satt med PHP och MySQL nu men vill minnas att PDO var det föredragna sättet, särskilt om man inte kör MySQL då uppenbarligen.

Permalänk

Labb 1 (Databaser) & Moment 2 (Webbutveckling II) inlämnade

Nu börjar har jag mer eller mindre hamnat i fas för de två pågående kurserna (Databaser & Webbutveckling II).

Nedanför återfinns min inlämnade Labb 1 (eller "Moment 1") i Databaser-kursen [VT2023 DT003G Datateknik GR (A), Databaser, 7,5 hp (distans)]:

Det är en ER-modell för ett hypotetiskt filmuthyrningsföretag. Den har ej ännu rättats och den som upptäcker något "allvarligt uppenbart fel" får gärna anmärka på det!

Enligt en som läser år 2 i detta distansprogram sägs det att läraren verkar vara riktigt petig och att, "Det finns bara perfekta databaser som godkänns!". Det återstå att se hur det är med den saken. Många verkar än så länge ha fått kompletteringskrav (Fx) dock.

Resten av inlägget är kodkopior av Moment 2 i Webbutveckling II [VT2023 DT093G Datateknik GR (B), Webbutveckling II, 7,5 hp (distans)] då git-klonen är privat mellan mig och läraren (de använder GitHub Classroom för momenten).

Alla p-filer ("pages") ligger i rotkatalogen medan c-filer ("components") och config-filen ligger i underkatalogen include för den som vill lägga in allt och provköra med valfri Apache-server. CSS-filen i CSS-mapp såklart.

index.php

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=0; ?> <title><?= pageTitle("Startsida"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <?php include("p-questions.php");?> <?php include("include/c-footer.php");?>

Dold text

css/styles.css

/* =============================== NORMALIZING + FONTS - All Pages =============================== */ * { padding: 0; margin: 0; box-sizing: border-box; font-size: 1rem; font-family: 'Orbitron', sans-serif; } /* ================== <BODY> - All Pages ================== */ body{ width: 100%; margin: 0 auto; } /* ======================== FONT ELEMENTS - Standard ======================== */ h1{ font-size: 3rem; } h2{ font-size: 2.1rem; text-align: center; margin-bottom: 20px; } h1,h2,h3{ font-family: 'Orbitron'; font-weight: 800; } h3{ font-size: 1.4rem; text-align: center; margin-bottom: 10px; } span, i, p{ font-size: 1.3rem; line-height: 1.5; max-width: 50em; } /* ======================== .main-layout div ======================== */ .main-layout{ min-height: 100vh; min-height: 100dvh; display: grid; grid-template-rows: auto 1fr auto; background-color: rgb(56,59,58); } /* ======================== <nav> & .menu-btn ======================== */ .menu-btn{ display: inline-block; width: 200px; padding: 20px; font-size: 20px; font-weight: 700; cursor: pointer; border: none; background: #383b3a; color: #1bbb85; border: 2px solid #1bbb85; border-radius: 5px; text-align: center; margin: 10px; transition: background 400ms ease-out, color 400ms ease-out; } .current-btn{ box-shadow: 5px 5px 5px #1bbb85, 5px -5px 5px #1bbb85, -5px 5px 5px #1bbb85, -5px -5px 5px #1bbb85; } a{ color: #1bbb85; text-decoration: none; } .menu-btn:hover, .menu-btn:focus{ background: #1bbb85; border-radius: 5px; } .menu-btn:hover > a, .menu-btn:focus > a{ color: #383b3a; text-decoration: underline; } #nav-ul{ margin-top: 1rem; display: flex; flex-direction: row; flex-wrap: wrap; justify-content: space-evenly; } /* =========================== <main> =========================== */ main{ margin: 0 auto; padding-left: 10%; padding-right: 10%; } /* =========================== HEADER & FOOTER SETTINGS =========================== */ header, footer{ background: rgb(56,59,58); background: linear-gradient(0deg, rgba(56,59,58,1) 0%, rgba(27,187,133,1) 35%, rgba(128,31,125,1) 100%); } header{ padding: 3%; text-align: center; } footer{ padding: 3%; text-align: center; font-weight: bold; } #footer-text{ font-size: 0.9rem; line-height: 1.5; } .main-layout{ min-width: 390px; } /* =========================== page: questions =========================== */ .questions{ margin-top: 20px; margin-bottom: 20px; list-style-type:decimal; font-size: 2rem; } .questions > strong{ display: inline-block; font-size: 1.4rem; max-width: 50em; } /* =========================== page: variables =========================== */ .uppgifts-div{ background-color: white; height: fit-content; border: 2px solid #1bbb85; border-radius: 5px; padding: 20px; box-shadow: 2px 2px 3px #1bbb85, 2px -2px 3px #1bbb85, -2px 2px 3px #1bbb85, -2px -2px 3px #1bbb85; margin-bottom: 30px; } .variabel-ul{ display: flex; flex-direction: column; justify-content: center; align-items: center; } .variabel-ul > li{ width: 250px; } .variabelP{ font-size: 1.1rem; text-align: center; } .variabelP > a:hover, .variabelP > a:focus, .backA:hover, .backA:focus{ color: black; font-weight: bold; } /* =========================== page: iterations =========================== */ .courseList{ display: flex; flex-direction: column; } .courseList > li{ margin-left: 50px; } /* =========================== page: forms =========================== */ #form, #form2{ display: grid; grid-template-rows: 1fr 1fr; gap: 10px; } label{ display: block; } input{ width: 100%; } #send, #send2{ margin-bottom: 20px; } /* =========================== page: fileread =========================== */ /* =========================== MEDIA QUERIES =========================== */ @media only screen and (min-width: 1350px){ #nav-ul{ justify-content: center; align-content: center; } } @media only screen and (max-width : 600px){ #nav-ul{ justify-content: center; align-content: center; } .menu-btn{ padding: 10px; margin: 5px; width: 175px; } h1{ font-size: 2rem; } span, i, p{ text-align: justify; } }

Dold text

p-calculate.php

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=4; ?> <title><?= pageTitle("Startsida"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <div class="uppgifts-div"> <h3>Beräkna arean</h3> <?php // Kolla att Skicka knapp klickats if(isset($_POST['skicka2'])){ // Kolla att BÅDA fält matats in if(!empty($_POST['langd']) && !empty($_POST['bredd'])){ $langd = $_POST['langd']; $bredd = $_POST['langd']; echo "Längden $langd meter och bredden $bredd meter ger en area på " . $langd*$bredd . " kvadratmeter."; } // Annars skriv ut uppmaning om att mata in båda fält. else{ echo "<span style='color: red; font-size:1rem;'>Både längd och bredd måste anges!</span>"; } echo "<br><br><a href='p-forms.php' class='backA' style='display:block; text-align:center;'>Gå tillbaks till föregående sida</a>"; } ?> </div> <?php include("include/c-footer.php");?>

Dold text

p-conditions.php

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=2; ?> <title><?= pageTitle("Villkor"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <h2>Moment 2 - 2. Villkor</h2> <div class="uppgifts-div"> <h3>Datum/klockslag: ÅÅÅÅ-MM-DD:TT.MM</h3> <?php $dateNow = date('Y-m-d') . ':' . date('H.i'); echo "<p style='text-align: center;'>Datum/klockslag: $dateNow<br><br></p>"; ?> <h3>Idag är det (inte) söndag</h3> <p style='text-align: center;'>Idag är det <?php $isSunday = date('D'); echo $isSunday === 'Sun' ? ' söndag' : ' inte söndag'; ?>.<br><br></p> <h3>Det är morgon/förmiddag/eftermiddag eller kväll/natt</h3> <p style='text-align: center;'>Det är <?php // Gammalt variabelnamn från JS-Intro-kursen - gammal favorit! $getHM = date('H:i'); if($getHM >= '06:00' && $getHM <= '08:59'){ echo 'morgon'; } else if($getHM >= "09:00" && $getHM <= '11:59'){ echo 'förmiddag'; } else if($getHM >= '12:00' && $getHM <= '17:59'){ echo 'eftermiddag'; } else if($getHM >= '18:00' && $getHM <= '05:59'){ echo 'kväll/natt'; } ?>.</p> <h3>Idag är det <i>Veckodag</i></h3> <p style='text-align: center;'>Idag är det <?php $weekDay = date('D'); switch($weekDay){ case 'Mon': echo 'måndag'; break; case 'Tue': echo 'tisdag'; break; case 'Wed': echo 'onsdag'; break; case 'Thu': echo 'torsdag'; break; case 'Fri': echo 'fredag'; break; case 'Sat': echo 'lördag'; break; case 'Sun': echo 'söndag'; break; }?>.</p><br> <?php echo nextPage('p-iterations','3. Upprepningar');?> </div> <?php include("include/c-footer.php");?>

Dold text

p-fileread.php

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=5; ?> <title><?= pageTitle("5. Filinläsning"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <h2>Moment 2 - 5. Filinläsning</h2> <div class="uppgifts-div"> <h3>Inläsning av extern textfil</h3> <?php // Om filen ej existerar. if(!file_exists('courses.txt')){ echo "Filen kunde inte hittas!"; } // Öppnna annars för inläsning ('r') else{ echo "Filen finns. Öppnar den nu och skriver ut som en punktlista...<br><br>"; $fp = fopen('courses.txt','r'); // Skriver ut <ul>-element echo "<ul style='margin-left: 30px;'>"; // feof = slutet på filen så betyder "så länge INTE // slutet på filen har nåtts för öppnad fil $fp så..." while(!feof($fp)){ echo "<li>" . fgets($fp) . "</li>"; } // Avsluta punktlistan, skapa luft nedanför innan nästa länk echo "</ul><br>"; }?> <?php echo nextPage('index','0. Startsida/Frågor');?> </div> <?php include("include/c-footer.php");?>

Dold text

p-forms.php

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=4; ?> <title><?= pageTitle("4. Formulär"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <h2>Moment 2 - 4. Formulär</h2> <div class="uppgifts-div"> <h3>Del 1 - Skicka data med GET</h3> <?php // Kolla att Skicka knapp klickats if(isset($_GET['skicka1'])){ // Kolla att BÅDA fält matats in if(!empty($_GET['fname']) && !empty($_GET['ename'])){ $fornamn = $_GET['fname']; $efternamn = $_GET['ename']; // Skriv ut för -& efternamn echo "Hej " . $fornamn . " " . $efternamn . "!"; } // Annars skriv ut uppmaning om att mata in båda fält. else{ echo "<span style='color: red; font-size:1rem;'>Du måste ange både för- och efternamn!</span>"; } } ?> <form id="form" action="p-forms.php" method="GET"> <div> <label for="fnamn">Förnamn:</label> <input id="fnamn" type="text" name="fname"> </div> <div> <label for="enamn">Efternamn:</label> <input id="enamn" type="text" name="ename"> </div> <input type="submit" name="skicka1" value="Skicka" id="send"> </form> <h3>Del 2 - Skicka data med POST</h3> <form id="form2" action="p-calculate.php" method="POST"> <div> <p style="font-size: 1rem; margin-bottom: 5px;">Beräkna arean på en yta genom att ange längd och bredd.</p> <label for="langd">Längd:</label> <input id="langd" type="number" name="langd"> </div> <div> <label for="bredd">Bredd:</label> <input id="bredd" type="number" name="bredd"> </div> <input type="submit" value="Skicka" name="skicka2" id="send2"> </form> <?php echo nextPage('p-fileread','5. Filinläsning');?> </div> <?php include("include/c-footer.php");?>

Dold text

p-iterations.php

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=3; ?> <title><?= pageTitle("3. Upprepningar"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <h2>Moment 2 - 3. Upprepningar</h2> <div class="uppgifts-div"> <h3>Del 1</h3> <p style='text-align: center;'> <?php for($i = 10; $i>0; $i--){ echo $i . '<br>'; } ?> </p><br> <h3>Del 2</h3> <p style="margin-bottom: 10px; text-align: center;">Kurslistan i den ordning kurserna ges:</p> <?php $arrCourses = array( "Webbutveckling I","Introduktion till programmering med JavaScript", "Grafisk teknik för webb för webb","Webbanvändbarhet","Webbutveckling II","Databaser", "Webbdesign för CMS","Webbutveckling III" ); // Skriv ut echo '<ul class="courseList">'; foreach($arrCourses as $arrCourse){ echo "<li>$arrCourse</li>"; } echo '</ul>'; ?><br> <h3>Del 3</h3> <p style="margin-bottom: 10px; text-align: center;">Kurslistan i bokstavsordning:</p> <?php // Sortera och skriv ut sort($arrCourses); echo '<ul class="courseList">'; foreach($arrCourses as $arrCourse){ echo "<li>$arrCourse</li>"; } echo '</ul>'; ?><br> <?php echo nextPage('p-forms','4. Formulär');?> </div> <?php include("include/c-footer.php");?>

Dold text

p-questions.php

<h2>Moment 2 - Frågor</h2> <ul> <li class="questions"> <strong>Har du tidigare erfarenhet av utveckling med PHP?</strong> <br><p>Nej, inte i denna utsträckning. Jag har för länge sedan kikat på det men förvirrats av dess sätt att användas på som exempelvis att skriva den direkt inuti HTML-kod. För säkert 10 år sedan öppnade jag upp diverse Wordpress-php-filer och förstod så klart ingenting.<br><br>Det blir roligt att öppna om samma Wordpress-filer efter CMS-kursen som då fokuserar på just Wordpress om jag minns rätt</p> </li> <li class="questions"> <strong>Beskriv kortfattat vad du upplever är fördelarna med att använda PHP för att skapa webbplatser.</strong> <br><p>Två starka fördelar:<br><br>1) Den första är att kunna skapa mer dynamiska webbplatser snabbare tack vare det modulära tänket vilket jag älskar. Jag hoppas vi aldrig går tillbaka till "vanilla HTML" på det viset utan får hålla på med både modulär php, javascript, och så vidare. C# är ju objektorienterat och OOP anammar ju delvis det där modulära tänket som jag rent av älskar!<br><br>2) Den andra stora fördelen är att kunna skapa säkra webbplatser tack vare att php-koden döljs undan. Jag upptäckte även att kommentarer inuti php döljs men om man skriver HTML-kommentarer precis utanför php-koden så syns de slags kommentarerna.</p> </li> <li class="questions"> <strong>Hur har du valt att strukturera upp dina filer och kataloger?</strong> <br><p>Jag har <i>index.php</i> i "webbserverns" rotkatalog. I mappen <i>include</i> så finns filer med prefix i filnamnen för att lättare veta vad som syftar på vad: "c" = component/komponent till en viss sida, "f" = funktion till en viss sida.<br><br>Notera således att funktionerna i <i>config.php</i> är medvetet globala då den filen ändå inkluderas på alla sidor. En första tanke var dock katalogerna <i>page</i> för sidorna (det vill säga, "p" = page/sida) och <i>function</i> för funktionerna.<br><br>Men det skippade jag då jag har haft problem med att länka rätt mellan filer som ligger i olika nivåer i olika kataloger än så länge.</p> </li> <li class="questions"> <strong>Har du följt guiden, eller skapat på egen hand?</strong> <br><p>Jag har delvis följt guiden för den grundläggande strukturen sedd i <i>index.php</i>. Då var det bara att kopiera och klistra in för alla undersidor och inkludera nödvändig php-kod där för att lösa respektive uppgift.</p> </li> <li class="questions"> <strong>Har du gjort några förbättringar eller vidareutvecklingar av guiden (om du följt denna)?</strong> <br><p>Vad som kan tolkas som förbättringar jag har gjort är i form av funktioner som kan anropas. <br><br>Exempelvis så sätts ett värde för vilken sida man är på vilket sedan anropas i <i>c-header.php</i> för att markera vilken nuvarande sida är. Detta syns <i>$currentBtn</i>-variabeln i början av undersidorna. <br><br>Annan funktion är den som anropas inuti varje <i>title</i>-element för att skriva ut undersidans namn efter webbplatsens namn. <br><br>En ytterligare funktion är den som anropas i slutet på varje uppgift: <i>nextPage</i> som returnar länknamn (ej filändelse) och strängen inom parentesen. Medför snabbare navigering. <br><br>Funktionerna finns i <i>config.php</i> då de används på varje webbsida, exklusive <i>nextPage</i> på denna sida.</p> </li> <li class="questions"> <strong>Vilken utvecklingsmiljö har du använt för att genomföra uppgiften (editor, webbserver-paket (Xampp, Lamp, Wamp eller liknande)?</strong> <br><p>Jag har använt mig av VSCode som huvudsaklig utvecklingsmiljö. Jag har använt mig av XAMPP som webbserver för PHP.<br><br>Installerade även <i>Apache</i> direkt som Service i Windows 10 för att slippa behöva starta XAMPP manuellt varje gång jag startar datorn.</p> </li> <li class="questions"> <strong>Har något varit svårt med denna uppgift?</strong> <br><p>Ja, CSS som vanligt. En sak jag inte förstod var varför jag fick felmeddelande när jag valde att inkludera typsnitt lokalt, alltså med <i>@font-face</i>.<br><br>Då stod det i <i>Console</i> att <i>"download failed font"</i> (trots att jag såg att sökvägen till filen var rätt med kataloger och filnamn).<br><br>Så jag fick använda direktlänk till Google Fonts för det futuristiska typsnittet på denna webbplats. Jag vet inte om jag hade valt fel slags varianter av typsnitten (det var .tff) eller om det är något php och typsnitt jag inte begriper mig ännu på.</p> </li> </ul>

Dold text

p-variables.php

<?php include("include/config.php");?> <?php // Markera nuvarande sida med Box-shadow under dess Menyknapp (se c-nav.php) $currentBtn=1; ?> <title><?= pageTitle("1. Variabler"); ?></title> <?php include("include/c-header.php");?> <?php include("include/c-nav.php");?> <h2>Moment 2 - 1. Variabler</h2> <div class="uppgifts-div"> <h3>Enskilda</h3> <?php // Skapar variabler $name = "WebbKodsLärlingen"; $age = "33"; $email = "WKL@yolo.com"; echo "<ul class='variabel-ul'><li>$name</li><li>$age</li><li>$email</li></ul><br>"; ?> <h3>Kombinerade</h3> <?php echo "<p class='variabelP'>Hej! Jag heter $name" . ", är " . $age . " år gammal och nås på följande e-post: " . "<a href='mailto:$email'>$email</a>.</p><br>"; echo nextPage('p-conditions','2. Villkor'); ?> </div> <?php include("include/c-footer.php");?>

Dold text

include/c-footer.php

</main> <footer> <span id="footer-text">© 2023 WebbKodsLärlingen<br> VT2023 DT093G Datateknik GR (B), Webbutveckling II, 7,5 hp (distans) - Moment 2</span> </footer> </div> </body> </html>

Dold text

include/c-header.php

</head> <body> <div class="main-layout"> <div> <header> <h1>FRAMTIDSBLOGGEN I PHP MOMENT 2</h1> </header>

Dold text

include/c-nav.php

<nav> <ul id="nav-ul"> <li class="menu-btn <?php echo setCurrentBtn(0); ?>">0. <a href="index.php"> Startsida </a> </li> <li class="menu-btn <?php echo setCurrentBtn(1); ?>">1. <a href="p-variables.php"> Variabler </a> </li> <li class="menu-btn <?php echo setCurrentBtn(2); ?>">2. <a href="p-conditions.php"> Villkor </a> </li> <li class="menu-btn <?php echo setCurrentBtn(3); ?>">3. <a href="p-iterations.php"> Upprepningar </a> </li> <li class="menu-btn <?php echo setCurrentBtn(4); ?>">4. <a href="p-forms.php"> Formulär </a> </li> <li class="menu-btn <?php echo setCurrentBtn(5); ?>">5. <a href="p-fileread.php"> Filinläsning </a> </li> </ul> </nav> </div> <main>

Dold text

include/config.php

<?php // PHP-kod konfiguration innan någon faktisk HTML ens laddas först // Funktion som ger extra CSS-klass så att vald menyknapp får grön box-shadow function setCurrentBtn($btnNumber){ return $GLOBALS['currentBtn']==$btnNumber ? 'current-btn' : ''; } // Funktion som skriver ut anropad sträng i <title>-element function pageTitle($pTitle){ return "FRAMTIDSBLOGGEN i PHP Moment 2 | " . $pTitle; } // Funktion som länkar till nästa uppgiftslösning (undersida) function nextPage($nPage, $pName){ return "<p style='text-align: center;' class='variabelP'><a href='$nPage.php'>Nästa uppgift ($pName)</a></p>"; } // Här slutar PHP-koden och HTML laddas in. Dessa kommentarer syns ej i webbläsaren! :-) ?> <!DOCTYPE html> <html lang="sv"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="css/styles.css"> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;80..." rel="stylesheet">

Dold text

På återseende!

Mvh,
WKL.
---------
✔️(B) HT2022 DT057G Datateknik GR (A), Webbutveckling I, 7,5 hp (distans)
✔️(A) HT2022 DT084G Datateknik GR (A), Introduktion till programmering i JavaScript, 7,5 hp (distans)
✔️(A) HT2022 DT068G Datateknik GR (B), Webbanvändbarhet, 7,5 hp (distans)
🚧(Inväntar slutbetyg) HT2022 DT200G Datateknik GR (A), Grafisk teknik för webb, 7,5 hp (distans)
🚧(Pågående) VT2023 DT093G Datateknik GR (B), Webbutveckling II, 7,5 hp (distans)
🚧(Pågående) VT2023 DT003G Datateknik GR (A), Databaser, 7,5 hp (distans)

Visa signatur

<WKL:"En kodrad i taget!";/>