Forum Discussion
Cheese Party
Wow, really amazing. Are you able to share the course or some of the code you used in this course? Thank you so much.
- Kate_Golomshtok18 days agoCommunity Member
Thank you, I’m glad you liked my example. Most of the blocks can be created quite easily with the help of AI or by finding ready-made examples online.
I’d also like to share a block with a “matching lines” mechanic. It’s my favorite block in the project. I remember how many hours it used to take me to set up a similar interaction in Storyline (spoiler: a lot 😆). Now it can be done much faster, and there’s no need to worry that something might accidentally break if a user starts experimenting with it.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cheese Around the World</title>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Poppins', sans-serif;
margin: 0;
padding: 20px;
background: #fff;
}
h2 {
text-align: center;
color: #d9991e;
font-size: 1.8rem;
}
.instructions {
text-align: center;
margin-bottom: 20px;
font-size: 1.1rem;
}
.container {
display: flex;
justify-content: space-between;
max-width: 900px;
margin: 0 auto;
position: relative;
}
.cheese-list, .country-list {
display: flex;
flex-direction: column;
gap: 15px;
position: relative;
}
.item {
padding: 12px 18px;
background-color: #fef6e4;
border-radius: 8px;
cursor: pointer;
font-size: 1.1rem;
text-align: left;
user-select: none;
position: relative;
transition: transform 0.2s;
}
.item:hover { transform: scale(1.03); }
.item.correct { background-color: #d4edda; }
.item.incorrect { background-color: #f8d7da; }
.item::after {
content: '';
width: 14px;
height: 14px;
background-color: #fbc02d;
border-radius: 50%;
position: absolute;
top: 50%;
transform: translateY(-50%);
z-index: 2;
}
.cheese-list .item::after { right: -7px; }
.country-list .item::after { left: -7px; }
#svg-lines {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 1;
}
button {
margin: 10px 5px 0;
background-color: #fbc02d;
color: #000;
border: none;
padding: 10px 18px;
border-radius: 6px;
cursor: pointer;
font-weight: 400;
font-size: 1.1rem;
display: none;
}
button:hover { opacity: 0.9; }
.feedback {
margin: 10px 0 0;
text-align: center;
font-size: 1.1rem;
font-weight: 500;
}
</style>
</head>
<body>
<h2>Cheese Around the World</h2>
<div class="instructions">
Connect each cheese to its country of origin by drawing a horizontal line. Click "Submit" after connecting all.
</div>
<div class="container">
<div class="cheese-list"></div>
<svg id="svg-lines"></svg>
<div class="country-list"></div>
</div>
<div style="text-align:center;">
<div class="feedback" id="feedback"></div>
<button id="submit">Submit</button>
<button id="tryAgain">Try Again</button>
<button id="showAnswers">Show Correct Answers</button>
</div>
<audio id="correct-sound" src="https://actions.google.com/sounds/v1/cartoon/clang_and_wobble.ogg"></audio>
<audio id="wrong-sound" src="https://orangefreesounds.com/wp-content/uploads/2015/01/Fail-sound-effect-2.mp3"></audio>
<script>
const data = [
{cheese:"Brie", country:"France"},
{cheese:"Cheddar", country:"England"},
{cheese:"Gouda", country:"Netherlands"},
{cheese:"Mozzarella", country:"Italy"},
{cheese:"Emmental", country:"Switzerland"}
];
function shuffle(array){ return array.sort(()=>Math.random()-0.5); }
const cheesesContainer = document.querySelector('.cheese-list');
const countriesContainer = document.querySelector('.country-list');
shuffle(data).forEach(d=>{
const c = document.createElement('div');
c.className='item';
c.dataset.cheese=d.cheese;
c.textContent=d.cheese;
cheesesContainer.appendChild(c);
});
shuffle(data).forEach(d=>{
const cn = document.createElement('div');
cn.className='item';
cn.dataset.country=d.country;
cn.textContent=d.country;
countriesContainer.appendChild(cn);
});
const cheeses = document.querySelectorAll('.cheese-list .item');
const countries = document.querySelectorAll('.country-list .item');
let selected = null;
let matches = {};
const svg = document.getElementById('svg-lines');
const feedback = document.getElementById('feedback');
const submitBtn = document.getElementById('submit');
const tryAgainBtn = document.getElementById('tryAgain');
const showAnswersBtn = document.getElementById('showAnswers');
const correctSound = document.getElementById('correct-sound');
const wrongSound = document.getElementById('wrong-sound');
wrongSound.volume = 0.25;
function getPoint(el, side){
const rect = el.getBoundingClientRect();
const parentRect = svg.getBoundingClientRect();
const y = rect.top + rect.height/2 - parentRect.top;
const x = side==='right' ? rect.right - 7 - parentRect.left : rect.left + 7 - parentRect.left;
return {x, y};
}
function selectItem(el){
selected = el;
document.querySelectorAll('.item').forEach(i=>i.style.border='');
el.style.border='2px solid #fbc02d';
}
function connectItem(el){
if(!selected || selected===el) return;
const leftEl = selected.dataset.cheese ? selected : el;
const rightEl = selected.dataset.country ? selected : el;
if(matches[leftEl.dataset.cheese]){
svg.removeChild(matches[leftEl.dataset.cheese].line);
}
for(let key in matches){
if(matches[key].country===rightEl.dataset.country){
svg.removeChild(matches[key].line);
delete matches[key];
}
}
const start = getPoint(leftEl,'right');
const end = getPoint(rightEl,'left');
const line = document.createElementNS('http://www.w3.org/2000/svg','line');
line.setAttribute('x1', start.x);
line.setAttribute('y1', start.y);
line.setAttribute('x2', end.x);
line.setAttribute('y2', end.y);
line.setAttribute('stroke', '#fbc02d');
line.setAttribute('stroke-width', 3);
svg.appendChild(line);
matches[leftEl.dataset.cheese] = {country:rightEl.dataset.country, line, cheeseEl:leftEl, countryEl:rightEl};
selected.style.border='';
selected=null;
if(Object.keys(matches).length===cheeses.length){
submitBtn.style.display='inline-block';
}
}
cheeses.forEach(c=>c.addEventListener('click', ()=>selectItem(c)));
countries.forEach(c=>c.addEventListener('click', ()=>connectItem(c)));
submitBtn.addEventListener('click', ()=>{
submitBtn.style.display='none';
let correctCount=0;
for(let ch in matches){
if(matches[ch].country===data.find(d=>d.cheese===ch).country){
matches[ch].cheeseEl.classList.add('correct');
matches[ch].countryEl.classList.add('correct');
correctCount++;
} else {
matches[ch].cheeseEl.classList.add('incorrect');
matches[ch].countryEl.classList.add('incorrect');
}
}
cheeses.forEach(c => c.style.pointerEvents = 'none');
countries.forEach(c => c.style.pointerEvents = 'none');
if(correctCount===cheeses.length){
feedback.textContent="✅ Great job! All cheeses matched correctly!";
correctSound.play();
} else {
feedback.textContent="❌ Some matches are incorrect. Try again or click 'Show Correct Answers'.";
wrongSound.play();
tryAgainBtn.style.display='inline-block';
showAnswersBtn.style.display='inline-block';
}
});
tryAgainBtn.addEventListener('click', ()=>{
Object.values(matches).forEach(m=> svg.removeChild(m.line));
Object.values(matches).forEach(m=>{
m.cheeseEl.classList.remove('correct','incorrect');
m.countryEl.classList.remove('correct','incorrect');
});
matches={};
feedback.textContent='';
submitBtn.style.display='none';
tryAgainBtn.style.display='none';
showAnswersBtn.style.display='none';
cheeses.forEach(c => c.style.pointerEvents = '');
countries.forEach(c => c.style.pointerEvents = '');
});
showAnswersBtn.addEventListener('click', ()=>{
Object.values(matches).forEach(m=> svg.removeChild(m.line));
matches={};
cheeses.forEach(c=>{
const correctCountry = data.find(d=>d.cheese===c.dataset.cheese).country;
const cn = Array.from(countries).find(x=>x.dataset.country===correctCountry);
const start = getPoint(c,'right');
const end = getPoint(cn,'left');
const line = document.createElementNS('http://www.w3.org/2000/svg','line');
line.setAttribute('x1', start.x);
line.setAttribute('y1', start.y);
line.setAttribute('x2', end.x);
line.setAttribute('y2', end.y);
line.setAttribute('stroke', '#28a745');
line.setAttribute('stroke-width', 3);
svg.appendChild(line);
matches[c.dataset.cheese]={country:correctCountry,line,cheeseEl:c,countryEl:cn};
});
document.querySelectorAll('.item').forEach(i=>{
i.classList.remove('incorrect');
i.classList.add('correct');
});
cheeses.forEach(c => c.style.pointerEvents = 'none');
countries.forEach(c => c.style.pointerEvents = 'none');
tryAgainBtn.style.display='none';
showAnswersBtn.style.display='none';
feedback.textContent = "Here is the correct match";
});
</script>
</body>
</html>
Related Content
- 4 months ago
- 2 years ago