Johan Broddfelt
/* Comments on code */

Spelling exercise

Since my post about the Multiplication exercise got more response than any of my other posts on LinkedId, I feel that I have to do another attempt. But this time it is about spelling. I have done an exercise for my kids when they were about to learn letters, but it is hard to find images for all words. Instead I wanted to use audio for this application and found these neat lines that could play an audio file using javascript:

var audio = new Audio('your_audio_file.mp3');
audio.play();

This time we also have to take some issues with words into account, like capitalisation of letters. When comparing we want "Chef" to be the same as "chef". The way we solve that is to use the string function toLowerCase(). There will also be new words added each week thus we add a class .week_btn, note the basic styling of the buttons in the CSS below.

body {
  font-family: Arial, Helvetica, sans-serif;
}
div {
  padding: 0.5rem;
}
#facit, #ord {
  font-size: 24px;
}
#svar, #svara {
  font-size: 24px;
}
.ratt {
  color: #009900;
}
.fel {
  color: #990000;
}
.week_btn {
  display: inline-block;
  background: #ccc;
  margin: 0.5rem;
  border-radius: 1rem;
  cursor: pointer;
  box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.2);
  height: 2rem;
  line-height: 2rem;
  padding-left: 1rem;
  padding-right: 1rem;
  color: #000;
  text-decoration: none;
  font-size: 1.3rem;
}
.week_btn:hover {
  box-shadow: 2px 2px 5px 1px rgba(0, 0, 0, 0.2);
}
.week_btn.active {
  background: #99ccff;
}

We still want to keep the HTML to a minimum and add the buttons in the javascript. But we add a div with the id "veckoord", where we will place the week buttons. The rest of it stays the same as the multiplication code.

<a href="../" style="border-radius: 4px; border: 2px solid #eee; background: #f7f7f7; padding: 5px; color: #333; text-decoration: none;">Tillbaka</a>
<br><br>
<h1>Rättstavning</h1>
<div id="veckoord"></div>
<input type="button" id="ord" value="Spela upp">
<div><input type="text" id="svar"> <input type="button" id="svara" value="Svara"></div>
<div id="facit"></div>

In the javascript we must do a bit more work. I start by adding three class management functions in the top. The reason for this is to make the code below look cleaner and more easily understood.
Instead of the list of numbers to multiply we here add a variable called ordObject and fill it with the weeks we want to work with and within the weeks we add a list of all the words that was introduced as homework that week. Note that all words are written twice, this is because in some cases å, ä and ö cause issues in file names, and I anticipate future words to also contain spaces. For convenience I therefor use the file name of the audio file as a key and after the colon I write the word that should be spelled. This should be future proof enough.
The next thing that happens when the page is loading is that the labels for the week buttons are extracted from the ordObject and buttons are added to the div "veckoord". Then the last, most recent, button is set to active. This will enable the last weeks words by default. In the bottom of the file there is a script that add an event listener to each of the week buttons toggling them active and inactive as they are pressed. When pressed the new list of active words are generated and the game starts anew.

"use strict";
/*---- general functions ----*/ 
// Manage classes in html elements
function hasClass(ele, cls) {
  return !!ele.className.match(new RegExp('(s|^)' + cls + '(s|$)'));
}

function addClass(ele, cls) {
  if (!hasClass(ele, cls))
    ele.className += " " + cls;
}

function removeClass(ele, cls) {
  if (hasClass(ele, cls)) {
    var reg = new RegExp('(s|^)' + cls + '(s|$)');
    ele.className = ele.className.replace(reg, ' ');
  }
}

/*----------------------*/

var ord = document.getElementById('ord');
var svar = document.getElementById('svar');
var svara = document.getElementById('svara');
var facit = document.getElementById('facit');
var antalFel = 0;
var ordObjekt = {
  'Vecka 40 (K/TJ)': {
    'karlek': 'Kärlek',
    'kejsare': 'Kejsare',
    'kyckling': 'Kyckling',
    'kyrka': 'Kyrka',
    'tjej': 'Tjej',
    'tjock': 'Tjock',
    'tjusig': 'Tjusig',
    'tjuv': 'Tjuv'
  },
  'Vecka 41 (CH)': {
    'champinjon': 'Champinjon',
    'chockad': 'Chockad',
    'choklad': 'Choklad',
    'chauffor': 'Chaufför',
    'chef': 'Chef',
    'charmig': 'Charmig',
    'chatta': 'Chatta',
    'charkuteri': 'Charkuteri'
    }
};

// Översätt objeket till två arrayer
var filLista = [];
var ordLista = [];
var vecka = 0;
var veckoOrd = document.getElementById('veckoord')
veckoOrd.innerHTML = '<strong>Välj vilka veckoläxor du vill träna på</strong><br>';
for (var week in ordObjekt) {
  if (!ordObjekt.hasOwnProperty(week)) continue;
  veckoOrd.innerHTML = veckoOrd.innerHTML + '<div id="vecka_' + vecka + '" class="week_btn">' + week + '</div>';
  vecka++;
}

var lastBtn = document.getElementById('vecka_' + (vecka-1));
addClass(lastBtn, 'active');

var cnt = 0;
var laddaOrd = function() {
  filLista = [];
  ordLista = [];
  var vecka = 0;
  for (var week in ordObjekt) {
    if (!ordObjekt.hasOwnProperty(week)) continue;
    var btn = document.getElementById('vecka_' + vecka);
    if (hasClass(btn, 'active')) {
      for (var key in ordObjekt[week]) {
        if (!ordObjekt[week].hasOwnProperty(key)) continue;
        filLista[filLista.length] = key;
        ordLista[ordLista.length] = ordObjekt[week][key];
      }
    }
    vecka++;
  }
  var ord = document.getElementById('ord');
  ord.removeAttribute('data-facit');
  cnt = ordLista.length;
  return cnt;
}
laddaOrd();

var valideraSvar = function() {
  var ord = document.getElementById('ord');
  if (ord.getAttribute('data-facit') !== null) {
    if (svar.value == '') { return false; }
    if (svar.value.toLowerCase() === ord.getAttribute('data-facit').toLowerCase()) {
      facit.innerHTML = '<span class="ratt">RÄTT SVAR: ' + ord.getAttribute('data-facit') + '</span>';
      filLista.splice(ord.getAttribute('data-ord_id'), 1);
      ordLista.splice(ord.getAttribute('data-ord_id'), 1);
    } else {
      facit.innerHTML = '<span class="fel">FEL SVAR: Ordet var <b>' + ord.getAttribute('data-facit') + '</b>, du svarade ' + svar.value + '</span>';
      antalFel++;
    }
    facit.innerHTML = facit.innerHTML + '<br>Du har <b>' + ordLista.length + '</b> ord kvar att klara av.'
                    + '<div style="border: 3px solid #333; padding: 1px; width: 300px;"><div style="height: 20px; padding: 0; background: #090; width: ' + Math.ceil(100-ordLista.length/cnt*100) + '%;"></div></div>';
    if (antalFel > 0) {
      //facit.innerHTML = facit.innerHTML + '<br>Antal felaktiga svar: <b>' + antalFel + '</b>';
    }
  }
		
  if (ordLista.length > 0) {
    var ordId = Math.floor(Math.random() * ordLista.length);
    svar.value = '';
    ord.setAttribute('data-file', filLista[ordId]);
    ord.setAttribute('data-facit', ordLista[ordId]);
    ord.setAttribute('data-ord_id', ordId);

    if (ord.getAttribute('data-file') === null) {
      alert('Du måste välja minst en vecka att träna på.');
      return false;
    }
    var audio = new Audio('ljud/' + ord.getAttribute('data-file') + '.mp3');
    audio.play();

    svar.focus();
  } else {
    if (ord.getAttribute('data-facit') !== null) {
      veckoOrd.remove();
      ord.remove();
      svar.remove();
      svara.remove();
      facit.innerHTML = facit.innerHTML + 'GRATTIS! Du klarade alla orden =)';
      if (antalFel > 0) {
        //facit.innerHTML = facit.innerHTML + '<br>Antal felaktiga svar: <b>' + antalFel + '</b>';
      } else {
        facit.innerHTML = facit.innerHTML + '<br><b>Utan ett enda fel!!!</b>';
      }
      if (antalFel > 54) {
        antalFel = 54;
      }
      facit.innerHTML = facit.innerHTML + '<br>Din skill level idag är <b>' +  Math.ceil(100*(1-Math.log(1+antalFel)/4)) + '</b> av 100<br><br><a href="index.html" class="week_btn">Träna igen</a>';
    }
    var ord = document.getElementById('ord');
    ord.removeAttribute('data-file');
    ord.removeAttribute('data-facit');
    ord.removeAttribute('data-ord_id');
  }
}

valideraSvar();

svara.addEventListener('click', valideraSvar);
document.addEventListener('keydown', function(e) {
  if (e.keyCode === 13) {
    valideraSvar();
  }
});
ord.addEventListener('click',  function(e) {
  if (ord.getAttribute('data-file') === null) {
    alert('Du måste välja minst en vecka att träna på.');
    return false;
  }
  var audio = new Audio('ljud/' + ord.getAttribute('data-file') + '.mp3');
  audio.play();
});

var weekBtn = document.getElementsByClassName('week_btn');
var i = 0;
while (i < weekBtn.length) {
  weekBtn[i].addEventListener('click', function(e) {
    var btn = document.getElementById(e.srcElement.getAttribute('id'));
    if (hasClass(btn, 'active')) {
      removeClass(btn, 'active');
    } else {
      addClass(btn, 'active');
    }
    laddaOrd();
    valideraSvar();
  });
  i++;
};

With this setup, all I have to do when the teacher releases a new list of words is to record them and add a new week object to the ordObject, upload the audio files named into the folder ljud/ as described in the week object, and the new words are available to train on. If you want to try it out or download the page and play around with it your self, you find everything at the spelling page.

- Spelling, Homework, Exercise

Follow using RSS

<< Select dependant on other select

Comment

Name
Mail (Not public)
Send mail uppdates on new comments
0 comment