JS OOP - multiple timer created by object but only one works - javascript

This js is to create "lyricLine" and showing strings on random position of the page.
While creating an object of oLyricLine and calling drawText, it supposed to display each string of array lyricLines one by one until last line is shown. When last line is time-out, the timer should stop and the created div.lyricline should be deleted.
While different objects are created, (in this case LyricGen1 and LyricGen2) their timer should be ticking simultaneously.
But currently only timer of LyricGen2 is working.
Code is attached below.
<script src="jquery-min.js"></script>
<div id="container">
<div class="lyricline" id="0">
demo
</div>
</div>
<style>
body{
background: #000;
color: #fff;
font-family: sans-serif;
}
#container{
position: relative;
width: 100%;
height: 100%;
}
.lyricline{
position: absolute;
text-align: center;
}
</style>
<script>
var idarray = [];
//Object oLyricLine
function oLyricLine(obj){
this.lyricLines = [];
this.textSize = 30;
this.toppx = -1;
this.leftpx = -1;
this.unixtime = -1;
this.widthpx = -1;
this.colorhex = "#fff";
this.obj = obj;
}
oLyricLine.prototype.drawText = function(){
if (this.toppx != -1 && this.leftpx != -1 && this.unixtime != -1 && this.widthpx != -1){
var transitionInTime = 10;
var transitionOutTime = 10;
var tickfactor = 5;
$("#"+this.unixtime).css({
"color": this.colorhex,
"left": this.leftpx,
"top": this.toppx,
"width": this.widthpx,
"font-size":this.textSize
}).attr({"class":"lyricline","id":this.unixtime}).text(this.lyricLines[0]);
var tickCount = 0;
lyricLinesCount = 0;
var lyricLinesTick = [];
for (var i =0; i <= this.lyricLines.length - 1; i++) {
lyricLinesTick[i] = this.lyricLines[i].length * tickfactor;
if(i>0){lyricLinesTick[i]+=lyricLinesTick[i-1];}
};
var nLyricLines = this.lyricLines;
var nUnixtime = this.unixtime;
idarray[nUnixtime] = setInterval(function () {
tickCount += 1;
if (tickCount == lyricLinesTick[lyricLinesCount]){
lyricLinesCount +=1;
if(lyricLinesCount != lyricLinesTick.length){
$("#"+nUnixtime).text(nLyricLines[lyricLinesCount]);
}else{
$("#"+nUnixtime).remove();
clearInterval(idarray[nUnixtime]);
}
}
},100);
}
};
oLyricLine.prototype.widthGen = function() {
this.widthpx = maxWidth(this.lyricLines, this.textSize);
};
var unixtime1=Date.now();
$("#container").append($("<div></div>").attr("id",unixtime1));
var obj1=$("#"+unixtime1);
var LyricGen1 = new oLyricLine(obj1);
LyricGen1.lyricLines = ["gen1 line1","1.line2","1-------LINE#3"];
LyricGen1.textSize = 50;
LyricGen1.toppx = Math.floor(Math.random()*($(window).height()-LyricGen1.textSize));
LyricGen1.widthGen();
LyricGen1.leftpx = Math.floor(Math.random()*($(window).width()-parseInt(LyricGen1.widthpx)));
LyricGen1.unixtime = unixtime1;
LyricGen1.drawText();
$("#container").append($("<div></div>").attr("id","Datenow"));
var obj2=$("#Datenow");
var LyricGen2 = new oLyricLine(obj2);
LyricGen2.lyricLines = ["2.1","TWO=two","2........3","gen2 line number 4","2>>line5"];
LyricGen2.textSize = 80;
LyricGen2.toppx = Math.floor(Math.random()*($(window).height()-LyricGen1.textSize));
LyricGen2.widthGen();
LyricGen2.leftpx = Math.floor(Math.random()*($(window).width()-parseInt(LyricGen1.widthpx)));
LyricGen2.unixtime = "Datenow";
LyricGen2.drawText();
function strlen(str){ var len = 0; for (var i=0; i<str.length; i++) { var c = str.charCodeAt(i); if ((c >= 0x0001 && c <= 0x007e) || (0xff60<=c && c<=0xff9f)) { len++; } else { len+=2; } } return len; }
function maxWidth (lyricLines, textSize) {
var maxStringLength=0, maxStringId=-1;
for (var i = lyricLines.length - 1; i >= 0; i--) {
if (maxStringLength < strlen(lyricLines[i])){
maxStringLength = strlen(lyricLines[i]);
maxStringId = i;
};
};
$("#container").append($("<div></div>").css({
"background":"#fff",
"color":"#000",
"visibility":"hidden",
"font-size":textSize
}).attr({"class":"lyricline","id":"widgen"}).text(lyricLines[maxStringId]));
var maxPxLength = $("#widgen").css("width");
$("#widgen").remove();
return maxPxLength;
}
</script>

lyricLinesCount = 0;
You're missing a var here, making lyricLinesCount an implicit global variable. You want to have it local to each timer, though.

Related

Need help fixing my Conway Game of Life code

I am making a Conway Game of Life that it starts with a predefined board, I made that board through an two dimensional Array, have implemented the rules and they seem to be working, the problem is that the different generations just appear next to previous one without replacing it (see image [1])
image:
[![enter image description here][1]][1]
So, what i needed is to make the following generations replace the previous ones on the HTML display.
var gfg=new Array(10);
for (var COL=0; COL<gfg.length;COL++){
gfg[COL]=new Array(10);
}
run();
function run(){
board1();
rules();
}
function rules(){
for (var i=0; i<10;i++){
for(var j=0; j<10; j++){
const cell = gfg[i][j];
let numNeighbours = 0;
for (let x = -1; x < 2; x++) {
for (let y = -1; y < 2; y++) {
if (x === 0 && y === 0) {
continue;
}
var x_cell = i + x;
var y_cell = j + y;
if (x_cell > 0 && y_cell > 0 && x_cell <0 && y_cell <0) {
const currentNeighbour = 1;
numNeighbours=numNeighbours+currentNeighbour;
}
}
}
if (cell === 1 && numNeighbours < 2) {
gfg[i][j] = 0;
} else if (cell === 1 && numNeighbours > 3) {
gfg[i][j] = 0;
} else if (cell === 0 && numNeighbours === 3) {
gfg[i][j] = 1;
}
}
draw();
}
}
function draw(){
for (var i=0; i<10;i++){
for(var j=0; j<10; j++){
//Writes in HTML according to the coordinate value
if(gfg[i][j]===0){
document.write("◻");
}else if(gfg[i][j]===1){
document.write("◼");
}
}
document.write("<br>");
}
}
//predefined board
function board1() {
gfg[0][0] = 1;
gfg[0][1] = 0;
gfg[0][2] = 1;
gfg[0][3] = 0;
gfg[0][4] = 0;
gfg[0][5] = 1;
gfg[0][6] = 0;
gfg[0][7] = 0;
gfg[0][8] = 0;
gfg[0][9] = 1;
gfg[1][0] = 0;
gfg[1][1] = 0;
gfg[1][2] = 0;
gfg[1][3] = 0;
gfg[1][4] = 0;
gfg[1][5] = 0;
gfg[1][6] = 0;
gfg[1][7] = 1;
gfg[1][8] = 0;
gfg[1][9] = 0;
gfg[2][0] = 0;
gfg[2][1] = 0;
gfg[2][2] = 0;
gfg[2][3] = 1;
gfg[2][4] = 0;
gfg[2][5] = 1;
gfg[2][6] = 1;
gfg[2][7] = 0;
gfg[2][8] = 0;
gfg[2][9] = 0;
gfg[3][0] = 0;
gfg[3][1] = 0;
gfg[3][2] = 1;
gfg[3][3] = 0;
gfg[3][4] = 1;
gfg[3][5] = 0;
gfg[3][6] = 0;
gfg[3][7] = 0;
gfg[3][8] = 0;
gfg[3][9] = 1;
gfg[4][0] = 0;
gfg[4][1] = 0;
gfg[4][2] = 0;
gfg[4][3] = 0;
gfg[4][4] = 1;
gfg[4][5] = 0;
gfg[4][6] = 0;
gfg[4][7] = 0;
gfg[4][8] = 0;
gfg[4][9] = 0;
gfg[5][0] = 0;
gfg[5][1] = 1;
gfg[5][2] = 0;
gfg[5][3] = 0;
gfg[5][4] = 0;
gfg[5][5] = 0;
gfg[5][6] = 0;
gfg[5][7] = 0;
gfg[5][8] = 0;
gfg[5][9] = 0;
gfg[6][0] = 0;
gfg[6][1] = 0;
gfg[6][2] = 0;
gfg[6][3] = 0;
gfg[6][4] = 1;
gfg[6][5] = 0;
gfg[6][6] = 1;
gfg[6][7] = 0;
gfg[6][8] = 1;
gfg[6][9] = 0;
gfg[7][0] = 1;
gfg[7][1] = 0;
gfg[7][2] = 0;
gfg[7][3] = 1;
gfg[7][4] = 0;
gfg[7][5] = 0;
gfg[7][6] = 0;
gfg[7][7] = 1;
gfg[7][8] = 0;
gfg[7][9] = 0;
gfg[8][0] = 0;
gfg[8][1] = 0;
gfg[8][2] = 1;
gfg[8][3] = 0;
gfg[8][4] = 1;
gfg[8][5] = 0;
gfg[8][6] = 0;
gfg[8][7] = 0;
gfg[8][8] = 0;
gfg[8][9] = 0;
gfg[9][0] = 0;
gfg[9][1] = 1;
gfg[9][2] = 0;
gfg[9][3] = 0;
gfg[9][4] = 0;
gfg[9][5] = 0;
gfg[9][6] = 0;
gfg[9][7] = 0;
gfg[9][8] = 1;
gfg[9][9] = 0;
}
[1]: https://i.stack.imgur.com/ZKrFj.png
this is if you are using document.write()
All you will have to do is clear the container of the previous write with document.body.innerHTML = ""; this will clear your previous writes and then you can go through the loop again.
If you need to use html and javascript only canvas will still work as it is a built in html tag. However if you still wish to proceed with using html to display your cells then create a new div tag and give it an id e.g: id="life_container" then grab a reference to this tag when the page finishes loading and on every frame empty the div then go through your loop and fill it with your elements.
var container;
document.onload = function() {
container = document.getElementById('life_container');
}
// clear container
container.innerHtml = "";
// fill container
container.innerHtml += gfg[i][j] === 0 ? "◻" : "◼";
However I might suggest some improvements by using canvas instead of html to display your game of life. You can use P5 library, to run your loop and display it. You will need to add in extra calculation to figure out "where" to draw your cell on canvas but not much more.

How to make this memory board game work?

I am trying to make a memory board game. I got the css part of the game done, but how it the JavaScript part suppose to work out. I tried using the codes below. When I click on the box, even if they are the same, the box won't disappear and when it's not the same number, the box doesn't turn back. Also, for my "gamebox", I want to add a picture to be the background. I couldn't get it to work. Can anyone help me. Thanks.
<html>
<style>
#gamebox
{
position: absolute;
top: 100px;
left: 100px;
background-image: url("https://www.google.com/url?sa=i&rct=j&q=&esrc=s&source=images&cd=&cad=rja&uact=8&ved=&url=http%3A%2F%2Fwww.pokemontimes.it%2Fhome%2F2014%2F10%2Fannunciato-il-pokemon-center-mega-tokyo-in-apertura-a-dicembre%2F%3F%3Drelated&psig=AFQjCNFGPAm9tU9MR4AZJKe1s6F90F8UFg&ust=1454720806721506");
}
div.box
{
position: absolute;
background-color: red;
top: -800px;
left: -800px;
width: 100px;
height: 100px;
text-align: center;
font-size: 30px;
}
div.box:hover
{
background-color: blue;
}
</style>
<div id=gamebox></div>
<div id=boxdiv class=box onclick="clickedBox(this)"></div>
<script>
var squaresize = 100;
var numsquares = 6;
var numClicked = 0;
var firstClicked;
var secondClicked;
var game = [];
for (var i = 0; i < numsquares; i++)
{
game[i] = [];
for (var j = 0; j < numsquares; j++)
{
game[i][j] = Math.floor(Math.random()*5);
makeBox(i, j);
}
}
function theSame(abox, bbox)
{
var boxParts = abox.id.split("-");
var i = boxParts[1];
var j = boxParts[2];
var boxaNum = game[i][j];
var boxParts = bbox.id.split("-");
i = boxParts[1];
j = boxParts[2];
var boxbNum = game[i][j];
return(boxaNum == boxbNum);
}
function nextTurn()
{
if (numClicked != 2)
return;
if (theSame(firstClicked, secondClicked))
{
deleteBox(firstClicked);
deleteBox(secondClicked);
}
else
{
hideBox(firstClicked);
hideBox(secondClicked);
}
numClicked = 0;
}
function hideBox(box)
{
box.innerHTML = "";
box.style.backgroundColor = "red";
}
function deleteBox(box)
{
//really delete the box
box.style.backgroundColor = "";
}
function showBox(box)
{
var boxParts = box.id.split("-");
var i = boxParts[1];
var j = boxParts[2];
box.innerHTML = game[i][j];
box.style.backgroundColor = "black";
box.style.color = "white";
}
function clickedBox(box)
{
showBox(box);
numClicked++;
if (numClicked == 1)
{
firstClicked = box;
return;
}
if (numClicked == 2)
{
secondClicked = box;
}
}
function makeBox(i, j)
{
var boxdiv = document.getElementById("boxdiv");
var newBox = boxdiv.cloneNode(true);
newBox.style.left = i * (squaresize + 5);
newBox.style.top = j * (squaresize + 5);
newBox.style.width = squaresize;
newBox.style.height = squaresize;
newBox.id = 'box-' + i + '-' + j;
var gamebox = document.getElementById("gamebox");
gamebox.appendChild(newBox);
}
</script>
</html>
I think you're not calling nextTurn() anywhere in your code, meaning theSame() is never called, so nothing gets compared to eachother.
Maybe try calling nextTurn() when the numClicked === 2 in the clickedBox() function.

Javascript calculator backspace not working on first time

I am in the process of creating a calculator in javascript, and am trying to make a backspace. My initial thought was to use the .slice function, and it works, to a point. It works in the way that it takes a number off, but I am having trouble getting it to actually take off the number from the first value, if that makes any sense. It doesn't work on the first time, for example, if I type in 12, then use the backspace, then make the number 13 and do 13 + 6; it will come up with NaN. However if I do that same process again, it works as long as i use my clear button and not refresh the page. I have tried everything I can think of, bu't can't get it to work. Here is the code, sorry its a lot, but I figured it be better to have it all there. I am open to any suggestions, but prefer not to use jQuery, as I haven't yet learned a single thing about jQuery
Code:
//Variables
var xValue = 0;
var xValue2 = 0;
//Button Values
var plusButton = 0;
var subButton = 0;
var timesButton = 0;
var divideButton = 0;
var squaredButton = 0;
var powerButton = 0;
//Answers
var sum = 0;
var difference = 0;
var product = 0;
var quotent = 0;
var square = 0;
var power = 0;
//Functions
function add () {
if (plusButton >= 1) {
xValue2 = xValue;
xValue = "";
}
subButton = 0;
timesButton = 0;
divideButton = 0;
squaredButton = 0;
powerButton = 0;
//alert(xValue);
}
function addFunction () {
sum = +xValue + +xValue2;
answer.innerHTML = sum;
}
function subtract () {
if (subButton >= 1) {
xValue2 = xValue;
xValue = "";
}
plusButton = 0;
timesButton = 0;
divideButton = 0;
squaredButton = 0;
powerButton = 0;
}
function subtractFunction () {
difference = +xValue2 - +xValue1;
answer.innerHTML = difference;
}
function multiply () {
if(timesButton >= 1) {
temp = xValue2;
xValue2 = xValue;
xValue = "";
}
subButton = 0;
plusButton = 0;
divideButton = 0;
squaredButton = 0;
powerButton = 0;
}
function multiplyFunction () {
product = +xValue * +xValue2;
answer.innerHTML = product;
}
function divide () {
if(divideButton >= 1) {
temp = xValue2;
xValue2 = xValue;
xValue = "";
}
subButton = 0;
plusButton = 0;
timesButton = 0;
squaredButton = 0;
powerButton = 0;
}
function divideFunction () {
var quotent = +xValue / +xValue2;
answer.innerHTML = quotent;
}
function squared () {
subButton = 0;
plusButton = 0;
timesButton = 0;
divideButton = 0;
powerButton = 0;
}
function squaredFunction () {
square = Math.pow(xValue, 2);
answer.innerHTML = square;
}
function powerNum () {
if(powerButton >= 1) {
xValue2 = xValue;
xValue = "";
}
plusButton = 0;
subButton = 0;
timesButton = 0;
divideButton = 0;
squaredButton = 0;
}
function powerFunction () {
var power = Math.pow(xValue2, xValue);
answer.innerHTML = power;
}
function compute () {
if(plusButton >= 1) {
addFunction();
xValue = sum;
}
else if (subButton >= 1) {
subtractFunction();
xValue = difference;
}
else if (timesButton >= 1) {
multiplyFunction();
xValue = product;
}
else if (divideButton >= 1) {
divideFunction();
xValue = quotent;
}
else if (squaredButton >= 1) {
squaredFunction();
xValue = square;
}
else if (powerButton >= 1) {
powerFunction();
xValue2 = power;
}
xValue2 = 0;
plusButton = 0;
subButton = 0;
timesButton = 0;
divideButton = 0;
squaredButton = 0;
powerButton = 0;
}
function clearScreen() {
answer.innerHTML = "";
xValue = 0;
plusButton = 0;
xValue2 = 0;
temp = 0;
}
function backOne () {
var str = answer.innerHTML;
var res = str.slice(0, -1);
answer.innerHTML = res;
xValue = parseInt(res);
}
You can use substring on the current displayed value in the calculator and replace the current value with the substring of it. The substring would be (0, length - 1).
http://www.w3schools.com/jsref/jsref_substring.asp
EDIT: should be length - 1

Conway Game of Life - cells won't appear on canvas

I am trying to code a Conway Game of Life app in a canvas on a webpage. However after writing the code, I cannot get the cells to appear on my canvas element. Can you tell what I am doing wrong?
My guess is that is has to do with the functions for the buttons - the functions I wrote are supposed to initialize the game, but it does not happen.
<!DOCTYPE html>
<html>
<head>
<script>
var CONWAY = (function() {
var my = {};
var Board = function() { //create a game board object
this.cells = {}; //collection object to hold cell coordinates
};
var Cell = function(x, y, alive) { //create a cell object
this.x = x; //the x coordinate for the cell
this.y = y; //the y coordinate for the cell
this.alive = alive; //state to show if the cell is alive or dead
};
Cell.prototype = { //add isAlive method to the cell object
isAlive: function() {
return this.alive; //return cell's state
}
};
Board.prototype = {
addCell: function(cell) { //the method to add a cell to the board
this.cells[getCellRepresentation(cell.x, cell.y)] = cell;
},
getCellAt: function(x,y) { //returns the value of the specified coordinates
return this.cells[getCellRepresentation(x,y)];
},
getAliveNeighbors: function(cell) { //check nearby cells for states according to the rules
var x = cell.x;
var y = cell.y;
var aliveCells = 0;
for(var i = -1; i < 2; i++) { //look for live cells in the vicinity
for(var j = -1; j < 2; j++) {
if(i === 0 && i === j) {
continue;
}
var currentCell = this.getCellAt(x + i, y + j);
if(currentCell && currentCell.isAlive()) {
aliveCells++;
}
}
}
return aliveCells; //return the number of live cells
},
calculateNextState: function(cell) {
var tempCell = new Cell(cell.x, cell.y, cell.alive);
var livingNeighbors = this.getAliveNeighbors(cell);
if(cell.alive) {
if(livingNeighbors === 2 || livingNeighbors === 3) {
tempCell.alive = true;
}
else {
tempCell.alive = false;
}
}
else {
if(livingNeighbors === 3) {
tempCell.alive = true;
}
}
return tempCell;
},
step: function() {
var cells = this.cells;
var tempBoard = {};
for(var c in this.cells) {
var cell = this.cells[c];
var newCell = this.calculateNextState(cell);
tempBoard[c] = newCell;
}
this.cells = tempBoard;
}
};
function getCellPrepresentation (x,y) {
return "x" + x + "y" + y;
}
var CONWAY_GLOB_MAX_X = 99;
var CONWAY_GLOB_MAX_Y = 23;
var randomInit = function(board) {
for (var y = 0; y <= CONWAY_GLOB_MAX_Y; y++) {
for (var x = 0; x <= CONWAY_GLOB_MAX_X; x++) {
board.addCell(new Cell(x, y, (Math.random() > 0.8)));
}
}
};
var draw = function(board, canvas) {
var output = ' ';
for(var y = 0; y <= CONWAY_GLOB_MAX_X; y++) {
for(var x = 0; x <= CONWAY_GLOB_MAX_Y; x++) {
var cell = board.getCellAt(x,y);
output += cell && cell.isAlive() ? 'o' : ' ';
}
output += '\n\
';
}
canvas.html(output);
};
var doConway = function(body) {
var board = new Board(); //create new board
randomInit(board); //initialize board with random dead and alive cells
draw(board, body); //draw the board
return setInterval(function() { //update every 130 ms
board.step();
draw(board, body);
}, 130);
};
my.initConway = function(id, body) {
clearInterval(id);
return doConway(body);
};
my.resetConway = function(id, body) {
body.empty();
clearInterval(id);
};
var conwayRandIntervalId = null;
var conwayBody = getElementbyId('conway');
var resetGame = function() {
this.resetConway(conwayRandIntervalId, conwayBody);
};
var startGame = function() {
conwayRandIntervalId = this.initConway(conwayRandIntervalId, conwayBody);
}
return my;
}());
</script>
<style type="text/css">
div.conway {
overflow: hidden;
font-family: courier;
float: left;
width: 500px;
height: 288px;
background-color: #000;
font-size: 10px;
color: #fff;
}
</style>
</head>
<body>
<div>
<button id='startCW' onclick="startGame();">Random Conway's Game of Life</button> | <button id='resetCW' onclick="resetGame();">Reset</button>
<div class='conway' id='conway'></div>
</div>
</body>
</html>
I had a little bit of time, so I took the liberty of fixing this application.
The initial problem is you were not exposing the function startGame properly, the button has no way to trigger it. I exposed it through your CONWAY object by setting it on my. Then you were missing the document part of document.getElementById. Next you were trying to call getCellRepresentation, but it should have been getCellPrepresentation.
The next issue was related to having your script execute before the dom has been loaded. There are a few ways around this, such as using jquerys document load function and placing your logic in there, but the easiest way was to just put your script tag as the LAST tag in your body. As the browser iterates through the html, its going to build the dom elements, and then execute your code, which allows your document.getElementById to return the proper element. Doing this before the dom loads will always return null.
I changed your canvas.html(output) to canvas.innerHTML=output, as this was a dependency on jquery that I did not have on my machine. Either way is acceptable.
Finally I cleaned up your output. In your version the output was falling all on one line because you were doing things such as using spaces and \n's. Since we were in an html block I changed the output to 's and <br />'s. Another solution would be to use a <pre> element instead of a div, but I haven't tested this.
I may have gone a little too far with the help, so sorry if you were just looking for the reason there was no output. Post a comment if you have any questions :-)
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
div.conway {
overflow: hidden;
font-family: courier;
float: left;
width: 500px;
height: 288px;
background-color: #000;
font-size: 10px;
color: #fff;
}
</style>
</head>
<body>
<div>
<button id='startCW' onclick="CONWAY.startGame();">Random Conway's Game of Life</button> | <button id='resetCW' onclick=" CONWAY.resetGame();">Reset</button>
<div class='conway' id='conway'></div>
</div>
<script type="text/javascript">
var CONWAY = (function () {
var my = {};
var Board = function () { //create a game board object
this.cells = {}; //collection object to hold cell coordinates
};
var Cell = function (x, y, alive) { //create a cell object
this.x = x; //the x coordinate for the cell
this.y = y; //the y coordinate for the cell
this.alive = alive; //state to show if the cell is alive or dead
};
Cell.prototype = { //add isAlive method to the cell object
isAlive: function () {
return this.alive; //return cell's state
}
};
Board.prototype = {
addCell: function (cell) { //the method to add a cell to the board
this.cells[getCellPrepresentation(cell.x, cell.y)] = cell;
},
getCellAt: function (x, y) { //returns the value of the specified coordinates
return this.cells[getCellPrepresentation(x, y)];
},
getAliveNeighbors: function (cell) { //check nearby cells for states according to the rules
var x = cell.x;
var y = cell.y;
var aliveCells = 0;
for (var i = -1; i < 2; i++) { //look for live cells in the vicinity
for (var j = -1; j < 2; j++) {
if (i === 0 && i === j) {
continue;
}
var currentCell = this.getCellAt(x + i, y + j);
if (currentCell && currentCell.isAlive()) {
aliveCells++;
}
}
}
return aliveCells; //return the number of live cells
},
calculateNextState: function (cell) {
var tempCell = new Cell(cell.x, cell.y, cell.alive);
var livingNeighbors = this.getAliveNeighbors(cell);
if (cell.alive) {
if (livingNeighbors === 2 || livingNeighbors === 3) {
tempCell.alive = true;
}
else {
tempCell.alive = false;
}
}
else {
if (livingNeighbors === 3) {
tempCell.alive = true;
}
}
return tempCell;
},
step: function () {
var cells = this.cells;
var tempBoard = {};
for (var c in this.cells) {
var cell = this.cells[c];
var newCell = this.calculateNextState(cell);
tempBoard[c] = newCell;
}
this.cells = tempBoard;
}
};
function getCellPrepresentation(x, y) {
return "x" + x + "y" + y;
}
var CONWAY_GLOB_MAX_X = 99;
var CONWAY_GLOB_MAX_Y = 23;
var randomInit = function (board) {
for (var y = 0; y <= CONWAY_GLOB_MAX_Y; y++) {
for (var x = 0; x <= CONWAY_GLOB_MAX_X; x++) {
board.addCell(new Cell(x, y, (Math.random() > 0.8)));
}
}
};
var draw = function (board, canvas) {
var output = ' ';
for (var y = 0; y <= CONWAY_GLOB_MAX_X; y++) {
for (var x = 0; x <= CONWAY_GLOB_MAX_Y; x++) {
var cell = board.getCellAt(x, y);
output += cell && cell.isAlive() ? 'o' : ' ';
}
output += '<br/>';
}
canvas.innerHTML=(output);
};
var doConway = function (body) {
var board = new Board(); //create new board
randomInit(board); //initialize board with random dead and alive cells
draw(board, body); //draw the board
return setInterval(function () { //update every 130 ms
board.step();
draw(board, body);
}, 130);
};
my.initConway = function (id, body) {
clearInterval(id);
return doConway(body);
};
my.resetConway = function (id, body) {
body.empty();
clearInterval(id);
};
var conwayRandIntervalId = null;
var conwayBody = document.getElementById('conway');
my.resetGame = function () {
this.resetConway(conwayRandIntervalId, conwayBody);
};
my.startGame = function () {
conwayRandIntervalId = this.initConway(conwayRandIntervalId, conwayBody);
}
return my;
}());
</script>
</body>
</html>

Large list rendering in JavaScript

I am trying to render the list based on virtual rendering concept. I am facing some minor issues, but they are not blocking the behaviour. Here is the working fiddle http://jsfiddle.net/53N36/9/ and Here are my problems
Last items are not visible, I assume some where I missed indexing.(Fixed, Please see the edit)
How to calculate scrollPosition if I want to add custom scroll to this.
Is this the best method or any other?
I have tested it with 700000 items and 70 items in chrome. Below is the code
(function () {
var list = (function () {
var temp = [];
for (var i = 0, l = 70; i < l; i++) {
temp.push("list-item-" + (i + 1));
}
return temp;
}());
function listItem(text, id) {
var _div = document.createElement('div');
_div.innerHTML = text;
_div.className = "listItem";
_div.id = id;
return _div;
}
var listHold = document.getElementById('listHolder'),
ht = listHold.clientHeight,
wt = listHold.clientWidth,
ele = listItem(list[0], 'item0'),
frag = document.createDocumentFragment();
listHold.appendChild(ele);
var ht_ele = ele.clientHeight,
filled = ht_ele,
filledIn = [0];
for (var i = 1, l = list.length; i < l; i++) {
if (filled + ht_ele < ht) {
filled += ht_ele;
ele = listItem(list[i], 'item' + i);
frag.appendChild(ele);
} else {
filledIn.push(i);
break;
}
}
listHold.appendChild(frag.cloneNode(true));
var elements = document.querySelectorAll('#listHolder .listItem');
function MouseWheelHandler(e) {
var e = window.event || e;
var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
console.log(delta);
//if(filledIn[0] != 0 && filledIn[0] != list.length){
if (delta == -1) {
var start = filledIn[0] + 1,
end = filledIn[1] + 1,
counter = 0;
if (list[start] && list[end]) {
for (var i = filledIn[0]; i < filledIn[1]; i++) {
if (list[i]) {
(function (a) {
elements[counter].innerHTML = list[a];
}(i));
counter++;
}
}
filledIn[0] = start;
filledIn[1] = end;
}
} else {
var start = filledIn[0] - 1,
end = filledIn[1] - 1,
counter = 0;
if (list[start] && list[end]) {
for (var i = start; i < end; i++) {
if (list[i]) {
(function (a) {
elements[counter].innerHTML = list[a];
}(i));
counter++;
}
}
filledIn[0] = start;
filledIn[1] = end;
}
}
//}
}
if (listHold.addEventListener) {
listHold.addEventListener("mousewheel", MouseWheelHandler, false);
listHold.addEventListener("DOMMouseScroll", MouseWheelHandler, false);
} else listHold.attachEvent("onmousewheel", MouseWheelHandler);
}());
Please suggest me on this.
EDIT:
I have tried again and I am able to fix the indexing issue. http://jsfiddle.net/53N36/26/
But how can I calculate the scroll position based on the array list currently displayed.
Is this the best method or any other?
I think something that would make this much easier is not to try to handle scrolling yourself.
In this fiddle I show that you can let the browser handle scrolling for you, even though we are using virtual rendering.
Using .scrollTop I detect where the browser thinks the user is looking, and I draw in items based on that.
You'll note that if you set hidescrollbar to false and the user uses it to scroll, my method still runs fine.
Therefore, to calculate scroll position you can just use .scrollTop.
And as for custom scrolling, just make sure you influence the .scrollTop of #listHolder and recall refreshWindow()
CODE FROM FIDDLE
(function () {
//CHANGE THESE IF YOU WANT
var hidescrollbar = false;
var numberofitems = 700000;
//
var holder = document.getElementById('listHolder');
var view = null;
//get the height of a single item
var itemHeight = (function() {
//generate a fake item
var div = document.createElement('div');
div.className = 'listItem';
div.innerHTML = 'testing height';
holder.appendChild(div);
//get its height and remove it
var output = div.offsetHeight;
holder.removeChild(div);
return output;
})();
//faster to instantiate empty-celled array
var items = Array(numberofitems);
//fill it in with data
for (var index = 0; index < items.length; ++index)
items[index] = 'item-' + index;
//displays a suitable number of items
function refreshWindow() {
//remove old view
if (view != null)
holder.removeChild(view);
//create new view
view = holder.appendChild(document.createElement('div'));
var firstItem = Math.floor(holder.scrollTop / itemHeight);
var lastItem = firstItem + Math.ceil(holder.offsetHeight / itemHeight) + 1;
if (lastItem + 1 >= items.length)
lastItem = items.length - 1;
//position view in users face
view.id = 'view';
view.style.top = (firstItem * itemHeight) + 'px';
var div;
//add the items
for (var index = firstItem; index <= lastItem; ++index) {
div = document.createElement('div');
div.innerHTML = items[index];
div.className = "listItem";
view.appendChild(div);
}
console.log('viewing items ' + firstItem + ' to ' + lastItem);
}
refreshWindow();
document.getElementById('heightForcer').style.height = (items.length * itemHeight) + 'px';
if (hidescrollbar) {
//work around for non-chrome browsers, hides the scrollbar
holder.style.width = (holder.offsetWidth * 2 - view.offsetWidth) + 'px';
}
function delayingHandler() {
//wait for the scroll to finish
setTimeout(refreshWindow, 10);
}
if (holder.addEventListener)
holder.addEventListener("scroll", delayingHandler, false);
else
holder.attachEvent("onscroll", delayingHandler);
}());
<div id="listHolder">
<div id="heightForcer"></div>
</div>
html, body {
width:100%;
height:100%;
padding:0;
margin:0
}
body{
overflow:hidden;
}
.listItem {
border:1px solid gray;
padding:0 5px;
width: margin : 1px 0px;
}
#listHolder {
position:relative;
height:100%;
width:100%;
background-color:#CCC;
box-sizing:border-box;
overflow:auto;
}
/*chrome only
#listHolder::-webkit-scrollbar{
display:none;
}*/
#view{
position:absolute;
width:100%;
}

Categories