Search code examples
javascriptjqueryhtmlcssblockly

Check if DOM elements are present inside a DIV then run functions assigned to those elements in order


i'm trying to develop a game using html, css and js. At the moment I'm focusing on manipulating DOM elements without using the canvas tag. My idea is to create a pseudo graphical programming language, similar to the Blockly environment. So far I have inserted 3 clickable elements inside #toolbox that create their copies in #workspace.

Now, I am trying to assign functions to the elements present in #workspace, which once pressed the Run button are executed in order of appearance, so as to create a queue of commands that is able to move the pink square inside #output_section.

Therefore I cannot understand how to write the function that is able to verify the presence of the elements and then be able to perform the different functions assigned to these elements.

Any ideas? :D

I'm using Jquery 3.3.1

function addRed() {
    var redWorkspace = document.createElement("DIV");
    redWorkspace.className = "remove-block block red";
    document.getElementById("workspace").appendChild(redWorkspace);
};

function addBlue() {
    var blueWorkspace = document.createElement("DIV");
    blueWorkspace.className = "remove-block block blue";
    document.getElementById("workspace").appendChild(blueWorkspace);
};

function addGreen() {
    var greenWorkspace = document.createElement("DIV");
    greenWorkspace.className = "remove-block block green";
    document.getElementById("workspace").appendChild(greenWorkspace);
};

$("#clear_workspace").click(function () {
    $("#workspace").empty();
});

$(document).on("click", ".remove-block", function () {
    $(this).closest("div").remove();
});
html,
body {
    margin: 0;
    padding: 0;
}

#workspace {
    display: flex;
    height: 100px;
    padding: 10px;
    background: black;
}

#toolbox {
    display: flex;
    padding: 10px;
    width: 300px;
}

#output_section {
    height: 500px;
    width: 500px;
    border: solid black;
    margin: 10px;
    position: relative;
}

#moving_square {
    position: absolute;
    bottom: 0;
    right: 0;
    width: 100px;
    height: 100px;
    background: pink;
}

.block {
    height: 100px;
    width: 100px;
}


.red {
    background: red;
}

.blue {
    background: cyan;
}

.green {
    background: green;
}

.grey {
    background: #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<html>
<body>
    <div id="workspace"></div>
    <div id="workspace-menu">
       <button id="run_workspace">Run</button>
        <button id="clear_workspace">Clear</button>
    </div>
    <div id="toolbox" class="grey">
    <div onclick="addRed()" class="block red">Left</div>
    <div onclick="addBlue()" class="block blue">Up</div>
    <div onclick="addGreen()" class="block green">Right</div>
    </div>
    <div id="output_section">
        <div id="moving_square"></div>
    </div>
</body>
</html>


Solution

  • Commonly, it's a good idea to store all required information in arrays and objects, and use HTML only to display your data.

    Also, if you are already using jQuery - use it for all 100%)
    Made some improvements:

    let mobs = {
      pinky: {
        node: $('#moving_square'),
        coors: { top: 400, left: 400 },
        step: 30,
        moveQueue: [],
        // moveTimeout ???
      },
    }; // storing here all created objects, that must move.
    /* Each [moveQueue] array will store the chain of moves, like ["up", "up", "left"]
    
    You can take each "key-word" of move, and get required function buy that key,
    from the 'move' object */
    
    let move = { // Think about how to simlify this object and functions. It's possible!)
      left: function (obj) {
        let left = obj.coors.left = (obj.coors.left - obj.step);
        obj.node.css('left', left + 'px');
      },
      up: function (obj) {
        let top = obj.coors.top = (obj.coors.top - obj.step);
        obj.node.css('top', top + 'px');
      },
      right: function (obj) {
        let left = obj.coors.left = (obj.coors.left + obj.step);
        obj.node.css('left', left + 'px');
      }
    };
    
    let stepTimeout = 1000;
    let running = false;
    let timeouts = {}; // store all running timeouts here, 
    // and clear everything with for( key in obj ) loop, if required
    
    $('#toolbox .block').on('click', function () {
      let color = $(this).attr('data-color');
      let workBlock = '<div class="remove-block block ' + color + '"></div>';
      $('#workspace').append(workBlock);
    
      mobs.pinky.moveQueue.push( $(this).text().toLowerCase() ); // .attr('data-direction');
      // instead of pinky - any other currently selected object
      
      // $(this).text().toLowerCase() — must be "left", "up", "right"
    });
    
    $('#run_workspace').on('click', function () {
      running = true;
      runCode();
    
      function runCode() {
        for (let obj in mobs) { // mobile objects may be multiple
          // Inside the loop, obj == mobs each key name. Here it's == "pinky"
          
          let i = 0;
          let pinky = mobs[obj];
    
          localRun();
          
          function localRun() {
            let direction = pinky.moveQueue[i]; // getting direction key by array index.
            
            move[direction](pinky); // calling the required function from storage.
    
            if (pinky.moveQueue[++i] && running ) {
              // self-calling again, if moveQueue has next element.
              // At the same time increasing i by +1 ( ++i )
              timeouts[obj] = setTimeout(localRun, stepTimeout);
            }
          }
        }
      }
    
    });
    
    
    $("#clear_workspace").click(function () {
      $("#workspace").empty();
    });
    
    $('#workspace').on("click", ".remove-block", function () {
      $(this).closest("div").remove();
    });
    html,
    body {
      margin: 0;
      padding: 0;
    }
    
    #workspace {
      display: flex;
      height: 100px;
      padding: 10px;
      background: black;
    }
    
    #toolbox {
      display: flex;
      padding: 10px;
      width: 300px;
    }
    
    #output_section {
      height: 500px;
      width: 500px;
      border: solid black;
      margin: 10px;
      position: relative;
    }
    
    #moving_square {
      position: absolute;
      top: 400px;
      left: 400px;
      width: 100px;
      height: 100px;
      background: pink;
    }
    
    .block {
      height: 100px;
      width: 100px;
    }
    
    .red {
      background: red;
    }
    
    .blue {
      background: cyan;
    }
    
    .green {
      background: green;
    }
    
    .grey {
      background: #ccc;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    
    <div id="workspace"></div>
    <div id="workspace-menu">
      <button id="run_workspace">Run</button>
      <button id="clear_workspace">Clear</button>
    </div>
    <div id="toolbox" class="grey">
      <div data-color="red" class="block red">Left</div>
      <div data-color="blue" class="block blue">Up</div>
      <div data-color="green" class="block green">Right</div>
    </div>
    <div id="output_section">
      <div id="moving_square"></div>
    </div>

    But... jQuery was used only for clicks... Translation to JS:

    let mobs = {
      pinky: {
        node: document.getElementById('moving_square'),
        coors: { top: 400, left: 400 },
        step: 30,
        moveQueue: [],
      },
    };
    
    let move = {
      left: function (obj) {
        let left = obj.coors.left = (obj.coors.left - obj.step);
        obj.node.style.left = left + 'px';
      },
      up: function (obj) {
        let top = obj.coors.top = (obj.coors.top - obj.step);
        obj.node.style.top = top + 'px';
      },
      right: function (obj) {
        let left = obj.coors.left = (obj.coors.left + obj.step);
        obj.node.style.left = left + 'px';
      }
    };
    
    let stepTimeout = 1000;
    let running = false;
    let timeouts = {};
    
    let blocks = document.querySelectorAll('#toolbox .block');
    let workSpace = document.getElementById('workspace');
    
    blocks.forEach(function(block){
      block.addEventListener('click', function(){
        let color = this.dataset.color;
        let workBlock = '<div class="remove-block block ' + color + '"></div>';
        
        workSpace.insertAdjacentHTML('beforeend', workBlock);
        
        mobs.pinky.moveQueue.push( this.textContent.toLowerCase() );
      });
    });
    
    document.getElementById('run_workspace').addEventListener('click', function () {
      running = true;
      runCode();
    
      function runCode() {
        for (let obj in mobs) { // mobile objects may be multiple
          // Inside the loop, obj == mobs each key name. Here it's == "pinky"
          
          let i = 0;
          let pinky = mobs[obj];
    
          localRun();
          
          function localRun() {
            let direction = pinky.moveQueue[i]; // getting direction key by array index.
            
            move[direction](pinky); // calling the required function from storage.
    
            if (pinky.moveQueue[++i] && running ) {
              // self-calling again, if moveQueue has next element.
              // At the same time increasing i by +1 ( ++i )
              timeouts[obj] = setTimeout(localRun, stepTimeout);
            }
          }
        }
      }
    
    });
    
    
    document.getElementById("clear_workspace").addEventListener('click', function () {
      workSpace.textContent = "";
    });
    
    workSpace.addEventListener('click', function (e) {
      if( e.target.classList.contains('remove-block') ){
        e.target.remove();
      }
    });
    html,
    body {
      margin: 0;
      padding: 0;
    }
    
    #workspace {
      display: flex;
      height: 100px;
      padding: 10px;
      background: black;
    }
    
    #toolbox {
      display: flex;
      padding: 10px;
      width: 300px;
    }
    
    #output_section {
      height: 500px;
      width: 500px;
      border: solid black;
      margin: 10px;
      position: relative;
    }
    
    #moving_square {
      position: absolute;
      top: 400px;
      left: 400px;
      width: 100px;
      height: 100px;
      background: pink;
    }
    
    .block {
      height: 100px;
      width: 100px;
    }
    
    .red {
      background: red;
    }
    
    .blue {
      background: cyan;
    }
    
    .green {
      background: green;
    }
    
    .grey {
      background: #ccc;
    }
    <div id="workspace"></div>
    <div id="workspace-menu">
      <button id="run_workspace">Run</button>
      <button id="clear_workspace">Clear</button>
    </div>
    <div id="toolbox" class="grey">
      <div data-color="red" class="block red">Left</div>
      <div data-color="blue" class="block blue">Up</div>
      <div data-color="green" class="block green">Right</div>
    </div>
    <div id="output_section">
      <div id="moving_square"></div>
    </div>