How to properly use javascript within oTree / django?

I have a problem regarding implementing my non-oTree HTML, CSS, JS files into oTree format. I have this puzzle game (see screenshot 1) enter image description here. Implemented in oTree it is buggy – as you can see in screenshot 2 enter image description here. The JS logic kinda works as the counter recognises clicks on the here empty and buggy structured picture pieces. The JS code lies within the PuzzelPage.html file like this

{{ block scripts }}
<script> var turns = 0; // Zähler für die Anzahl der Züge
var puzzleSolved = false; // Zustand des Puzzles
var timer; // Globale Variable für den Timer
var currTile, otherTile; // Variablen für die aktuellen Puzzle-Teile

// Diese Funktion mischt ein Array in einer zufälligen Reihenfolge
function shuffleArray(array) {
    for (let i = array.length - 1; i > 0; i--) {
        let j = Math.floor(Math.random() * (i + 1));
        [array[i], array[j]] = [array[j], array[i]];
    }
}

// Diese Funktion wird aufgerufen, sobald das Fenster geladen ist
window.onload = function() {
    let imgOrder = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"];
    shuffleArray(imgOrder);

    // Erstelle die Puzzle-Teile und füge sie zum Board hinzu
    for (let r = 0; r < 3; r++) {
        for (let c = 0; c < 4; c++) {
            let tile = document.createElement("img");
            tile.id = r.toString() + "-" + c.toString();
            tile.src = imgOrder.shift() + ".jpg";
            tile.addEventListener("click", tileClick);
            document.getElementById("board").append(tile);
        }
    }

    // Starte den Timer
    startTimer();
};

function startTimer() {
    var timeLeft = 120; // 2 Minuten
    var timerElement = document.getElementById("timer");
    timer = setInterval(function() {
        var minutes = Math.floor(timeLeft / 60);
        var seconds = timeLeft % 60;
        timerElement.textContent = (minutes < 10 ? "0" : "") + minutes + ":" + (seconds < 10 ? "0" : "") + seconds;

        if (timeLeft <= 30) {
            timerElement.classList.add('time-warning');
        }

        if (timeLeft <= 0) {
            clearInterval(timer);
            timerElement.textContent = "00:00";
            alert("Die Zeit ist abgelaufen!");
            puzzleSolved = true;
            timerElement.classList.remove('time-warning');
        }

        timeLeft--;
    }, 1000);
}

function tileClick() {
    if (puzzleSolved) {
        return; // Keine Aktion, wenn das Puzzle bereits gelöst wurde
    }

    if (turns === 0) {
        currTile = this;
        turns++;
    } else {
        otherTile = this;
        let currImg = currTile.src;
        let otherImg = otherTile.src;
        currTile.src = otherImg;
        otherTile.src = currImg;
        turns = 0;

        let counter = parseInt(document.getElementById("counter").innerText);
        document.getElementById("counter").innerText = counter + 1;

        checkIfSolved();
    }
}

function checkIfSolved() {
    let tiles = document.getElementById("board").getElementsByTagName("img");
    for (let i = 0; i < tiles.length; i++) {
        let url = tiles[i].src;
        let match = url.match(/(d+).jpg$/);
        if (match && match[1]) {
            let number = parseInt(match[1], 10);
            if (number - 1 !== i) {
                return; // Nicht gelöst.
            }
        } else {
            return; // Nicht gelöst.
        }
    }
    
    puzzleSolved = true;
    clearInterval(timer);
    var timerElement = document.getElementById("timer");
    timerElement.textContent = "00:00";
    timerElement.classList.remove('time-warning');
    alert("Puzzle gelöst!"); // Benachrichtigung anzeigen
}<script> {{ endblock}}

also the picture file lie in the same file as the .html file.

If you maybe know why this is happening please let me now. If you need the code just let me know.

Kind regards 🙂