I am trying to make a chess game. So far i have only made the white pawns moveable (you move them by dragging them to where you want them to go). However, only the first move works. Why does this happen? I use
$(".piece").mousedown(function(){}
, but it is only called once.
The problem is $("#" + tileFn).append($("#" + tileIn).html()); which creates a new piece element, to whom the mousedown handler is not attached.
One solution is to use event deletagation, or instead of creating a new element just move the existing element like
function parent(element) {
var parentID = $(element).parent().attr("ID");
var parentClass = $(element).parent().attr("class");
var parentType = $(element).parent().get(0).nodeName;
if (parentID != null) {
return ("#" + parentID);
} else if (parentClass != null) {
return ("." + parentClass);
} else {
if (parentType.toLowerCase() == "body") {
parentType = document;
return parentType;
} else {
return parentType;
}
}
}
var dimensions = 600; // must be divisible by 8
var tile1 = "<div class='tile tile1' id='";
var tile2 = "<div class='tile tile2' id='";
var end = "'></div>";
var multiplicity = "";
var tileIn = "";
var tileFi = "";
var classes = "";
var color = "";
var type = "";
var possible = [];
$(document).ready(function() {
//setup start
for (var i = 0; i < 4; i++) {
for (var j = 0; j < 4; j++) {
row = i * 2 + 1;
column = j * 2 + 1;
$("#container").append(tile1 + row + column + end + tile2 + row + (column + 1) + end);
}
for (var k = 0; k < 4; k++) {
row = i * 2 + 2;
column = k * 2 + 1;
$("#container").append(tile2 + row + column + end + tile1 + row + (column + 1) + end);
}
}
$("#container").css({
height: dimensions,
width: dimensions
});
$(".tile").css({
height: dimensions / 8,
width: dimensions / 8
});
$(".piece").css({
height: dimensions / 8,
width: dimensions / 8
});
$("<div class='b p piece'><img src='bp.icns' height='69'></div>").appendTo("#21, #22, #23, #24, #25, #26, #27, #28");
$("<div class='b r piece'><img src='br.icns' height='69'></div>").appendTo("#11, #18");
$("<div class='b n piece'><img src='bn.icns' height='69'></div>").appendTo("#12, #17");
$("<div class='b b piece'><img src='bb.icns' height='69'></div>").appendTo("#13, #16");
$("<div class='b k piece'><img src='bk.icns' height='69'></div>").appendTo("#14");
$("<div class='b q piece'><img src='bq.icns' height='69'></div>").appendTo("#15");
$("<div class='w p piece'><img src='wp.icns' height='69'></div>").appendTo("#71, #72, #73, #74, #75, #76, #77, #78");
$("<div class='w r piece'><img src='wr.icns' height='69'></div>").appendTo("#81, #88");
$("<div class='w n piece'><img src='wn.icns' height='69'></div>").appendTo("#82, #87");
$("<div class='w b piece'><img src='wb.icns' height='69'></div>").appendTo("#83, #86");
$("<div class='w q piece'><img src='wq.icns' height='69'></div>").appendTo("#84");
$("<div class='w k piece'><img src='wk.icns' height='69'></div>").appendTo("#85");
//setup end
$(".piece").mousedown(function() {
tileIn = parent($(this)).substr(1, 2);
classes = $(this).attr("class");
color = classes.charAt(0);
type = classes.charAt(2);
y = tileIn.charAt(0);
x = tileIn.charAt(1);
//white start
if (color == "w") {
//white pawn start
if (type == "p") {
if (y == "7") {
possible = ["6" + x, "5" + x];
} else {
possible = [(y - 1) + x];
}
return;
}
//white pawn end
//
else if ("a" == "b") {
}
}
//white end
//black start
else {
}
//white end
});
$(".tile").mouseup(function() {
tileFn = $(this).attr("id");
if (jQuery.inArray(tileFn, possible) !== -1 && $(this).is(':empty')) {
$("#" + tileFn).append($("#" + tileIn).contents());
} else {}
possible = [];
});
});
* {
user-drag: none;
-moz-user-select: none;
-webkit-user-drag: none;
}
#container {
margin: 0 auto;
border: 1px solid gray;
}
.tile {
float: left;
}
.tile1 {
background-color: white;
}
.tile2 {
background-color: rgba(0, 0, 0, 0.58);
}
.piece {
padding: 3px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/jquery-ui.min.js"></script>
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/themes/smoothness/jquery-ui.css">
<div id="container"></div>
The selector $(".piece") only selects the fields which has pices on it at the time you execute the statement. You will have to add the function to the fields as the pieces are moved on the board.
So you mouseup function should probably set the call back for the piece on the new field.
Related
in this tic tac toe game, why the last move of last player to play, is not showed? So, when it's the last turn of the game, nothing is shown on the square, not an O and neither an X. What is the problem?
The code follows:
This is the HTML code:
<div id="tictactoe" align="center"></div>
<div align="center">
<span id='turn'>Turn of Player X</span>
</div>
This is the CSS code:
table {
border-collapse:collapse;
}
td {
background-color: black;
border: 3px solid white;
font-size:60px;
color:#ffffff;
border-radius: 10px 10px 10px 10px;
}
And this is the JavaScript code:
var N_SIZE = 3,
EMPTY = " ",
boxes = [],
turn = "X",
score,
moves;
/*
* Initializes the Tic Tac Toe board and starts the game.
*/
function init() {
var board = document.createElement('table');
board.setAttribute("border", 1);
board.setAttribute("cellspacing", 0);
var identifier = 1;
for (var i = 0; i < N_SIZE; i++) {
var row = document.createElement('tr');
board.appendChild(row);
for (var j = 0; j < N_SIZE; j++) {
var cell = document.createElement('td');
cell.setAttribute('height', 50);
cell.setAttribute('width', 50);
cell.setAttribute('align', 'center');
cell.setAttribute('valign', 'center');
cell.classList.add('col' + j,'row' + i);
if (i == j) {
cell.classList.add('diagonal0');
}
if (j == N_SIZE - i - 1) {
cell.classList.add('diagonal1');
}
cell.identifier = identifier;
cell.addEventListener("click", set);
row.appendChild(cell);
boxes.push(cell);
identifier += identifier;
}
}
document.getElementById("tictactoe").appendChild(board);
startNewGame();
}
/*
* New game
*/
function startNewGame() {
score = {
"X": 0,
"O": 0
};
moves = 0;
turn = "X";
boxes.forEach(function (square) {
square.innerHTML = EMPTY;
});
}
/*
* Check if a win or not
*/
function win(clicked) {
// Get all cell classes
var memberOf = clicked.className.split(/\s+/);
for (var i = 0; i < memberOf.length; i++) {
var testClass = '.' + memberOf[i];
var items = contains('#tictactoe ' + testClass, turn);
// winning condition: turn == N_SIZE
if (items.length == N_SIZE) {
return true;
}
}
return false;
}
function contains(selector, text) {
var elements = document.querySelectorAll(selector);
return [].filter.call(elements, function(element){
return RegExp(text).test(element.textContent);
});
}
/*
* Sets clicked square and also updates the turn.
*/
function set() {
if (this.innerHTML !== EMPTY) {
return;
}
this.innerHTML = turn;
moves += 1;
score[turn] += this.identifier;
if (win(this)) {
alert('Winner: Player ' + turn);
startNewGame();
} else if (moves === N_SIZE * N_SIZE) {
alert("Draw");
startNewGame();
} else {
turn = turn === "X" ? "O" : "X";
document.getElementById('turn').textContent = 'Turn of Player ' + turn;
}
}
init();
This is the link: https://codepen.io/sp2012/pen/gOmXqOO
Thank you.
Use a timeout in set() before you start a new game:
setTimeout(...,0)
Maybe take a look at this:
var N_SIZE = 3,
EMPTY = " ",
boxes = [],
turn = "X",
score,
moves;
/*
* Initializes the Tic Tac Toe board and starts the game.
*/
function init() {
var board = document.createElement('table');
board.setAttribute("border", 1);
board.setAttribute("cellspacing", 0);
var identifier = 1;
for (var i = 0; i < N_SIZE; i++) {
var row = document.createElement('tr');
board.appendChild(row);
for (var j = 0; j < N_SIZE; j++) {
var cell = document.createElement('td');
cell.setAttribute('height', 50);
cell.setAttribute('width', 50);
cell.setAttribute('align', 'center');
cell.setAttribute('valign', 'center');
cell.classList.add('col' + j, 'row' + i);
if (i == j) {
cell.classList.add('diagonal0');
}
if (j == N_SIZE - i - 1) {
cell.classList.add('diagonal1');
}
cell.identifier = identifier;
cell.addEventListener("click", set);
row.appendChild(cell);
boxes.push(cell);
identifier += identifier;
}
}
document.getElementById("tictactoe").appendChild(board);
startNewGame();
}
/*
* New game
*/
function startNewGame() {
score = {
"X": 0,
"O": 0
};
moves = 0;
turn = "X";
boxes.forEach(function(square) {
square.innerHTML = EMPTY;
});
}
/*
* Check if a win or not
*/
function win(clicked) {
// Get all cell classes
var memberOf = clicked.className.split(/\s+/);
for (var i = 0; i < memberOf.length; i++) {
var testClass = '.' + memberOf[i];
var items = contains('#tictactoe ' + testClass, turn);
// winning condition: turn == N_SIZE
if (items.length == N_SIZE) {
return true;
}
}
return false;
}
function contains(selector, text) {
var elements = document.querySelectorAll(selector);
return [].filter.call(elements, function(element) {
return RegExp(text).test(element.textContent);
});
}
/*
* Sets clicked square and also updates the turn.
*/
function set() {
if (this.innerHTML !== EMPTY) {
return;
}
this.innerHTML = turn;
moves += 1;
score[turn] += this.identifier;
if (win(this)) {
setTimeout(()=>{
if (confirm('Winner: Player ' + turn))
startNewGame();
}, 0)
} else if (moves === N_SIZE * N_SIZE) {
setTimeout(()=>{
if (confirm('Winner: Player ' + turn))
startNewGame();
}, 0)
} else {
turn = turn === "X" ? "O" : "X";
document.getElementById('turn').textContent = 'Turn of Player ' + turn;
}
}
init();
table {
border-collapse: collapse;
}
td {
background-color: black;
border: 3px solid white;
font-size: 60px;
color: #ffffff;
border-radius: 10px 10px 10px 10px;
}
<div id="tictactoe" align="center"></div>
<div align="center">
<span id='turn'>Turn of Player X</span>
</div>
When you are checking if the player has won the rerender is not happening immediately. When the alert pops up the executiuon is stopped and right after you click OK new game is started. You can use a little hack with setTimeout to show the alert after the rerender has happened
if (win(this)) {
setTimeout(function() {
alert('Winner: Player ' + turn);
startNewGame();
}, 0);
} else if (moves === N_SIZE * N_SIZE) {
setTimeout(function() {
alert("Draw");
startNewGame();
}, 0);
}
Run the Code Snippet Please, it'll allow you to understand my question better
As you can see, I have cells with numbers and one Cell with Symbol (S) and red color.
If i click on any other cells, my (S)"UniqueCell" will move there.
lets say I click on Cell with number 55, My UniqueCell will move there, replacing the 55 with (S), now I click on other cell, lets say cell that have number 320, my UniqueCell moves from cell 55 to cell 320, now my UniqueCell replaced the 320, with it's (S), however cell 55 gained back its numbers.
How i can prevent the cells from gaining back its numbers? how i can make it lose its numbers permanently once i clicked on it?
note: I'm trying to make a game where player A pick vertical and player B to pick horizontal, hence the green moving vertical and horizontal every click, if possible, i want each time i click on cell where the green is, if it was Vertical, player gain those points, if it was horizontal, player b gain the points
var isCol = 0;
var board = [];
for (r = 0; r < 7; r++) {
var line = [];
for (c = 0; c < 7; c++) {
line.push(RandomGenerator(50, 500));
}
board.push(line);
}
function prs(curr, c, r) {
showTable(curr, c, r);
isCol = (isCol + 1) % 2;
}
function toColor(col, row, chosen_col, chosen_row) {
var ret = false;
switch (isCol) {
case 0:
if (row == chosen_row) {
ret = true;
}
break;
case 1:
if (col == chosen_col) {
ret = true;
}
break;
}
return ret;
}
function showTable(c, chosen_col, chosen_row) {
var str = "";
str += "<table border=1>";
for (row = 0; row < 7; row++) {
str += "<tr>";
for (let col = 0; col < 7; col++) {
str += "<td onclick='prs(this, " + col + "," + row + ")'";
if (toColor(col, row, chosen_col, chosen_row)) {
if(c.textContent == board[row][col]){
str += " class=uniqueCell";
}
else str += " class='grn' ";
}
str += ">";
if(c.textContent == board[row][col]){
str += 'S';
}
else str += board[row][col];
str += "</td>";
}
str += "</tr>";
}
str += "</table>";
document.getElementById("ff").innerHTML = str;
}
function RandomGenerator(min, max) {
return Math.floor(Math.random() * (max - min) + min);
}
showTable(-1);
var getUnique = function(){
var tdElements = document.querySelectorAll('#ff td');
tdElements[
RandomGenerator(0, tdElements.length)
].classList.add('uniqueCell');
// update the text of the cell using the class
document.querySelector('.uniqueCell').textContent = 'S';
};
getUnique();
td{
border:2px solid black;
width:10px;
height:10px;
text-align: center;
}
td:hover{background-color:lightgreen;}
.grn{
background-color:green;
color:white;
}
.uniqueCell {
background-color: tomato;
}
<div id="ff"></div>
we keep points collected in object P1 & P2 it has for now points property P1.points & P2.points
I added inside prs() funtion called in Onclick
$('#turn').text(`Player ${(isCol+1)} turn`);
if (CellPoint) {
if (isCol) {P1.points+=CellPoint;}else{P2.points+= CellPoint;}
$('#p1').text(`Player 1: ${P1.points}`);
$('#p2').text(`Player 2: ${P2.points}`);
} else {
console.log('selected S');
}
var isCol = 0;
var CellPoint = 0;
var board = [];
var P1 = {
points: 0
};
var P2 = {
points: 0
};
for (r = 0; r < 7; r++) {
var line = [];
for (c = 0; c < 7; c++) {
line.push(RandomGenerator(50, 500));
}
board.push(line);
}
function prs(curr, c, r) {
CellPoint = parseInt($(curr).text());
showTable(curr, c, r);
isCol = (isCol + 1) % 2;
clr = isCol ? 'blue' : 'red';
$(curr).text('S');
$('#turn').css("color", clr)
.text(`Player ${(isCol+1)} turn`);
if (CellPoint) {
if (isCol) {
P1.points += CellPoint;
} else {
P2.points += CellPoint;
}
$('#p1').text(`Player 1: ${P1.points}`);
$('#p2').text(`Player 2: ${P2.points}`);
} else {
console.log('selected S');
}
}
function toColor(col, row, chosen_col, chosen_row) {
var ret = false;
switch (isCol) {
case 0:
if (row == chosen_row) {
ret = true;
}
break;
case 1:
if (col == chosen_col) {
ret = true;
}
break;
}
return ret;
}
function showTable(c, chosen_col, chosen_row) {
if(c!==-1){board[chosen_row][chosen_col] = 'S';}
var str = "";
str += "<table border=1>";
for (row = 0; row < 7; row++) {
str += "<tr>";
for (let col = 0; col < 7; col++) {
str += "<td onclick='prs(this, " + col + "," + row + ")'";
if(board[row][col]=='S'){
str += " class=uniqueCell";
} else{
if (toColor(col, row, chosen_col, chosen_row)) {
str += " class='grn' ";} }
str += ">";
if(board[row][col]=='S') {
str += 'S';
} else str += board[row][col];
str += "</td>";
}
str += "</tr>";
}
str += "</table>";
document.getElementById("ff").innerHTML = str;
}
function RandomGenerator(min, max) {
return Math.floor(Math.random() * (max - min) + min);
}
showTable(-1);
var getUnique = function() {
var tdElements = document.querySelectorAll('#ff td');
tdElements[
RandomGenerator(0, tdElements.length)
].classList.add('uniqueCell');
// update the text of the cell using the class
document.querySelector('.uniqueCell').textContent = 'S';
};
getUnique();
td {
border: 2px solid black;
width: 10px;
height: 10px;
text-align: center;
}
td:hover {
background-color: lightgreen;
}
.grn {
background-color: green;
color: white;
}
.turn1 {
background-color: green;
color: red;
}
.turn0 {
background-color: green;
color: blue;
}
.uniqueCell {
background-color: tomato;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div><span id='p1' style='color:red;'>Player1: </span> X <span style='color:blue;' id='p2'>Player2: </span></div>
<p id='turn'>Player 1 turn</p>
<div id="ff"></div>
The issue is you are redrawing the table in each click of cells, as a result keeping track of the previously clicked elements are little bit difficult. Instead create the table only for single (first) time then loop through all the td's to add the style and text based on specific condition.
You can try the following way:
var isCol = 0;
var board = [];
for (r = 0; r < 7; r++) {
var line = [];
for (c = 0; c < 7; c++) {
line.push(RandomGenerator(50, 500));
}
board.push(line);
}
function showTable(c, chosen_col, chosen_row) {
var str = "";
str += "<table border=1>";
for (row = 0; row < 7; row++) {
str += "<tr>";
for (let col = 0; col < 7; col++) {
str += `<td>${board[row][col]}</td>`;
}
str += "</tr>";
}
str += "</table>";
document.getElementById("ff").innerHTML = str;
}
function RandomGenerator(min, max) {
return Math.floor(Math.random() * (max - min) + min);
}
showTable(-1);
var getUnique = function(){
var tdElements = document.querySelectorAll('#ff td');
tdElements[
RandomGenerator(0, tdElements.length)
].classList.add('uniqueCell');
// update the text of the cell using the class
document.querySelector('.uniqueCell').textContent = 'S';
};
getUnique();
var tds = document.querySelectorAll('td');
var isPlayerA = true;
var playerA = 0;
var playerB = 0;
tds.forEach(function(td){
td.addEventListener('click', function(){
document.querySelectorAll('.grn').forEach(td => td.classList.remove('grn'));
var col = this.cellIndex + 1;
var row = this.parentNode.rowIndex;
if(isPlayerA){
this.parentNode.querySelectorAll('td').forEach(td => td.classList.add('grn'));
isPlayerA = false;
playerA += Number(this.textContent);
}
else{
document.querySelectorAll(`td:nth-child(${col})`).forEach(c =>c.classList.add('grn'));
isPlayerA = true;
playerB += Number(this.textContent);
}
this.textContent = 'S';
this.classList.add('uniqueCell');
console.clear();
console.log(`Player A: ${playerA} :: Player B: ${playerB}`);
});
});
td{
border:2px solid black;
width:10px;
height:10px;
text-align: center;
}
td:hover{background-color:lightgreen;}
.grn{
background-color:green;
color:white;
}
.uniqueCell {
background-color: tomato;
}
<div id="ff"></div>
I created a grid with random boxes, yellow and one red.
The problem is that when I refresh the page sometimes the red box doesn't appear, is hidden somewhere, I guess under a yellow box. Also, sometimes even the yellow boxes are not all displayed.
I guess there's a problem in the loop to create them?
var grid = document.getElementById("grid-box");
for (var i = 1; i <= 100; i++) {
var square = document.createElement("div");
square.className = 'square';
square.id = 'square' + i;
grid.appendChild(square);
}
var obstacles = [];
while (obstacles.length < 10) {
var randomIndex = parseInt(99 * Math.random());
if (obstacles.indexOf(randomIndex) === -1) {
obstacles.push(randomIndex);
document.getElementById('square' + randomIndex).style.backgroundColor = 'yellow';
}
}
var playerOne = [];
while (playerOne.length < 1) {
var randomIndex = parseInt(99 * Math.random());
if (playerOne.indexOf(randomIndex) === -1) {
playerOne.push(randomIndex);
document.getElementById('square' + randomIndex).style.backgroundColor = 'red';
}
}
#grid-box {
width: 400px;
height: 400px;
margin: 0 auto;
font-size: 0;
position: relative;
}
#grid-box>div.square {
font-size: 1rem;
vertical-align: top;
display: inline-block;
width: 10%;
height: 10%;
box-sizing: border-box;
border: 1px solid #000;
}
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<div id="grid-box"></div>
You have to change 2 things:
1. initial value of i should be 0 when you create squares
2. when you make red box then
replace
if (playerOne.indexOf(randomIndex)
with
if (playerOne.indexOf(randomIndex) === -1 && obstacles.indexOf(randomIndex) === -1) {
Here is the final code:
var grid = document.getElementById("grid-box");
// create 100 squares
for (var i = 0; i < 100; i++) { // first change
var square = document.createElement("div");
square.className = 'square';
square.id = 'square' + i;
grid.appendChild(square);
}
var obstacles = [];
while (obstacles.length < 10) {
var randomIndex = parseInt(99 * Math.random());
if (obstacles.indexOf(randomIndex) === -1) {
obstacles.push(randomIndex);
document.getElementById('square' + randomIndex).style.backgroundColor = 'yellow';
}
}
var playerOne = [];
while (playerOne.length < 1) {
var randomIndex = parseInt(99 * Math.random());
if (playerOne.indexOf(randomIndex) === -1 && obstacles.indexOf(randomIndex) === -1) { // second change
playerOne.push(randomIndex);
document.getElementById('square' + randomIndex).style.backgroundColor = 'red';
}
}
It is because, when randomIndex would be zero(0), then you are searching element whose id is "square0" and it is not available because your for loop starts runs from 1 to 100.
Math.random() would sometimes return 0, but your ids are starting from 1 eg: 'square1', there is no 'square0' div.
Make your loop starts from 0:
for (var i = 0; i < 100; i++){
// Code here
}
I have a grid with a player, yellow box, and obstacles (.ob) and black boxes. I don't want the player to go in the obstacle squares when I click the 'UP' button.
I was thinking to check if the next class has .ob do not go there. Any suggestions?
let moveCounter = 0;
var grid = document.getElementById("grid-box");
for (var i = 1; i <= 50; i++) {
var square = document.createElement("div");
square.className = 'square';
square.id = 'square' + i;
grid.appendChild(square);
}
var obstacles = [];
while (obstacles.length < 20) {
var randomIndex = parseInt(49 * Math.random());
if (obstacles.indexOf(randomIndex) === -1) {
obstacles.push(randomIndex);
var drawObstacle = document.getElementById('square' + randomIndex);
$(drawObstacle).addClass("ob")
}
}
var playerTwo = [];
while (playerTwo.length < 1) {
var randomIndex = parseInt(49 * Math.random());
if (playerTwo.indexOf(randomIndex) === -1) {
playerTwo.push(randomIndex);
var drawPtwo = document.getElementById('square' + randomIndex);
$(drawPtwo).addClass("p-1")
}
};
$('#button_up').on('click', function() {
moveCounter += 1;
$pOne = $('.p-1')
var id = $pOne.attr('id')
var idNumber = +id.slice(6);
var idMove = idNumber - 10
var idUpMove = 'square' + idMove;
$pOne.removeClass('p-1');
$('#' + idUpMove).addClass('p-1');
});
#grid-box {
width: 400px;
height: 400px;
margin: 0 auto;
font-size: 0;
position: relative;
}
#grid-box > div.square {
font-size: 1rem;
vertical-align: top;
display: inline-block;
width: 10%;
height: 10%;
box-sizing: border-box;
border: 1px solid #000;
}
.p-1 {
background-color: yellow;
}
.ob {
background-color: black;
}
<div id="grid-box"></div>
<div class="move">
<button id="button_up">UP</button><br>
</div>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
jsFifddle
Use the following code
$('#button_up').on('click', function() {
moveCounter += 1;
$pOne = $('.p-1')
var id = $pOne.attr('id')
var idNumber = +id.slice(6);
var idMove = idNumber - 10
var idUpMove = 'square' + idMove;
if($('#' + idUpMove).hasClass('ob')){
return false;
}
$pOne.removeClass('p-1');
$('#' + idUpMove).addClass('p-1');
});
Here we check the next selected class having ".ob" class if its return true then we stop the process if it returns false then process continues
if($('#' + idUpMove).hasClass('ob')){
return false;
}
Fiddle
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%;
}