Animating a swap of cells in a table on click using JQuery - javascript

I'm trying to make a simple flash-card game in JavaScript using JQuery. In one part of the game, you should click on a row in a "table" (tablica) made out of divs (tablica[i][j]) to swap the cells in that row (to put the content in the cell in the correct column). Here is the relevant piece of code:
for (var j=0; j<odgovor1.length; j++)
for (var i=1; i<3; i++)
{
tablica[i][j]=document.createElement("div");
tablica[i][j].setAttribute("class","rijecUDrugomDijelu");
if (i===1) tablica[i][j].appendChild(document.createTextNode(odgovor1[j]));
else if (i===2) tablica[i][j].appendChild(document.createTextNode(odgovor2[j]));
tablica[i][j].style.top=228+27*j;
tablica[i][j].style.left=-153+110+153*i;
tablica[i][j].onclick=eval(
"(function()"+
"{"+
"var tmp=tablica[1]["+j+"].style.left;"+
"tablica[1]["+j+"].style.left=tablica[2]["+j+"].style.left;"+
"tablica[2]["+j+"].style.left=tmp;"+
"tmp=odgovor1["+j+"];"+
"odgovor1["+j+"]=odgovor2["+j+"];"+
"odgovor2["+j+"]=tmp;"+
"})"
);
pozadina.appendChild(tablica[i][j]);
}
When the user clicks on a row in that table, the cells in that row are swapped, and the content of the table is correctly tracked in the string arrays odgovor1 and odgovor2. However, they are swapped without any animation, they are swapped immediately. When I try to apply JQuery animations to the cells (divs) tablica[1][j] and tablica[2][j], the program crashes. Do you know how to do that properly?Again, I assure you, the code above works well now, but when I try to use JQuery animations instead of simply swapping the properties style.left, it crashes.

Using object properties instead of eval appears to work:
for (var j=0; j<odgovor1.length; j++)
for (var i=1; i<3; i++)
{
tablica[i][j]=document.createElement("div");
tablica[i][j].setAttribute("class","rijecUDrugomDijelu");
if (i===1) tablica[i][j].appendChild(document.createTextNode(odgovor1[j]));
else if (i===2) tablica[i][j].appendChild(document.createTextNode(odgovor2[j]));
tablica[i][j].style.top=228+27*j;
tablica[i][j].style.left=-153+110+153*i;
tablica[i][j].redak=j;
tablica[i][j].onclick=
function()
{
var tmp=tablica[1][this.redak].style.left;
$(tablica[1][this.redak]).animate({left:(tablica[2][this.redak].style.left)},500);
$(tablica[2][this.redak]).animate({left:(tmp)},500);
tmp=odgovor1[this.redak];
odgovor1[this.redak]=odgovor2[this.redak];
odgovor2[this.redak]=tmp;
}
pozadina.appendChild(tablica[i][j]);
}

Related

Why appending a div using html and jquery variable has different outputs?

Trying to solve a practice related to javaScript and JQuery, and have met an issue which I could not identify.
I created a variable to store a div, and tried to create multiple instances of it as follows:
(there is a logical mistake I have in inner loop related to the number of appended defineBox objects but this is something I am
working on separately,not related to this topic)
$("#wrapper").empty();
var lineBox=document.createElement('div');
for(var i=0; i<size; i++){
$('#wrapper').append('lineBox');
for(var j=0; j<size; j++){
$("#wrapper > div ").append(defineBox(boxSize));
}
}
When the code is executed, it shows only one "lineBox" div in html document. defineBox is appended to the lineBox div.
When I use the following code instead, it displays "i" times of 'div' just as expected from the loop and each 'div' includes 'defineBox's.
for(var i=0; i<size; i++){
$('#wrapper').append('<div></div>');
for(var j=0; j<size; j++){
$("#wrapper > div ").append(defineBox(boxSize));
}
}
I thought creating a var with document.createElement('div') would have the same result as '<div></div>'. Where does the difference come from?
Try the jQuery's .clone() method. You're creating one instanced of the div in the dom.
$("#wrapper").empty();
var lineBox=document.createElement('div');
$('#wrapper').append(lineBox);
for(var i=0; i<size-1; i++){
$('#wrapper').append(lineBox.clone());
for(var j=0; j<size; j++){
$("#wrapper > div ").append(defineBox(boxSize));
}
}
Here is the documentation for the clone method of jQuery:
https://api.jquery.com/clone/
Each time this expression called
$('#wrapper').append('<div></div>');
jQuery creates a new div element. In the first example you've shared, new div element created just once with var lineBox=document.createElement('div');

How to access rows in table generated in javascript

I've generated a table in my html by using this code:
var board=document.getElementById("tab");
for(var i=0; i<lvl1.rows; i++ )
{
var row=board.insertRow();
for(var j=0; j<lvl1.cols; j++)
{
var cell = row.insertCell();
}
}
The point is to keep the design of the page almost totally separated from the game engine (creating a Minesweeper game).
Imagine I want to change the colour of the cell in position [2][3]. How can I change the background colour of this cell if I don't have the "td's" and "tr's" in the HTML code?
Thanks
To access the ith cell of row j, use:
board.rows[j].cells[i]
You can set the background color style of a cell like this.
board.rows[j].cells[i].style.backgroundColor = "red";

Jquery wrapping my content with divs using append

for(var i=0; i<num_cols; i++)
{
//Wrapper for column
$('#cupcake-list').append('<div>');
//end wrapper
col_count++;
num_in_col = rowsInCol(total,num_perCol,col_count);
start = i*num_perCol;
end = start + num_in_col;
for(var d=start; d<end; d++)
{
$('#cupcake-list').append('<p>'+cupcakeData[d].name+'</p>');
}
//Wrapper for column
$('#cupcake-list').append('</div>');
//end wrapper
}
I just want to encapsulate my p tags within div tags to act as rows, however all I get are <div></div><p>ssdfsdf</p><p>sdfsdfdsf</p><div></div>etc....
What's the best way of doing it?
Start with a fragment so that you don't access the DOM more than once, and append it all at the end. You can skip the wrap by starting with your empty fragment, like so:
var $fragment;
for(var i=0; i<num_cols; i++)
{
$fragment = $('<div />');
col_count++;
num_in_col = rowsInCol(total,num_perCol,col_count);
start = i*num_perCol;
end = start + num_in_col;
for(var d=start; d<end; d++)
{
$fragment.append('<p>'+cupcakeData[d].name+'</p>');
}
//Wrapper for column
$('#cupcake-list').append($fragment);
//end wrapper
}
This is a much faster way to do it! Append parts of a string to an array and then you only have to update the DOM once.
var a = [];
for(var i=0; i<num_cols; i++)
{
a.push('<div>');
col_count++;
num_in_col = rowsInCol(total,num_perCol,col_count);
start = i*num_perCol;
end = start + num_in_col;
for(var d=start; d<end; d++)
{
a.push('<p>'+cupcakeData[d].name+'</p>');
}
a.push('</div>');
}
$('#cupcake-list').append(a.join(''));
EDIT:
I'll explain why yours wasn't working. When you were calling $('#cupcake-list').append('<div>'); you thought it would only add the opening div tag, but that is not the case. jQuery won't let you do this is because they want to make sure the html is valid after every function call. If you were to just add the opening div and then do some other stuff, the next closing div (</div>) in the document would close the div you just opened, changing the structure of the document entirely.
In summation:
$('#cupcake-list').append('<div>'); and $('#cupcake-list').append('</div>'); will both append <div></div> to the document. Also, access and update the DOM as if it costs you a million dollars because it is among the slowest things you can do in javascript.
jQuery has a method called .wrap, and some similar ones (.wrapAll).
If you are having the output that you showed, your code is not reaching the inner for, so you have a logic problem. I think your way of doing this is correct. When i need to build some structure on the fly i usually do the same thing.
JQuery append adds DOM nodes, not HTML. So you can accomplish your task like this:
for(var i=0; i<num_cols; i++)
{
col_count++;
num_in_col = rowsInCol(total,num_perCol,col_count);
start = i*num_perCol;
end = start + num_in_col;
for(var d=start; d<end; d++)
{
$('#cupcake-list').append($('<div></div>').append('<p>'+cupcakeData[d].name+'</p>'));
}
}
First, $('<div></div>') creates a new empty div element not yet attached to the page (you can also do $('<div>') as a shorthand if you want). Then .append('<p>...</p>') adds a p element inside the div. Finally, $('#cupcake-list').append(...) adds the whole div to the end of #cupcake-list.

Attaching change() even listeners to select/dropdown dynamically

I'm creating 3 dropdowns/select boxes on the fly and insert them in the DOM through .innerHTML.
I don't know the ID's of the dropdowns until I created them in Javascript.
To know which dropdowns have been created, I create an array where I store the ID's of the dropdowns I have created.
for(var i=0; i<course.books.length; i++)
{
output+="<label for='book_"+course.books[i].id+"'>"+ course.books[i].name +"</label>";
output+="<select id='variant"+course.books[i].id+"' name='book_"+course.books[i].id+"'>";
output+="<option value='-'>-- Select one --</option>";
for(var j=0; j<course.books[i].options.length; j++)
{
output+="<option value='"+course.books[i].options[j].id+"'>"+course.books[i].options[j].name+"</option>";
}
output+="</select>";
}
Now I have an array with 3 id's like:
dropdown1
dropdown2
dropdown3
What I want to accomplish with Javascript (without using jQuery or another framework) is to loop over these 3 dropdowns and attach a change event listener to them.
When a user changes the selection in one of these dropdown, I want to call a function called updatePrice for example.
I'm a bit stuck on the dynamic adding of event listeners here.
Now you have added your code its straight forward and you can ignore my verbose answer !!!
output+="<select id='variant"+course.books[i].id+"' name='book_"+course.books[i].id+"'>";
could become :
output+="<select onchange="updatePrice(this)" id='variant"+course.books[i].id+"' name='book_"+course.books[i].id+"'>";
This will call the updatePrice function, passing the select list that changed
However
IMO its far better (from a performance point of view for a start) to create elements in the DOM using the DOM.
var newSelect = document.createElement("select");
newSelect.id = "selectlistid"; //add some attributes
newSelect.onchange = somethingChanged; // call the somethingChanged function when a change is made
newSelect[newSelect.length] = new Option("One", "1", false, false); // add new option
document.getElementById('myDiv').appendChild(newSelect); // myDiv is the container to hold the select list
Working example here -> http://jsfiddle.net/MStgq/2/
You got the array already? Then you can do this:
function updatePrice()
{
alert(this.id + " - " + this.selectedIndex);
}
var list = ["dropdown1", "dropdown2"];
for(var i=0;i<list.length;i++)
{
document.getElementById(list[i]).onchange = updatePrice;
}
Fiddle: http://jsfiddle.net/QkLMT/3/
That won't work across browsers.
You'll want something like
for(var i = 0; i < list.length; i++)
{
$("#"+list[i]).change(updatePrice);
}
in jquery.

JavaScript: Search for a string in a row and delete row if string is found

I would like a script that removes every table row for which the keyword STRING is found in a cell, but my script seems to remove every other row that contains the STRING keyword. Apparently, every time a row is deleted the numbering of the rows is updated? How would one account for this? Thanks in advance.
<script type="text/javascript">
var table = document.getElementById("DatePreferred").firstChild;
var rowCount = table.rows.length;
for(var i=0; i<rowCount; i++) {
var row = table.rows[i];
var text = row.cells[0].innerText;
if(text.indexOf("STRING")!=-1){
table.deleteRow(i);
}
}
</script>
Edit: So FishBasketGordo's answer got my script to work for IE and Safari but it wasn't working in FF. I looked into where the error was and apparently FF handles .innerText differently. You have to use .textContent instead. So if you add this below to the script above it will use the appropriate method:
if (row.cells[0].textContent){
var text = row.cells[0].textContent;}
else {var text = row.cells[0].innerText;}
When I need to do something like this, I like to work backward:
for(var i= rowCount - 1; i >= 0; i--) {
var row = table.rows[i];
var text = row.cells[0].innerText;
if(text.indexOf("STRING")!=-1){
table.deleteRow(i);
}
}
Just decrement i using i-- when you remove a row, so that your for loop re-examines the same index (which will now contain the next row).
EDIT: Having looked at your code again, you'll want to compare i to table.rows.length rather than your rowCount variable to account for the changing length of table.rows.

Categories