Hello i have a problem with javascript in bubble.
Ive created a word matching game using html, css and js that looks like this perfectly on JS:
The problem is that the when i put it on Bubble it doesnt render the same.
I created a html file where i included de html and the Css
And on page load i run Javascript to make the matching game work .
When te page loads this is what we get:
the title and the word cards are missing.
Our Html,Css is:
Juego de Vocabulario¡Une las palabras con sus definiciones!
Descargar <!-- Vocabulary data -->
<div id="vocabulary-data">
<div class="vocabulary-title">Biohuerto Arturito</div>
<div class="vocabulary-pair" data-index="0">
<span class="term">Biohuerto</span>
<span class="definition">Un espacio donde cultivamos plantas y vegetales de manera orgánica</span>
</div>
<div class="vocabulary-pair" data-index="1">
<span class="term">Compost</span>
<span class="definition">Material orgánico descompuesto que usamos como fertilizante natural</span>
</div>
<div class="vocabulary-pair" data-index="2">
<span class="term">Semilla</span>
<span class="definition">La parte de la planta que usamos para cultivar nuevas plantas</span>
</div>
<div class="vocabulary-pair" data-index="3">
<span class="term">Germinación</span>
<span class="definition">El proceso cuando una semilla comienza a crecer</span>
</div>
<div class="vocabulary-pair" data-index="4">
<span class="term">Riego</span>
<span class="definition">El acto de dar agua a las plantas</span>
</div>
<div class="vocabulary-pair" data-index="5">
<span class="term">Abono</span>
<span class="definition">Nutrientes que agregamos a la tierra para ayudar a las plantas a crecer</span>
</div>
</div>
<div class="game-area">
<div class="columns-container">
<div class="column terms-column">
<h2>Palabras</h2>
<div class="cards-container" id="terms-container">
<!-- Terms will be added here by JavaScript -->
</div>
</div>
<div class="column definitions-column">
<h2>Definiciones</h2>
<div class="cards-container" id="definitions-container">
<!-- Definitions will be added here by JavaScript -->
</div>
</div>
</div>
</div>
<div class="feedback-area">
<div id="celebration" class="celebration hidden">
<p>¡Excelente trabajo! 🎉</p>
</div>
</div>
</div>
</div>
<script src="script.js" defer></script>
#conectapalabras {
--main-color: #2f4060;
--accent-color: #ff6768;
--background-color: #fbfbfb;
--card-background: #ffffff;
--shadow-color: rgba(47, 64, 96, 0.1);
--selected-color: #e3f2fd;
--selected-border: #2196f3;
--wrong-color: #ffebee;
--wrong-border: #f44336;
font-family: 'Nunito', sans-serif;
background-color: var(--background-color);
color: var(--main-color);
line-height: 1.6;
}
#conectapalabras * {
margin: 0;
padding: 0;
box-sizing: border-box;
}
#conectapalabras .game-container {
max-width: 1200px;
margin: 2rem auto;
padding: 0 1rem;
}
#conectapalabras header {
text-align: center;
margin-bottom: 2rem;
}
#conectapalabras h1 {
color: var(–main-color);
font-size: 2.5rem;
margin-bottom: 0.5rem;
}
#conectapalabras .subtitle {
color: var(–accent-color);
font-size: 1.2rem;
}
#conectapalabras .columns-container {
display: flex;
gap: 2rem;
margin-bottom: 2rem;
}
#conectapalabras .column {
flex: 1;
background-color: var(–card-background);
border-radius: 12px;
padding: 1.5rem;
box-shadow: 0 4px 6px var(–shadow-color);
}
#conectapalabras .column h2 {
color: var(–main-color);
margin-bottom: 1rem;
text-align: center;
}
#conectapalabras .cards-container {
display: flex;
flex-direction: column;
gap: 1rem;
}
#conectapalabras .card {
background-color: var(–card-background);
border: 2px solid var(–main-color);
border-radius: 8px;
padding: 1rem;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s, background-color 0.2s, border-color 0.2s;
user-select: none;
}
#conectapalabras .card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px var(–shadow-color);
}
#conectapalabras .card.selected {
background-color: var(–selected-color);
border-color: var(–selected-border);
transform: scale(1.05);
box-shadow: 0 6px 12px var(–shadow-color);
}
#conectapalabras .card.matched {
background-color: #e8f5e9;
border-color: #4caf50;
cursor: default;
}
#conectapalabras .card.wrong {
background-color: var(–wrong-color);
border-color: var(–wrong-border);
animation: shake 0.5s;
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-5px); }
75% { transform: translateX(5px); }
}
#conectapalabras .feedback-area {
text-align: center;
margin-top: 2rem;
}
#conectapalabras .celebration {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: var(–accent-color);
color: white;
padding: 2rem;
border-radius: 12px;
font-size: 1.5rem;
animation: celebrate 0.5s ease-out;
}
#conectapalabras .hidden {
display: none;
}
@keyframes celebrate {
0% {
transform: translate(-50%, -50%) scale(0.8);
opacity: 0;
}
50% {
transform: translate(-50%, -50%) scale(1.1);
}
100% {
transform: translate(-50%, -50%) scale(1);
opacity: 1;
}
}
@media (max-width: 768px) {
#conectapalabras .columns-container {
flex-direction: column;
}
#conectapalabras .game-container {
margin: 1rem auto;
}
#conectapalabras h1 {
font-size: 2rem;
}
}
/* Vocabulary data styles /
/ Removed display: none for #conectapalabras #vocabulary-data and .vocabulary-pair so they are always visible */
/* Worksheet styles */
#conectapalabras .worksheet-header {
display: flex;
justify-content: space-between;
margin-bottom: 2rem;
padding: 1rem;
background-color: var(–card-background);
border-radius: 8px;
box-shadow: 0 2px 4px var(–shadow-color);
}
#conectapalabras .student-info {
display: flex;
gap: 2rem;
}
#conectapalabras .name-field,
#conectapalabras .date-field {
font-size: 1.2rem;
color: var(–main-color);
font-weight: 600;
}
#conectapalabras .download-btn {
background-color: var(–accent-color);
color: white;
border: none;
padding: 0.8rem 1.5rem;
border-radius: 8px;
font-size: 1.1rem;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
margin: 1rem 0;
}
#conectapalabras .download-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px var(–shadow-color);
}
/* Hide print-only elements in app view */
#conectapalabras .print-only {
display: none;
}
/* Print styles */
@media print {
#conectapalabras .print-only {
display: block;
}
#conectapalabras .download-btn {
display: none;
}
#conectapalabras .worksheet-header {
box-shadow: none;
border: 1px solid #ddd;
margin-bottom: 0.5rem;
padding: 0.25rem;
}
#conectapalabras .game-container {
margin: 0;
padding: 0;
}
#conectapalabras .columns-container {
gap: 0.5rem;
margin-bottom: 0.5rem;
}
#conectapalabras .column {
padding: 0.5rem;
}
#conectapalabras .column h2 {
margin-bottom: 0.25rem;
font-size: 1rem;
}
#conectapalabras .cards-container {
gap: 0.25rem;
}
#conectapalabras .card {
padding: 0.25rem;
font-size: 0.8rem;
min-height: 1.5rem;
display: flex;
align-items: center;
border-width: 1px;
}
#conectapalabras h1 {
font-size: 1.5rem;
margin-bottom: 0.25rem;
}
#conectapalabras .subtitle {
font-size: 0.9rem;
margin-bottom: 0.25rem;
}
/* Ensure the game area fits on one page */
#conectapalabras .game-area {
page-break-inside: avoid;
}
/* Make name and date fields smaller */
#conectapalabras .name-field,
#conectapalabras .date-field {
font-size: 0.9rem;
}
#conectapalabras .worksheet-pdf-page {
page-break-inside: avoid;
break-inside: avoid;
max-height: 10in;
overflow: hidden;
font-size: 0.7rem;
padding: 0 !important;
margin: 0 !important;
box-sizing: border-box;
width: 100%;
background: white;
}
#conectapalabras .worksheet-pdf-page * {
margin: 0 !important;
padding: 0 !important;
box-sizing: border-box;
line-height: 1.1 !important;
}
#conectapalabras .worksheet-pdf-page .columns-container {
display: flex !important;
flex-direction: row !important;
gap: 0.05rem;
}
#conectapalabras .worksheet-pdf-page .column {
padding: 0 !important;
margin-bottom: 0.02rem;
}
#conectapalabras .worksheet-pdf-page .column h2 {
font-size: 0.7rem !important;
margin-bottom: 0.1rem !important;
color: var(--main-color) !important;
text-align: center !important;
font-weight: bold !important;
display: block !important;
}
#conectapalabras .worksheet-pdf-page .card {
padding: 0.05rem 0.1rem !important;
font-size: 0.65rem !important;
min-height: 0.8rem;
border-width: 1px;
}
#conectapalabras .worksheet-pdf-page h1 {
font-size: 0.9rem;
margin-bottom: 0.02rem;
}
#conectapalabras .worksheet-pdf-page .subtitle {
font-size: 0.7rem;
margin-bottom: 0.02rem;
}
#conectapalabras .worksheet-pdf-page .worksheet-header {
margin-bottom: 0.02rem;
padding: 0.02rem;
}
#conectapalabras .answer-key-container {
page-break-before: always;
padding: 0.1in 0.05in !important;
margin: 0 !important;
box-sizing: border-box;
width: 100%;
background: white;
}
#conectapalabras .answer-key h2 {
font-size: 0.9rem;
margin-bottom: 0.3rem;
}
#conectapalabras .answer-pair {
padding: 0.05rem !important;
gap: 0.05rem;
}
#conectapalabras .answer-pair .term,
#conectapalabras .answer-pair .definition {
font-size: 0.65rem !important;
}
#conectapalabras .answer-pair .arrow {
font-size: 0.8rem !important;
}
#conectapalabras .force-page-break {
page-break-before: always;
break-before: page;
}
#conectapalabras .answer-key-container {
margin: 0 !important;
padding: 0 !important;
box-sizing: border-box;
width: 100%;
background: white;
max-height: 10in;
overflow: hidden;
}
}
/* Answer Key styles */
#conectapalabras .answer-key-container {
padding: 1rem;
background-color: var(–card-background);
min-height: 100vh;
width: 100%;
}
#conectapalabras .answer-key {
max-width: 800px;
margin: 0 auto;
}
#conectapalabras .answer-key h2 {
color: var(–main-color);
text-align: center;
margin-bottom: 1.5rem;
font-size: 1.5rem;
}
#conectapalabras .answers {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
#conectapalabras .answer-pair {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
background-color: var(–background-color);
border-radius: 4px;
border: 1px solid var(–shadow-color);
}
#conectapalabras .answer-pair .term {
font-weight: 600;
color: var(–main-color);
flex: 1;
font-size: 0.9rem;
}
#conectapalabras .answer-pair .arrow {
color: var(–accent-color);
font-size: 1.2rem;
}
#conectapalabras .answer-pair .definition {
flex: 2;
color: var(–main-color);
font-size: 0.9rem;
}
@media print {
#conectapalabras .answer-key-container {
padding: 0.5rem;
}
#conectapalabras .answer-key h2 {
font-size: 1.2rem;
margin-bottom: 1rem;
}
#conectapalabras .answer-pair {
padding: 0.25rem;
gap: 0.25rem;
}
#conectapalabras .answer-pair .term,
#conectapalabras .answer-pair .definition {
font-size: 0.8rem;
}
#conectapalabras .answer-pair .arrow {
font-size: 1rem;
}
}
#conectapalabras #vocabulary-data {
position: absolute !important;
left: -9999px !important;
width: 1px !important;
height: 1px !important;
overflow: hidden !important;
}
The js running on pageload is:
// Game state
let matchedPairs = 0;
let selectedCard = null;
// DOM Elements
let termsContainer;
let definitionsContainer;
let celebration;
let vocabularyData;
let gameTitle;
// Get vocabulary pairs from HTML
function getVocabularyPairs() {
try {
console.log(‘Getting vocabulary pairs…’);
console.log(‘vocabularyData element:’, vocabularyData);
// Get the title
const titleElement = vocabularyData.querySelector('.vocabulary-title');
if (titleElement) {
gameTitle.textContent = titleElement.textContent;
document.title = `${titleElement.textContent} - Juego de Vocabulario`;
}
const pairs = [];
const pairElements = vocabularyData.querySelectorAll('.vocabulary-pair');
console.log('Found pair elements:', pairElements.length);
if (pairElements.length === 0) {
console.error('No vocabulary pairs found in the HTML');
return [];
}
pairElements.forEach((pair, index) => {
const term = pair.querySelector('.term');
const definition = pair.querySelector('.definition');
console.log(`Processing pair ${index}:`, { term, definition });
if (!term || !definition) {
console.error('Missing term or definition in a vocabulary pair');
return;
}
pairs.push({
term: term.textContent,
definition: definition.textContent
});
});
console.log('Final vocabulary pairs:', pairs);
return pairs;
} catch (error) {
console.error('Error getting vocabulary pairs:', error);
return [];
}
}
// Shuffle array using Fisher-Yates algorithm
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i–) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
// Initialize the game
function initGame() {
console.log(‘Initializing game…’);
const vocabularyPairs = getVocabularyPairs();
if (vocabularyPairs.length === 0) {
console.error('No vocabulary pairs to initialize game with');
return;
}
// Create arrays of terms and definitions
const terms = vocabularyPairs.map(pair => pair.term);
const definitions = vocabularyPairs.map(pair => pair.definition);
console.log('Terms:', terms);
console.log('Definitions:', definitions);
// Shuffle terms and definitions independently
const shuffledTerms = shuffleArray([...terms]);
const shuffledDefinitions = shuffleArray([...definitions]);
console.log('Creating term cards...');
// Create term cards
shuffledTerms.forEach((term, index) => {
const termCard = createCard(term, 'term', terms.indexOf(term));
termsContainer.appendChild(termCard);
});
console.log('Creating definition cards...');
// Create definition cards
shuffledDefinitions.forEach((definition, index) => {
const definitionCard = createCard(definition, 'definition', definitions.indexOf(definition));
definitionsContainer.appendChild(definitionCard);
});
console.log('Game initialization complete');
}
// Create a card
function createCard(text, type, index) {
const card = document.createElement(‘div’);
card.className = ‘card’;
card.textContent = text;
card.dataset.index = index;
card.dataset.type = type;
// Add click event
card.addEventListener('click', handleCardClick);
return card;
}
// Handle card click
function handleCardClick() {
// If card is already matched, do nothing
if (this.classList.contains(‘matched’)) return;
// If no card is selected, select this one
if (!selectedCard) {
selectedCard = this;
this.classList.add('selected');
return;
}
// If clicking the same card, deselect it
if (selectedCard === this) {
selectedCard.classList.remove('selected');
selectedCard = null;
return;
}
// If clicking a card of the same type, change selection
if (selectedCard.dataset.type === this.dataset.type) {
selectedCard.classList.remove('selected');
selectedCard = this;
this.classList.add('selected');
return;
}
// Check if it's a match
if (selectedCard.dataset.index === this.dataset.index) {
// Correct match
selectedCard.classList.remove('selected');
selectedCard.classList.add('matched');
this.classList.add('matched');
matchedPairs++;
// Show celebration
showCelebration();
// Check if game is complete
if (matchedPairs === getVocabularyPairs().length) {
setTimeout(() => {
alert('¡Felicitaciones! Has completado el juego. 🎉');
}, 1000);
}
} else {
// Wrong match
selectedCard.classList.remove('selected');
this.classList.add('wrong');
selectedCard.classList.add('wrong');
// Remove wrong class after a short delay
setTimeout(() => {
this.classList.remove('wrong');
selectedCard.classList.remove('wrong');
}, 1000);
}
selectedCard = null;
}
// Celebration animation
function showCelebration() {
celebration.classList.remove(‘hidden’);
setTimeout(() => {
celebration.classList.add(‘hidden’);
}, 1500);
}
// Initialize DOM elements and start the game
document.addEventListener(‘DOMContentLoaded’, function() {
console.log(‘DOM Content Loaded’);
try {
// Initialize DOM elements
const root = document.getElementById(‘conectapalabras’);
termsContainer = root.querySelector(‘#terms-container’);
definitionsContainer = root.querySelector(‘#definitions-container’);
celebration = root.querySelector(‘#celebration’);
vocabularyData = root.querySelector(‘#vocabulary-data’);
gameTitle = root.querySelector(‘#game-title’);
console.log('DOM Elements:', {
termsContainer,
definitionsContainer,
celebration,
vocabularyData,
gameTitle
});
if (!termsContainer || !definitionsContainer || !celebration || !vocabularyData || !gameTitle) {
console.error('One or more required DOM elements not found');
return;
}
console.log('DOM elements initialized successfully');
// Start the game
initGame();
console.log('Game initialized successfully');
// Add PDF download event listener
const downloadBtn = root.querySelector('#downloadBtn');
if (downloadBtn) {
downloadBtn.addEventListener('click', function() {
// Create a root container for PDF with the same ID for styling
const pdfRoot = document.createElement('div');
pdfRoot.id = 'conectapalabras';
// Create a container for PDF
const pdfContainer = document.createElement('div');
pdfContainer.style.width = '100%';
// Create the worksheet page
const worksheetPage = document.createElement('div');
worksheetPage.className = 'worksheet-pdf-page';
worksheetPage.style.maxHeight = '10in';
worksheetPage.style.overflow = 'hidden';
worksheetPage.style.margin = '0';
worksheetPage.style.padding = '0';
// Clone the game container for the worksheet
const gameContainer = root.querySelector('.game-container').cloneNode(true);
// Remove the download button from the PDF version
const downloadBtn = gameContainer.querySelector('.download-btn');
if (downloadBtn) downloadBtn.remove();
// Make sure the worksheet header is visible
const worksheetHeader = gameContainer.querySelector('.worksheet-header');
if (worksheetHeader) worksheetHeader.classList.remove('print-only');
// Only keep the header and game area for the worksheet page
const header = gameContainer.querySelector('header');
const gameArea = gameContainer.querySelector('.game-area');
worksheetPage.appendChild(header.cloneNode(true));
worksheetPage.appendChild(gameArea.cloneNode(true));
pdfContainer.appendChild(worksheetPage);
// Add a page break
const pageBreak = document.createElement('div');
pageBreak.className = 'force-page-break';
pdfContainer.appendChild(pageBreak);
// Create the answer key page
const answerKeyContainer = document.createElement('div');
answerKeyContainer.className = 'answer-key-container';
answerKeyContainer.style.minHeight = '10in'; // Set minimum height to full page
answerKeyContainer.style.display = 'flex';
answerKeyContainer.style.flexDirection = 'column';
answerKeyContainer.style.justifyContent = 'flex-end'; // Push content to bottom
answerKeyContainer.innerHTML = `
<div class="answer-key">
<h2>Clave de Respuestas</h2>
<div class="answers">
${Array.from(root.querySelectorAll('.vocabulary-pair')).map(pair => `
<div class="answer-pair">
<span class="term">${pair.querySelector('.term').textContent}</span>
<span class="arrow">→</span>
<span class="definition">${pair.querySelector('.definition').textContent}</span>
</div>
`).join('')}
</div>
</div>
`;
pdfContainer.appendChild(answerKeyContainer);
// Append everything to the PDF root container
pdfRoot.appendChild(pdfContainer);
// PDF options
const opt = {
margin: 0.2,
filename: 'biohuerto-worksheet.pdf',
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 2, useCORS: true, letterRendering: true },
jsPDF: { unit: 'in', format: 'letter', orientation: 'portrait', compress: true }
};
// Generate PDF from the root container with the correct ID for styling
html2pdf().set(opt).from(pdfRoot).save();
});
}
} catch (error) {
console.error('Error initializing game:', error);
}
});