I am trying to write a simple web-page embedded program in javascript. Right now, I am attempting to write a linked list of all active buttons on the screen at any given time. However, when I attempt to iterate through a linked list that isn't empty, the program freezes.
This is the relevant block of code:
document.addEventListener("click", (event) => {
if (seekButtons) {
if (!(activeButtons.isEmpty())) {
var runner = activeButtons.head;
/*
*
* This while loop seems to run indefinitely if the activeButtons list has anything in it.
*
*/
while (runner) {
//this list should be populated only by buttons
console.log("a button! \n");
if (runner.element.isInside(mouseX, mouseY)) {
document.getElementById("fiddleText").innerHTML = ('clicked');
console.log("We are in the button! \n");
//This line doesn't seem to work correctly. It's meant to move on to the next item in the list (and to exit the while loop if it's the last item), but the loop runs forever.
runner = runner.next;
}
}
}
}
});
I think I need another set of eyes to look over this and help me figure out why this isn't working.
The linked list code (not written by me) looks like this:
//this code shamelessly stolen from https://www.geeksforgeeks.org/implementation-linkedlist-javascript/
// User defined class node
class Node {
// constructor
constructor(element) {
this.element = element;
this.next = null
}
}
// linkedlist class
class LinkedList {
constructor() {
this.head = null;
this.size = 0;
}
// adds an element at the end
// of list
add(element) {
// creates a new node
var node = new Node(element);
// to store current node
var current;
// if list is Empty add the
// element and make it head
if (this.head == null)
this.head = node;
else {
current = this.head;
// iterate to the end of the
// list
while (current.next) {
current = current.next;
}
// add node
current.next = node;
}
this.size ;
}
// insert element at the position index
// of the list
insertAt(element, index) {
if (index < 0 || index > this.size)
return console.log("Please enter a valid index.");
else {
// creates a new node
var node = new Node(element);
var curr, prev;
curr = this.head;
// add the element to the
// first index
if (index == 0) {
node.next = this.head;
this.head = node;
} else {
curr = this.head;
var it = 0;
// iterate over the list to find
// the position to insert
while (it < index) {
it ;
prev = curr;
curr = curr.next;
}
// adding an element
node.next = curr;
prev.next = node;
}
this.size ;
}
}
// removes an element from the
// specified location
removeFrom(index) {
if (index < 0 || index >= this.size)
return console.log("Please Enter a valid index");
else {
var curr, prev, it = 0;
curr = this.head;
prev = curr;
// deleting first element
if (index === 0) {
this.head = curr.next;
} else {
// iterate over the list to the
// position to removce an element
while (it < index) {
it ;
prev = curr;
curr = curr.next;
}
// remove the element
prev.next = curr.next;
}
this.size--;
// return the remove element
return curr.element;
}
}
// removes a given element from the
// list
removeElement(element) {
var current = this.head;
var prev = null;
// iterate over the list
while (current != null) {
// comparing element with current
// element if found then remove the
// and return true
if (current.element === element) {
if (prev == null) {
this.head = current.next;
} else {
prev.next = current.next;
}
this.size--;
return current.element;
}
prev = current;
current = current.next;
}
return -1;
}
// finds the index of element
indexOf(element) {
var count = 0;
var current = this.head;
// iterate over the list
while (current != null) {
// compare each element of the list
// with given element
if (current.element === element)
return count;
count ;
current = current.next;
}
// not found
return -1;
}
// Helper Methods
// checks the list for empty
isEmpty() {
return this.size == 0;
}
// gives the size of the list
size_of_list() {
console.log(this.size);
}
// prints the list items
printList() {
var curr = this.head;
var str = "";
while (curr) {
str = curr.element " ";
curr = curr.next;
}
console.log(str);
}
}
My main code looks like this:
//canvas elements
var canvas = document.getElementById("SnekGamCanvas");
var ctx = canvas.getContext("2d");
canvas.addEventListener('click', function () { }, false);
//some important variables
var px = canvas.width / 2;
var py = canvas.height / 2;
var snekColor = "#EC942D";
var clock = 0;
var mouseX = 0.5;
var mouseY = 0.5;
var activeButtons = new LinkedList();
var seekButtons = true;
//classes
class clickButton {
constructor(text, color, altColor, width, height, radius, xpos, ypos) {
this.text = text;
this.color = color;
this.altColor = altColor;
this.width = width;
this.height = height;
this.radius = radius;
this.xpos = xpos;
this.ypos = ypos;
}
isInside(datX, datY) {
//usually, datX will be mouseX, and datY will be mouseY.
if (datX > (this.xpos) && datX < (this.xpos this.width)) {
if ((datY > this.ypos) && datY < (this.ypos this.height)) {
return true;
}
}
return false;
}
drawButton() {
ctx.strokeStyle = "#000000"
if (this.isInside(mouseX, mouseY)) {
ctx.fillStyle = this.altColor;
roundRect(this.xpos, this.ypos, this.width, this.height, this.radius, true, true, this.altColor);
ctx.fillStyle = "#000000";
ctx.strokeStyle = "#000000";
ctx.font = '40px san-serif';
ctx.strokeText(this.text, this.xpos 10, this.ypos 40);
ctx.fillText(this.text, this.xpos 10, this.ypos 40);
}
else {
ctx.fillStyle = this.color;
roundRect(this.xpos, this.ypos, this.width, this.height, this.radius, true, true, this.color);
ctx.fillStyle = "#000000";
ctx.strokeStyle = "#000000";
ctx.font = '40px san-serif';
ctx.strokeText(this.text, this.xpos 10, this.ypos 40);
ctx.fillText(this.text, this.xpos 10, this.ypos 40);
}
//draw_Ball(303, 500, 50, snekColor);
}
clickOnButton() {
document.getElementById("fiddleText").innerHTML = ('clicked');
}
}
//buttons
var startButton = new clickButton("Start Game", "#74B5ED", "#1824C7", 200, 50, 20, ((canvas.width / 2) - 100), (canvas.height * (4 / 5)));
//images
var seel = new Image();
seel.onload = function () {
ctx.drawImage(seel, 0, 0, canvas.width, canvas.height);
}
seel.src = "https://assets.pokemon.com/assets/cms2/img/pokedex/full/086.png"
var snek_title = new Image();
snek_title.onload = function () {
ctx.drawImage(snek_title, 0, 0, canvas.width, canvas.height);
}
snek_title.src = "https://globin347.com/images/Snake Title.png"
//stuff about mouse moving
//the relative mouse position code came from this stackoverflow page: https://stackoverflow.com/questions/17130395/real-mouse-position-in-canvas
function getMousePosX(canvas, evt) {
var rect = canvas.getBoundingClientRect(), // abs. size of element
scaleX = canvas.width / rect.width; // relationship bitmap vs. element for X
return (evt.clientX - rect.left) * scaleX; // scale mouse coordinates after they have
}
function getMousePosY(canvas, evt) {
var rect = canvas.getBoundingClientRect(), // abs. size of element
scaleY = canvas.height / rect.height; // relationship bitmap vs. element for Y
return (evt.clientY - rect.top) * scaleY;
}
document.addEventListener('mousemove', (event) => {
//document.getElementById("fiddleText").innerHTML = (`Mouse X: ${event.clientX}, Mouse Y: ${event.clientY}`);
mouseX = getMousePosX(canvas, event);
mouseY = getMousePosY(canvas, event);
//document.getElementById("fiddleText").innerHTML = ('mouseX: ' mouseX ', mouseY: ' mouseY);
//now convert total position to canvas position
//mouseX, mouseY = getMousePos(canvas, event);
//document.getElementById("fiddleText").innerHTML = ('mouseX: ' mouseX ', mouseY: ' mouseY);
});
//mouse clicking
document.addEventListener("click", (event) => {
if (seekButtons) {
if (!(activeButtons.isEmpty())) {
var runner = activeButtons.head;
/*
*
* This while loop seems to run indefinately if the activeButtons list has anything in it.
*
*/
while (runner) {
//this list should be populated only by buttons
console.log("a button! \n");
if (runner.element.isInside(mouseX, mouseY)) {
document.getElementById("fiddleText").innerHTML = ('clicked');
console.log("We are in the button! \n");
//This line doesn't seem to work correctly. It's meant to move on to the next item in the list (and to exit the while loop if it's the last item), but the loop runs forever.
runner = runner.next;
}
}
}
}
});
//begin
var gameState = -1;
function draw() {
clock = 1;
ctx.clearRect(0, 0, canvas.width, canvas.height);
//document.getElementById("fiddleText").innerHTML = ("Clock: " clock);
if (gameState == -1) {
//startup
setup();
}
else if (gameState == 0) {
//this hasn't been implemented yet
startMenu();
}
else if (gameState == 1) {
//this hasn't been implemented yet either
playGame();
}
else if (gameState == 2) {
//ditto
gameOver();
}
else {
//something's wrong
ctx.drawImage(seel, 0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#b30000";
ctx.strokeStyle = "#000000";
ctx.font = '140px san-serif';
ctx.fillText('OH NO', 120, 120);
ctx.strokeText('OH NO', 120, 120);
ctx.fillText('IT BLOKE', 200, 630);
ctx.strokeText('IT BLOKE', 200, 630);
}
}
setInterval(draw, 10);
function setup() {
//this should be added to the buttons list before the program starts in earnest
activeButtons.add(startButton);
activeButtons.printList();
//document.getElementById("fiddleText").innerHTML = ('Oh Boy Baby');
//document.getElementById("fiddleText").innerHTML = ('large');
gameState = 0;
}
function startMenu() {
ctx.drawImage(snek_title, 0, 0, canvas.width, canvas.height);
startButton.drawButton();
//draw_Ball(mouseX, mouseY, 50, snekColor);
}
function playGame() {
draw_Ball(200, 700, 50, snekColor);
draw_Ball(400, 700, 50, snekColor);
draw_Ball(300, 500, 50, snekColor);
}
function gameOver() {
}
//this function was stolen from stack overflow
function showImage(width, height, image_source, alt_text) {
var img = document.createElement("img");
img.src = image_source;
img.width = width;
img.height = height;
img.alt = alt_text;
}
function draw_Ball(bx, by, size, ballColor) {
ctx.beginPath();
ctx.arc(bx, by, size, 0, (Math.PI * 2));
ctx.fillStyle = ballColor;
ctx.fill();
ctx.strokeStyle = "#000000";
ctx.stroke();
ctx.closePath();
}
//This next function was taken from stack overflow
function roundRect(x, y, width, height, radius, stroke, fill, color) {
ctx.beginPath();
ctx.moveTo(x radius, y);
ctx.lineTo(x width - radius, y);
ctx.quadraticCurveTo(x width, y, x width, y radius);
ctx.lineTo(x width, y height - radius);
ctx.quadraticCurveTo(x width, y height, x width - radius, y height);
ctx.lineTo(x radius, y height);
ctx.quadraticCurveTo(x, y height, x, y height - radius);
ctx.lineTo(x, y radius);
ctx.quadraticCurveTo(x, y, x radius, y);
if (stroke) {
ctx.stroke();
}
if (fill) {
ctx.fill();
}
ctx.closePath();
return;
}
And, just in case, here are my css and html files:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - Portfolio</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" />
</head>
<body >
<header>
<nav >
<div >
<a asp-area="" asp-controller="Home" asp-action="Index">Portfolio</a>
<button type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span ></span>
</button>
<div >
<ul >
<li >
<a asp-area="" asp-controller="Home" asp-action="Index">Home</a>
</li>
<!--
<li >
<a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</li>
-->
<li >
<a asp-area="" asp-controller="Home" asp-action="Resume">Resume</a>
</li>
<!----
<li >
<a asp-area="" asp-controller="Home" asp-action="Art3D">3D Art</a>
</li>
<li >
<a asp-area="" asp-controller="Home" asp-action="Art2D">2D Art</a>
</li>
<!---->
<li >
<a asp-area="" asp-controller="Home" asp-action="Snake">Snake</a>
</li>
<li >
<a asp-area="" asp-controller="Home" asp-action="CodeExamples">Code Examples</a>
</li>
<li >
<a asp-area="" asp-controller="Home" asp-action="Ballad">Ballad of the Masked Bandits</a>
</li>
<!--
<li >
<a asp-area="" asp-controller="Home" asp-action="DataBaseHub">Database Hub</a>
</li>
--->
<!--
<li >
<a asp-area="" asp-controller="Home" asp-action="Unavailable">???</a>
</li>
-->
<!--Temporary Links-->
</ul>
</div>
</div>
</nav>
</header>
<div id="MainDiv">
<main role="main" style="width:100%">
<!--Where the other code goes-->
@{
ViewData["Title"] = "Snake Game";
}
<div >
<h1>Snake Game</h1>
</div>
<div ></div>
<div >
<div >
<div >
<div >
<h1>By the power of Javascript, here is a playable snake game.</h1>
<div ></div>
<h1 id="fiddleText">Give it a moment to load.</h1>
</div>
<div ></div>
<div >
<canvas onl oad="draw()" id="SnekGamCanvas" width="1000" height="1000"></canvas>
</div>
</div>
</div>
<div >
<div ></div>
<a asp-area="" asp-controller="Home" asp-action="Index">Back to Home</a>
<div ></div>
</div>
<!--The code be here but if you are reading this you probably already knew that-->
<script src="~/js/Snake.js"></script>
</div>
</main>
</div>
<footer >
<div >
© 2021 - Portfolio - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
<script src="../jsc3d-master/jsc3d/jsc3d.js"></script>
@RenderSection("Scripts", required: false)
</body>
</html>
.
/* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
for details on configuring this project to bundle and minify static web assets. */
a.navbar-brand {
white-space: normal;
text-align: center;
word-break: break-all;
}
/* Provide sufficient contrast against white background */
a {
color: #0366d6;
}
.btn-primary {
color: #fff;
background-image: linear-gradient(30deg, #b6e2dd, #2a5efe);
border-color: #1861ac;
}
/*Link colors*/
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
/* Sticky footer styles
-------------------------------------------------- */
html {
font-size: 14px;
}
@media (min-width: 768px) {
html {
font-size: 16px;
}
}
.border-top {
border-top: 1px solid #e5e5e5;
}
.border-bottom {
border-bottom: 1px solid #e5e5e5;
}
.box-shadow {
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
}
button.accept-policy {
font-size: 1rem;
line-height: inherit;
}
/* Sticky footer styles
-------------------------------------------------- */
html {
position: relative;
min-height: 100%;
}
body {
/* Margin bottom by footer height */
margin-bottom: 60px;
}
.footer {
position: absolute;
bottom: 0;
width: 100%;
white-space: nowrap;
line-height: 60px; /* Vertically center the text there */
}
/* My Stuff
--------------------------------------------------------------------------
--------------------------------------------------------------------------
--------------------------------------------------------------------------
*/
/*This gives me more control over the exact dark background color*/
.dark-bg
{
background-color: #161631;
}
.purple_gradient
{
/*The image used*/
background-image: linear-gradient(#4b1ac4, #fff);
height:100%;
width:100%;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
}
.test_box_blue
{
/* A container with a solid color and an outline */
background-color: #2d1eb2;
width: 100%;
height: 100%;
margin: 0px;
}
.test_box
{
border:solid #000000;
}
#MainDiv
{
padding:0;
margin:0;
left:0;
top:0;
width:100%;
height:100%;
}
.tundra_backround
{
background-image: url('../images/Tundra_Fixed.png');
width:100%;
height:100%;
}
.white_space_box
{
height:50 px;
}
.background_gradient
{
background-image:linear-gradient(320deg, #fff, #96cbde);
}
.glo_button
{
min-width: 30%;
height: 20%;
border-radius: 25px;
padding: 20px;
margin: 10px;
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2), 0 6px 20px 0 rgba(0,0,0,0.19);
transition-duration: 0.4s;
border: 4px solid #000;
}
.big_r_button {
background-color: #a10000;
color: #fff;
}
.big_r_button:hover {
color: #fff;
background-color: #4e0505;
}
.big_b_button {
background-color: #080e9f;
color: #fff;
}
.big_b_button:hover {
color: #fff;
background-color: #161631;
}
.big_g_button {
background-color: #0a7727;
color: #fff;
}
.big_g_button:hover {
color: #fff;
background-color: #07340e;
}
.big_p_button {
background-color: #6f1cbf;
color: #fff;
}
.big_p_button:hover {
color: #fff;
background-color: #2a073e;
}
.buffer
{
padding: 20px;
}
.big_text
{
font-size: 60px;
font-family:'Times New Roman', Times, serif;
text-shadow: 2px 2px rgb(12 14 39 / 0.67);
}
.fancy_text_box{
background-image: linear-gradient(300deg, #ece1c4, #c99e69);
border-radius: 25px;
border: 4px solid #5d3c08;
}
.simple_text_box{
background-color: #fff;
border: 2px solid #000;
}
.ghostly_text_box{
background-color: rgb(255 255 255 / 0.60);
border-radius: 25px;
padding: 10px;
border: 3px solid #000;
}
.thick_border{
border: 4px solid #000;
}
.black_and_white_gradient{
background-image: linear-gradient(310deg, #fff, #000);
}
.red_border{
padding: 0px;
margin: 0px;
border: 4px solid #8f0000;
}
.model_box{
border: 4px solid #000;
background-color: #fff;
border-radius: 25px;
}
.image_box{
border: 4px solid #000;
background-color: #fff;
}
.chain_image_box {
border-top: 4px solid #000;
border-left: 4px solid #000;
border-right: 4px solid #000;
border-bottom: 0px;
background-color: #fff;
}
.margin_setter {
margin: 20px;
padding: 20px;
}
#model_display_1{
}
CodePudding user response:
You're only moving to the next runner when the current runner is inside the button. So when your while
loop gets to a runner that isn't in the button, it gets stuck on that element and loops infinitely.
Take the runner = runner.next;
line out of the if.
while (runner) {
//this list should be populated only by buttons
console.log("a button! \n");
if (runner.element.isInside(mouseX, mouseY)) {
document.getElementById("fiddleText").innerHTML = ('clicked');
console.log("We are in the button! \n");
}
runner = runner.next;
}
A for
loop might make things simpler:
for (let runner = activeButtons.head; runner; runner = runner.next) {
if (runner.element.isInside(mouseX, mouseY)) {
document.getElementById("fiddleText").innerHTML = ('clicked');
console.log("We are in the button! \n");
}
}