Bubble Javascript problem

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
Nombre: _________________
Fecha: _________________

¡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);
}

});

Why are you using Bubble? All of that can be done with Bubble native features…

1 Like

I did it much faster using Cursor. My whole app is on bubble so i just want to integrate it there for my users.

Ive integrated other JS codes similar for different functionalities on my bubble platform but in this particular case its not working and i dont understand why.

Thanks
Thanks

HTML file? You mean you put your code block into the HTML element and it’s not rendering as expected?

Hello. Yes! i put the html and the css code on an html element and normally it renders. then i put all the js code to run when the page is loaded.

when page is loaded i run the js code.

Most likely the code runs after the HTML is rendered and it’s missing the necessary scripts to render correctly.

Just to test: run the code first and then render the HTML. Try making the HTML element visible (make sure it’s not visible on page load) 1 second after the code executes.

Ask cursor :slight_smile:

Hi! I tried it but it didnt worked. Any more ideas? Maybe im doing it wrong

This topic was automatically closed after 70 days. New replies are no longer allowed.