Chess Board Tree Explorer HTML CSS JavaScript

Get 1000000 0FP0EXP Token to remove widget entirely!



source code
old source code

get any 0FP0EXP Token to automatically turn off or 100000 0FP0EXP Token to remove this JavaScript Mining.

Get 500000 0FP0EXP Token to remove my NFTS advertisements!

Get 400000 0FP0EXP Token to remove this donation notification!

get 300000 0FP0EXP Token to remove this paypal donation.

View My Stats

Need referral links?

get 200000 0FP0EXP Token to remove my personal ADS.

word number: 2984

Time: 2026-01-18 21:19:23 +0800

♟️ Opening Tree Explorer

Reference

Source Code

<!DOCTYPE html>
<html>
<head>
  <title>Chess Tree Study</title>
  <link rel="stylesheet" href="chessboard-1.0.0.min.css">
<style>
    /* Global Reset */
    * { box-sizing: border-box; } /* Important for sizing */
    body { 
        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
        display: flex; 
        flex-direction: column; 
        align-items: center; 
        background: #f4f4f4; 
        padding: 10px; /* Reduced padding for mobile */
        margin: 0;
    }
    h1 { margin: 10px 0 20px 0; font-size: 1.5em; text-align: center; }
    /* Flex Container: Default is Row (Desktop) */
    #main-container { 
        display: flex; 
        flex-direction: row; 
        gap: 20px; 
        width: 100%;
        max-width: 900px; /* Prevent it from getting too wide on huge screens */
        justify-content: center;
        align-items: flex-start;
    }
    /* The Board Container */
    #myBoard { 
        width: 400px; 
        flex-shrink: 0; /* Prevent board from squishing */
    }
    /* The Sidebar */
    #sidebar { 
        width: 300px; 
        display: flex; 
        flex-direction: column; 
        gap: 10px; 
        flex-grow: 1; /* Allow sidebar to fill space */
    }
    /* --- MOBILE OPTIMIZATION --- */
    @media (max-width: 768px) {
        #main-container {
            flex-direction: column; /* Stack vertically */
            align-items: center;
        }
        #myBoard {
            width: 100%;      /* Take full width of phone */
            max-width: 400px; /* But don't get massive on tablets */
        }
        #sidebar {
            width: 100%;      /* Full width controls */
            max-width: 400px;
        }
    }
    /* Move Buttons - Touch Friendly */
    .move-btn {
        padding: 15px; /* Larger tap target for fingers */
        background: white; 
        border: 1px solid #ccc; 
        cursor: pointer; 
        border-radius: 6px; 
        box-shadow: 0 1px 3px rgba(0,0,0,0.1);
    }
    .move-btn:active { background: #eee; transform: translateY(1px); }
    /* Notation & Comments */
    #history { 
        font-family: monospace; 
        font-size: 1.1em; 
        background: #fff; 
        border: 1px solid #ccc; 
        padding: 10px;
        border-radius: 4px;
    }
    #comment-box { 
        background: #e8f5e9; 
        padding: 12px; 
        border-left: 5px solid #4caf50; 
        font-style: italic; 
        border-radius: 0 4px 4px 0;
    }
    /* Controls */
    #controls { display: flex; gap: 10px; }
    button { 
        flex: 1; /* Buttons share equal width */
        padding: 12px; 
        font-size: 1em;
        border: none;
        background: #333;
        color: white;
        border-radius: 4px;
    }
</style>
</head>
<body>

  <h1>♟️ Opening Tree Explorer</h1>

  <div id="main-container">
    <div id="myBoard"></div>
    <div id="sidebar">
      <div id="history">Start</div>
      <div id="comment-box">Select a move to begin study.</div>
      <h4>Available Moves:</h4>
      <div id="move-options">Loading...</div>
      <div id="controls">
        <button id="resetBtn">↩️ Reset to Start</button>
        <button id="undoBtn">⬅️ Undo Last</button>
      </div>
    </div>
  </div>

  <script src="jquery-3.7.1.min.js"></script>
  <script src="chess-0.12.1.min.js"></script>
  <script src="chessboard-1.0.0.min.js"></script>

  <script>
    // --- 1. SETUP ---
    var board = null;
    var game = new Chess();
    var rootData = [];        // The full JSON loaded from file
    var currentNode = null;   // The specific object in the JSON tree we are currently on
    var nodeStack = [];       // Keeps track of path to allow "Undo"

    var config = {
        position: 'start',
        draggable: false, 
        // Use local images if you have them, otherwise use the raw GitHub link
        pieceTheme: 'img/chesspieces/wikipedia/{piece}.png' 
    };
    board = Chessboard('myBoard', config);

    // --- 2. LOGIC ---

    async function init() {
        try {
            const response = await fetch('chessopenings-tree.json');
            rootData = await response.json();
            
            // "rootData" is an array of moves (e4, d4). 
            // We create a fake "root" node to hold them as children.
            currentNode = { children: rootData };
            
            renderMoveOptions();
        } catch (e) {
            document.getElementById('move-options').innerHTML = "Error loading openings.json";
            console.error(e);
            rootData = local_tree_openings;
            currentNode = { children: rootData };
            renderMoveOptions();
        }
    }

    function onMoveClick(childNode) {
        // 1. Make the move in the engine
        game.move(childNode.move);
        board.position(game.fen());

        // 2. Speak the move (Voice Notation)
        speak(childNode.move);

        // 3. Update State
        nodeStack.push(currentNode); // Save previous parent
        currentNode = childNode;     // Set new current node

        // 4. Update UI
        updateUI();
    }

    function undo() {
        if (nodeStack.length === 0) return; // Can't undo past start

        game.undo();
        board.position(game.fen());
        currentNode = nodeStack.pop(); // Go back to parent node
        updateUI();
    }

    function reset() {
        game.reset();
        board.start();
        currentNode = { children: rootData };
        nodeStack = [];
        updateUI();
    }

    // --- 3. UI RENDERING ---

    function updateUI() {
        // Update History Text
        const history = game.pgn();
        document.getElementById('history').innerText = history || "Start Position";

        // Update Comment
        const comment = currentNode.comment ? currentNode.comment : "No comment for this position.";
        document.getElementById('comment-box').innerText = comment;

        // Render Next Options
        renderMoveOptions();
    }

    function renderMoveOptions() {
        const container = document.getElementById('move-options');
        container.innerHTML = "";

        if (!currentNode.children || currentNode.children.length === 0) {
            container.innerHTML = "<em>End of line. Go back to study other variations.</em>";
            return;
        }

        // Loop through available children (next moves) and create buttons
        currentNode.children.forEach(child => {
            const btn = document.createElement('div');
            btn.className = 'move-btn';
            
            // Show Move (e.g., "e5") and Name (e.g., "Open Game")
            let html = `<strong>${child.move}</strong>`;
            if (child.name) {
                html += ` <span class="move-name">${child.name}</span>`;
            }
            btn.innerHTML = html;

            btn.onclick = () => onMoveClick(child);
            container.appendChild(btn);
        });
    }

    function speak(text) {
        if ('speechSynthesis' in window) {
            let ut = new SpeechSynthesisUtterance(text);
            ut.rate = 1.0;
            window.speechSynthesis.speak(ut);
        }
    }

    // --- 4. BUTTONS ---
    document.getElementById('undoBtn').onclick = undo;
    document.getElementById('resetBtn').onclick = reset;

    // Start
    init();

    local_tree_openings = [
        {
            "move": "e4",
            "name": "King's Pawn Opening",
            "comment": "Best move for beginners. Controls the center immediately.",
            "children": [
            {
                "move": "e5",
                "name": "Open Game",
                "children": [
                {
                    "move": "Nf3",
                    "name": "King's Knight Opening",
                    "children": [
                    {
                        "move": "Nc6",
                        "children": [
                        {
                            "move": "Bb5",
                            "name": "Ruy Lopez",
                            "comment": "Attacks the defender of the e5 pawn."
                        },
                        {
                            "move": "Bc4",
                            "name": "Italian Game",
                            "children": [
                                {
                                    "move": "Bc5",
                                    "name": "Giuoco Piano"
                                }
                            ]
                        }
                        ]
                    }
                    ]
                }
                ]
            },
            {
                "move": "c5",
                "name": "Sicilian Defense",
                "comment": "A sharp, counter-attacking response.",
                "children": []
            }
            ]
        },
        {
            "move": "d4",
            "name": "Queen's Pawn Opening",
            "children": []
        }
    ]

    // --- RESPONISVE RESIZE FIX ---
    // When the browser window resizes, tell the board to resize
    window.addEventListener('resize', function() {
        board.resize();
    });

    // Also call it once quickly after load to catch any initial CSS layout shifts
    setTimeout(board.resize, 200);
  </script>
</body>
</html>