Changing adjacent cells in table when an event is triggered in JQuery - javascript

I'm trying to make a game of lights out. I can get the lights to toggle on and off when i click them, but i am having trouble thinking up logic to make the adjacent one come one as well. For example if i click an edge of the table, I should see the three lights adjacent to the light i clicked, become lit. I'm thinking maybe it has something to do with the "this" bound in my click method, Maybe the "this" is only referencing the one i clicked on and not the adjacent ones. I need to know, perhaps how to get it to reference the adjacent ones?
<html>
<head>
<title>Lights Out!</title>
<script type="text/javascript" src="jquery-1.4.js"></script>
<script type= "text/javascript">
var gameBoard = new Array(getTdCount())
function lightStatus(position)
{
//if light is on
if(gameBoard[position] ==true)
{
//set the original status back to false
gameBoard[position] = false;
return false
}
//turn on light
gameBoard[position]=true;
return gameBoard[position]
}
function getTdCount()
{
return $("td").length;
}
function getTrCount()
{
return $("tr").length;
}
function switchLights( obj, num )
{
if(lightStatus(num))
{
$("img", obj).attr('src', 'on.png')
}
else
{
$("img", obj).attr('src', 'off.png')
}
}
$(document).ready(function()
{
$("#board tr td").hover(function()
{
$(this).css('border-color', '00FFCC');
},
function()
{
$(this).css('border-color', 'black')
})
var $offProtoType = $('#offprototype').css('display', 'block').removeAttr('id')
$('td').append($offProtoType)
$tds = $('#board tr td');
$tds.click(function()
{
var num = $tds.index(this) + 1;
switchLights(this, num)
})
});
</script>
<style type="text/css">
td
{
border-style:solid;
border-color:black;
background-color:black;
float:left;
}
body
{
background-color: grey;
color: green;
}
</style>
</head>
<body>
<img style = "display:none" id="offprototype" src ="off.png">
<img style = "display:none" id="onprototype" src ="on.png">
<h1 align="center">Lights Out<h1>
<table id="board" border="3" bgcolor="black" align="center">
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
<input type="button" value="Shuffle" onclick="change()"/>
</body>
</html>

Use $(this).next() and $(this).prev() to get a reference to the next & previous.
You can use $(this).index() to obtain the position of the 'this' among its siblings; so use something like the following for abobe and below.
var pos = $(this).index();
var prevRow = $(this).parent().prev('tr');
var above = prevRow && prevRow.children().slice(pos);
var nextRow = $(this).parent().next('tr');
var below = prevRow && prevRow.children().slice(pos);

Probably the easiest thing to do is take a look at http://jqueryminute.com/finding-immediately-adjacent-siblings/ if you want just the siblings. If you want the whole row then you can use parent to select the parent element of the cell which should be the row.

Related

dynamically added dom-elements not responding to jQuery-function

Consider the following code:
$(document).ready(function(){
var table1 = $("table").eq(0);
var row_list;
var rows;
var x;
var y;
$("#mybutton").click(function(){
row_list = table1.find("tr");
rows = row_list.length;
x = $("#field_x").val();
y = $("#field_y").val();
if(x>rows || y>rows){
var num;
if(x>y) num=x;
else num=y;
var n = num-rows;
var row; table1.find("tr").eq(0).clone();
while(1){
row = table1.find("tr").eq(0).clone();
table1.append(row);
n--;
if(n===0) break;
}
n = num-rows;
var td;
while(1){
td = table1.find("td").eq(0).clone();
table1.find("tr").append(td);
n--;
if(n===0) break;
}
}
var text = $("#text").val();
var css = $("#css").val();
$("table:eq(0) tr:eq(" + (x-1) + ") td:eq(" + (y-1) + ")").text(text).css("color", css);
});
table1.find("td").click(function(){
$(this).html("");
});
});
* {
font: 14px normal Arial, sans-serif;
color: #000000;
}
table {
margin: 50px auto;
}
table, td {
border: 1px solid #aaa;
border-collapse: collapse;
}
th {
padding: 10px;
font-weight: bold;
}
td {
background-color: #eeeeee;
width: 80px;
height: 80px;
}
table:first-child tr td {
cursor: pointer;
}
td[colspan="4"]{
text-align:center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tbody>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th colspan="4">Fill a field:</th>
</tr>
</thead>
<tbody>
<tr>
<td>Text: <br/><input type="text" id="text" value=""></td>
<td>Field X: <br/><input type="text" id="field_x" value=""></td>
<td>Field Y: <br/><input type="text" id="field_y" value=""></td>
<td>CSS: <br/><input type="text" id="css" value=""></td>
</tr>
<tr>
<td colspan="4"><button id="mybutton">Fill</button></td>
</tr>
</tbody>
</table>
What the program does is the following:
The user can choose a field by giving an x-value and a y-value. In this field the content from the input field with label "Text" is displayed.
- This part of the program works fine.
If the user chooses an x-value or a y-value larger than the current number of rows (columns), rows and columns are added until the number of rows/columns is equal to the value in the x-(or y-) field.
- This part of the program also works fine.
The only functionality that does not work is the following:
If the user clicks on one of the non-empty fields in the table, the content of the table is supposed to go back to its natural (empty) state.
To this end, the following function was added to the code (see last couple of lines in the javascript part of the code):
table1.find("td").click(function(){
$(this).html("");
});
This piece of code basically means:
If the user clicks on any box ("td") in the table, the content of this box should disappear.
This is more or less the most simple part of the code. But it's also the one aspect that doesn't work. More precisely: It works for the original boxes, but it doesn't work for any boxes that were added. - And I don't get why it behaved that way.
If you are dynamically adding elements to the DOM and expect to be attaching events to them, you should consider using event delegation via the on() function :
// This will wire up a click event for any current AND future 'td' elements
$(table1).on('click', 'td', function(){
$(this).html("");
});
Simply using click() on it's own will only wire up the necessary event handlers for elements that exist in the DOM at the time of that function being called.
You're assigning the event handlers before the user has a chance to input any data. This means that if an additional row or column is added, the new <td>s need event handlers added manually.
Alternately, you can add a single click handler to the entire table:
table1.click(function (ev) { $(ev.target).html(''); }
The ev.currentTarget property will be the <table> element because that's the element the event handler is registered to, but the ev.target property will be the <td> element that you're looking for.
Here's a JSFiddle to experiment with.
Hey there here's what I thought the answer might be,
HTML File:
<!DOCTYPE html>
<html lang="de-DE">
<head>
<meta charset="UTF-8" />
<style>
* {
font: 14px normal Arial, sans-serif;
color: #000000;
}
table {
margin: 50px auto;
}
table, td {
border: 1px solid #aaa;
border-collapse: collapse;
}
th {
padding: 10px;
font-weight: bold;
}
td {
background-color: #eeeeee;
width: 80px;
height: 80px;
}
table:first-child tr td {
cursor: pointer;
}
td[colspan="4"]{
text-align:center;
}
.pre-height {
min-height: 80px;
}
</style>
</head>
<body>
<table>
<tbody>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td class="pre-height"></td>
<td class="pre-height"></td>
<td class="pre-height"></td>
<td class="pre-height"></td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th colspan="4">Fill a field:</th>
</tr>
</thead>
<tbody>
<tr>
<td>Text: <br/><input type="text" id="text" value=""></td>
<td>Field X: <br/><input type="text" id="field_x" value=""></td>
<td>Field Y: <br/><input type="text" id="field_y" value=""></td>
<td>CSS: <br/><input type="text" id="css" value=""></td>
</tr>
<tr>
<td colspan="4"><button id="myButton">Fill</button></td>
</tr>
</tbody>
</table>
<script src="jquery.min.js"></script>
<script src="jack.js"></script>
</body>
</html>
JACK.JS file:
window.onload = function() {
'use strict';
/**
* Appends 'n' number of rows to the table body.
*
* #param {Number} n - Number of rows to make.
*/
var makeRows = function(n) {
let tbody= document.getElementsByTagName("table")[0].getElementsByTagName("tbody")[0],
tr = document.querySelector("table:first-of-type tbody tr");
for (let i = 0; i < n; i++) {
let row = Node.prototype.cloneNode.call(tr, true);
tbody.appendChild(row);
}
};
/**
* Appends 'n' number of cells to each row.
*
* #param {Number} n - Number of cells to add to each row.
*/
var makeColumns = function(n) {
let addNCells = (function(n, row) {
for (let i = 0; i < n; i++) {
let cell = Node.prototype.cloneNode.call(td, true);
row.appendChild(cell);
}
}).bind(null, n);
let tbody= document.getElementsByTagName("table")[0].getElementsByTagName("tbody")[0],
td = document.querySelector("table:first-of-type tbody tr td"),
rows = document.querySelectorAll("table:first-of-type tbody tr");
rows.forEach(function(row) {
addNCells(row);
});
};
document.getElementById("myButton").addEventListener("click", () => {
let x = document.getElementById("field_x").value,
y = document.getElementById("field_y").value;
makeColumns(x);
makeRows(y);
});
/**
* Newly added code
*/
(function() {
let table = document.querySelector("table");
// We will add event listener to table.
table.addEventListener("click", (e) => {
e.target.innerHTML = "";
e.target.style.backgroundColor = "orange";
});
})();
};
Edit: And I didn't even answer the question completely. You might wanna attach event listener to the nearest non-dynamic parent so that click event will bubble up and you can capture that, check the code under the comment newly added code.

Move html td element using JavaScript

I am currently creating a basic javascript car racer whereby the car is a an HTML tr element placed in a td. I want to have it that when I push the key "e" player one will move and if the "d" key is pressed player 2 will move. I want to do this by using an eventListener to have it that when the key is pressed the car will move from its original td element to the next td element and remove the car from the previous. This will make it look like the car has moved. I do not know how to have this element moved using the key press but my code is below. Thanks for the help!
document.addEventListener('DOMContentLoaded', function() {
//run the code
function move = function(player){
/*
psuedo code:
if Keypressed = "E"
then move first car forward
else d move second car forward
if player1 has moved to the end
then end the game
else for player 2
set a var to add up the total moves, user can then easily adapted the length of the road and have it match
the var total.
*/
}
function keyPress = function(e){
if (e.charCode == "e"){
//move player one
}
else if (e.charCode == "d"){
//move player 2
}
else{
alert("Invalid key stroke");
}
}
})
.racer_table td {
background-color: white;
height: 50px;
width: 50px;
border: 5px;
border-color: black;
}
.racer_table td.active {
background-color: black;
}
<link rel="stylesheet" type = "text/css" href="style.css">
<script type="text/javascript" src="racer.js"> </script>
<body>
<table class="racer_table">
<tr id="player1_strip">
<td class="active"></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr id="player2_strip">
<td class="active"></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
</body>
</html>
I would set a var with the object then use jquery's 'next' function for sibling object.
if you add this button <button id="x1"/> this will work as an example:
$("#x1").click(function() {
var active = $("#player1_strip").children(".active");
active.next().addClass("active");
active.removeClass("active");
});
No jQuery solution. There is probably some other solution without need for an id, but I think this one is cleaner. You add an id to each <td> like 'tile1e', 'tile2e' etc. Then, your keypress function would look like this:
function keyPress = function(e){
if (e.charCode == "e"){
var tile = document.querySelector("#player1_strip > .active"); // get player tile
var nextTile = 'tile' + (tile.id[4] + 1) + 'e'; // get id of the next tile
tile.className = "";
document.getElementById(nextTile).className = "active";
}
else if (e.charCode == "d"){
var tile = document.querySelector("#player2_strip > .active"); // get player tile
var nextTile = 'tile' + (tile.id[4] + 1) + 'd'; // get id of the next tile
tile.className = "";
document.getElementById(nextTile).className = "active";
}
else{
alert("Invalid key stroke");
}
}
and the table something like this
<table class="racer_table">
<tr id="player1_strip">
<td id='tile0e' class="active"></td>
<td id='tile1e'></td>
<td id='tile2e'></td> <!-- move active to the next empty td element -->
<td id='tile3e'></td>
<td id='tile4e'></td>
<td id='tile5e'></td>
<td id='tile6e'></td>
<td id='tile7e'></td>
<td id='tile8e'></td>
<td id='tile9e'></td>
</tr>
<tr id="player2_strip">
<td id='tile0d' class="active"></td>
<td id='tile1d'></td>
<td id='tile2d'></td>
<td id='tile3d'></td>
<td id='tile4d'></td>
<td id='tile5d'></td>
<td id='tile6d'></td>
<td id='tile7d'></td>
<td id='tile8d'></td>
<td id='tile9d'></td>
</tr>
</table>
I hope you get the idea behind this.
But feel free to use the jQuery solution if you are comfortable with that, it is so much simpler.

How to keep adding data to next table column

This Fiddle Example shows a comparison table that dynamically shows information in a column when a button is clicked. Once the table is filled up, I want to start the whole process again. But as the example shows, the buttons are stuck at adding information to th:nth-child(2) and td:nth-child(2) during the second time instead of moving on to the next column like during the first time.
I'm guessing this part needs some change if( allCells === fullCells ) { to keep information being added to next columns.
HTML
<div class="area">
<button>Gah</button>
</div>
<div class="area">
<button>Kaj</button>
</div>
<div class="area">
<button>Fdw</button>
</div>
<div class="area">
<button>ffdf</button>
</div>
<table>
<thead>
<tr>
<th>Placeholder</th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Age</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Name</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Race</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Nationality</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Education</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Language</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
Code:
$(function() {
$('.area').each(function(){
var area = $(this),
filltable ='',
button = $(this).find('button'),
buttontext = button.text();
button.on("click",function(){
var allCells = $('table').find('th,td').length;
var fullCells = $('table').find('th,td').filter(function() {
return $(this).text() != '';
}).length;
if( allCells === fullCells ) { // If table is full
$('table').find('th,td').not(':first-child').removeClass('addinfo');
filltable();
}
else { // If table isn't full
filltable = function(){
var i = $('th.addinfo').length !== 0 ? $('th.addinfo:last').index() : 0;
console.log( i );
i + 2 > $('th').length ||
$('th,td').filter(':nth-child(' + (i + 2) + ')')
.addClass( 'addinfo' ).html(buttontext);
}
filltable();
}
}); // end button on click function
});
});
Please see the attached link for demo. I have created a function name cleartable() which clears the table if its full and have used your old filltable() function to repopulate. There is repetition of code which you will have to clean up.
th:nth-child(2) identifies second child of th.
td:nth-child(2) identifies second column.
Similarly if you wanted to do something with let say second row, you can use tr:nth-child(2).
I hope this helps you understand a little about parent-child relationship in jquery.
JSFIDDLE DEMO
function clearTable() {
$('table th:nth-child(2)').html('');
$('table th:nth-child(3)').html('');
$('table th:nth-child(4)').html('');
$('table td:nth-child(2)').html('');
$('table td:nth-child(3)').html('');
$('table td:nth-child(4)').html('');
}
http://jsfiddle.net/jqVxu/1/
I think you'd better to count th only. count all td and th make me confused.
$(function () {
function filltable(buttontext) {
var i = $('th.addinfo').length !== 0 ? $('th.addinfo:last').index() : 0;
i + 2 > $('th').length || $('th,td').filter(':nth-child(' + (i + 2) + ')')
.addClass('addinfo').html(buttontext);
}
$('.area').each(function () {
var area = $(this),
button = $(this).find('button'),
buttontext = button.text();
button.on("click", function () {
var allCells = $('table').find('th').length-1;
var fullCells = $('table th.addinfo').length;
console.log(allCells, fullCells);
if (allCells === fullCells) { // If table is full
$('table .addinfo').removeClass('addinfo');
filltable(buttontext);
} else { // If table isn't full
filltable(buttontext);
}
});
});
});

Minification of Javascript and increasing performance

Here is my code for the html:
<div class="board">
<table id="mastermind_table_one">
<tr id="one">
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
<table id="mastermind_table_two">
<tr id="two">
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
<table id="mastermind_table_three">
<tr id="three">
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
</div>
Here is my code for the Javascript:
$('.next_round').click(function() {
var count = 3;
var counter = setInterval(timer, 1000);
function timer() {
count = count - 1;
if (count === 0) {
$('#mastermind_table_two').each(function() {
$(this).find('td').each(function() {
$(this).css("background-color", setRandomColor);
})
})
clearInterval(counter);
}
})
I am building a game in which when the "next_round" button is clicked, all the empty tds are filled with a random background color (there's more code, such as setRandomColor function, but it's not relevant to the question).
I have ten mastermind_tables total, and that is where the problem lies. I am repeating this code for each table. Does anyone know how I can have this listed just once and move to the next td when I click "next_round"?
i.e. the js code above is specific to mastermind_table_two. How can I have it be dynamic and move to the next td once the previous one is executed?
UPDATED
My mistake, must have readed your question in the worng way
Try this, its a bit of a hacky but might fit into your needs
$('.next_round').click(function () {
$('[id^=mastermind_table_]').each(function () {
$(this).addClass("notTreated");//a fake class just to control
});
var counter = setInterval(timer, 1000);
function timer() {
var currenttable;
if ((currenttable = $(".notTreated").first()) == null) {
clearInterval(counter);
return;
}
$(currenttable).find('td').each(function () {
$(this).css("background-color", setRandomColor);
});
//after treat this table, removes the fake class
$(currenttable).removeClass("notTreated");
}
});

Highlight repeating strings

I have up to three almost-identical divs that contain tables of usernames. Some names may be repeated across the three divs. I'd like to highlight the names that are repeated. The first occurrence of the user should not be colored, the second occurrence should have an orange background and the third should have a red background. The divs go from left to right in order so the first div should not have any highlighted usernames.
My HTML looks like:
<div class="col-md-4">
<table class="table table-striped">
<tr>
<th>2/26/2014</th>
</tr>
<tr>
<td>user1</td>
</tr>
<tr>
<td>user2</td>
</tr>
<tr>
<td>user5</td>
</tr>
<tr>
<td>user17</td>
</tr>
</table>
</div>
<div class="col-md-4">
<table class="table table-striped">
<tr>
<th>2/27/2014</th>
</tr>
<tr>
<td>user13</td>
</tr>
<tr>
<td>user5</td>
</tr>
<tr>
<td>user7</td>
</tr>
</table>
</div>
<div class="col-md-4">
<table class="table table-striped">
<tr>
<th>2/28/2014</th>
</tr>
<tr>
<td>user5</td>
</tr>
<tr>
<td>user45</td>
</tr>
<tr>
<td>user1</td>
</tr>
</table>
</div>
I know that the username table cells will be selected with $('table.table td') (if I use jQuery) but I'm not sure what to do from there.
Please let me know if you have any questions.
Thank you.
Is this what you want?
I created a map to store text-occurrence pairs. Each time the text is repeated, the counter associated with it gets incremented. If the counter climbs to a certain value, the background will be set to another color. Give it a shot!
DEMO
var map = new Object();
$('td').each(function() {
var prop = $(this).text()
var bgColor = '#FFFFFF';
if (map[prop] > 1) {
bgColor = '#FF0000';
} else if (map[prop] > 0) {
bgColor = '#FF7F00';
}
$(this).css('background', bgColor);
if (map.hasOwnProperty(prop)) {
map[prop]++;
} else {
map[prop] = 1;
}
});
You could try something like this but I didn't test it
$('td').each(function(){
var text = this.html();
$('td:contains("'+text+'"):nth-child(2)').css({'background':'orange'});
$('td:contains("'+text+'"):nth-child(3)').css({'background':'red'});
});
Edit:
Not particularly elegant but it seems to work
http://jsfiddle.net/63L7L/1/
var second = [];
var third = [];
$('td').each(function(){
var text = $(this).html();
second.push($("td").filter(function() {
return $(this).text() === text;
})[1])
third.push($("td").filter(function() {
return $(this).text() === text;
})[2])
});
$(second).each(function(){
$(this).css({'background':'red'});
});
$(third).each(function(){
$(this).css({'background':'orange'});
});
With pure Javascript (ECMA5)
CSS
.orange {
background-color:orange;
}
.red {
background-color:red;
}
Javascript
Array.prototype.forEach.call(document.querySelectorAll('.table-striped td'), function (td) {
var textContent = td.textContent;
if (this.hasOwnProperty(textContent)) {
td.classList.add(++this[textContent] === 2 ? 'orange' : 'red');
} else {
this[textContent] = 1;
}
}, {});
On jsFiddle

Categories

Resources