append() and $(this).addClass() method in jQuery is not working - javascript

I am new to jQuery and I am refactoring former JavaScript code into jQuery. I use the append() and $(this).addClass() methods, but it seems they don't work. I don't know what the problem is.
The JavaScript code is about creating a puzzle game (15 puzzles). I am trying to add an element into an HTML file in the jQuery way.
var tile = function(i, j) {
this.seq = i * 4 + j + 1;
this.row = i + 1;
this.column = j + 1;
if (i * 4 + j != 15) {
$(this).addClass("block puzzle row" + this.row + " column" + this.column);
var xPosition = -j * 88;
var yPosition = -i * 88;
$(this).css("backgroundPosition", xPosition + "px " + yPosition + "px");
} else {
$(this).addClass("block row" + this.row + " column" + this.column);
$(this).attr('id', "blank");
}
}
function Init() {
var node = $("#imgContent"); // imgContent is a div
for (var i = 0; i < 4; i++) {
for (var j = 0; j < 4; j++) {
var t = new tile(i, j);
node.append(t);
}
}
// Generate the original picture before the start
Judge.isStart = false;
}
How do I use these jQuery methods properly?

You're using $(this) incorrectly.
When the tile() function is called with new, it is instantiated as an object and 'this' becomes a reference to that object. In doing so, you can assign values to its internal state using 'this.' syntax.
jQuery is primarily designed to manipulate the Document Object Model (DOM). Usually you'll pass a selector into jQuery which finds the matching HTMLElements from the document and returns them for manipulation. $(this) within the context of tile is passing the this reference which jQuery just returns back to you.
Important part here is that $(this) represents the tile object and not actually an element from the document. The .css .addClass and .append functions aren't applicable, as they work on elements. You need to pass a selector or an element into jQuery to use them.
A solution is to create an element within tile that can be appended to the document.
https://jsfiddle.net/hxqduoef/2/
var tile = function(i, j) {
this.seq = i * 4 + j + 1;
this.row = i + 1;
this.column = j + 1;
// $("<div></div>") creates a new div element wrapped in the jQuery object, it isn't part of the document until it gets appended. saving to this allows further manipulations within this constructor.
this.element = $("<div>Tile at "+ i +" "+ j +"</div>");
if (i * 4 + j != 15) {
this.element.addClass("block puzzle row" + this.row + " column" + this.column);
var xPosition = -j * 88;
var yPosition = -i * 88;
this.element.css("backgroundPosition", xPosition + "px " + yPosition + "px");
} else {
this.element.addClass("block row" + this.row + " column" + this.column);
this.element.attr('id', "blank");
}
}
function Init() {
var node = $("#imgContent"); // imgContent is a div
for (var i = 0; i < 4; i++) {
for (var j = 0; j < 4; j++) {
var t = new tile(i, j);
node.append(t.element);
}
}
// Generate the original picture before the start
// Judge.isStart = false;
}
This adds the "this.element" property, sets it as a new DIV element and references it instead of the t in the node.append(t.element) statement.
I hope this helps.

context of this is getting changed, keep this to a variable, then use that variable.
var $this = this;
var tile = function(i, j) {
$this.// your code
}

$(this) is actually defined by the invoker of the function, while explicit variables remain intact inside the function declaration block known as the enclosure.
What's happening in your function is tile(i,j), is being called explicitly, this means context or the "this" of the function is the window object.Being specific to your question, this is bound to the global object, that is window.
For example, consider the following function,
function myFunc(){
}
and,
var myObj= { myFunc: myFunc};
If you call using myObj.myFunc(); then this is bound to myObj.
If you call myFunc() directly, such as, myFunc();,
then this is bound to the global object, that is window.
Hence, calling a function without a following parent object will generally get you the global object which in most browsers means the window object.

Related

How to update all child IDs using plain JavaScript

I have a JavaScript function called resetIndex. It works fine but I want to reset all child IDs. How can I do this? Is there any method like firstChild and lastChild?
I'm new with JavaScript. Can anyone help?
I have following function:
function resetIndex(delId) {
for (var i = delId + 1; i < count; i++) {
var currentElement = document.getElementById(i);
currentElement.id = i - 1;
var update = currentElement.childNodes;
update.setAttribute('id', 'deleteLink(' + currentElement.id + ')');
}
count--;
}
You can use
node.children[0]
to get the first one, and
node.children[node.children.length - 1]
to get the last one.
Make sure to check if they exist, first.
To do something to all child-nodes, you can use a for-loop, like
for(let a = 0; a < node.children.length; a++) {
node.children[a].id = "my-new-id";
}

Trying to call a hasChildNodes method but getting cannot read property of Null

I'm attempting to make a simple turn-based two player game (like FE) as practice for an upcoming project. In trying to ensure that no characters overlap on the same grid tile, I tried to make a validator function that I can call for each character on generation (randomly generated locations), so that I wouldn't have the same script with minor changes in each character gen section. My original code (without the function) is above, the most recent attempt (with some context) is below:
function bTeamCharGen() {
var a = 10;
var b = 15;
var c = 0;
var d = 5;
var bTeamLead = document.createElement("img");
bTeamLead.src = "images/transp_img.gif";
bTeamLead.height = "38";
bTeamLead.width = "38";
bTeamLead.className = "lead";
bTeamLead.id = "bLead";
genStartPos();
var curr = document.getElementById("gridBlock_" + i + "_" + j);
if (curr.hasChildNodes()) {
while (curr.hasChildNodes()) {
genStartPos(a, b, c, d);
}
}
document.getElementById("gridBlock_" + i + "_" + j).appendChild(bTeamLead);
document.getElementById("bLead").style.background = "url('images/eirika_1_1.gif') 0 0";
document.getElementById("bLead").style.backgroundRepeat = "no-repeat";
//commented out basic structure for further characters here
}
This is the original code for one of the characters. The following is what I've currently arrived at (and still with the Uncaught TypeError):
var i, j;
//functions to generate grid and background
function genStartPos(minX, maxX, minY, maxY) {
i = Math.floor(Math.random() * (maxX - minX + 1) + minX);
j = Math.floor(Math.random() * (maxY - minY + 1) + minY);
}
function validStartPos() {
genStartPos();
var curr = document.getElementById("gridBlock_" + i + "_" + j);
if (curr.hasChildNodes()) {
while (curr.hasChildNodes()) {
genStartPos(a, b, c, d);
}
}
}
function bTeamCharGen() {
var a = 10;
var b = 15;
var c = 0;
var d = 5;
var bTeamLead = document.createElement("img");
bTeamLead.src = "images/transp_img.gif";
bTeamLead.height = "38";
bTeamLead.width = "38";
bTeamLead.className = "lead";
bTeamLead.id = "bLead";
validStartPos();
document.getElementById("gridBlock_" + i + "_" + j).appendChild(bTeamLead);
document.getElementById("bLead").style.background = "url('images/eirika_1_1.gif') 0 0";
document.getElementById("bLead").style.backgroundRepeat = "no-repeat";
//commented out basic structure for further characters here
}
function initialise() {
makeGrid();
setBackground();
bTeamCharGen();
rTeamCharGen();
}
It's a bit long and unwieldy, as I'm still trying to neaten it up quite a bit, but everything else worked until I did this. I'm calling the initialise function as an onload for the body, and the <script> is within the <head>, since I had problems earlier on when having it within <body>.
I'm not sure, but... I think you trying get element when "gridBlock_" + i + "_" + j is undefined
You calling genStartPos() without any parameter. So your i and j are undefined, so you string with element id.
If you cant pass variables to genStartPos(), you need to make some default value for maxX, maxY...
PS you also calling genStartPos(a, b, c, d) when a, b, c, d are undefined. Or maybe this just not all code you show

Append one dynamically-created element to another

I have the following prototype-based code:
for (j = 0; j < Math.floor(len / 2); ++j) {
var tr = $('tr');
var id1 = id[1] + (j * 2);
var id2 = id[1] + (j * 2 + 1);
// need to change the bits below here I believe
tr.appendChild(id1.createCheckbox());
tr.appendChild(id1.createButton());
tr.appendChild(id2.createCheckbox());
tr.appendChild(id2.createButton());
t[i].appendChild(tr);
}
Which is prototype-based code to append the result of another function to my new <tr> element. The index [i] comes from a for-loop which envelopes this for-loop and is working fine.
My question is, how to convert this into jQuery? Basically I want to do the opposite of this case here.
Before anyone suggests just dynamically creating a new button element, the createButton() and createCheckbox() are essential functions to create and format special buttons and also work fine.
Cheers peeps
First of all check this code again. Now it manipulates with the same element (with identifier "tr") in each iteration of loop. That's strange.
Find methods createCheckbox() and createButton(), they may look like
Number.prototype.createCheckbox = function() {
return new Element('input', {type: 'checkbox', value: 'cb' + this});
}
and convert them to functions. After conversion they should accept number (or maybe string if id[] is array of strings) as an argument and return jQuery collection with created checkbox/button:
function createCheckbox(id) {
return $('<input type="checkbox" value="cb' + id + '">');
}
Convert your code to
for (j = 0; j < Math.floor(len / 2); ++j) {
var $tr = $('#tr'); // get element with id="tr"
var id1 = id[1] + (j * 2);
var id2 = id[1] + (j * 2 + 1);
$tr.append(createCheckbox(id1));
$tr.append(createButton(id1));
$tr.append(createCheckbox(id2));
$tr.append(createButton(id2));
$tr.appendTo(t[i]); // note that t[i] is element, not a jQuery collection
}
not sure exactly what you asking for, maybe this:
for (j = 0; j < Math.floor(len / 2); ++j) {
var tr = $('<tr></tr>');
var id1 = id[1] + (j * 2);
var id2 = id[1] + (j * 2 + 1);
// need to change the bits below here I believe
tr.append(id1.createCheckbox());
tr.append(id1.createButton());
tr.append(id2.createCheckbox());
tr.append(id2.createButton());
$(t[i]).append(tr);
}
Try document.createElement('tr'); instead. It should work with the rest of your code, which is using JavaScript DOM manipulation. If you'd rather switch to jQuery DOM manipulation, use MamaWalter's approach.

loop created element's value always 5

Can someone please tell me why when I click on the [s] href created next to the list of names (myhand) generated it always says selection and i are 5?
var printDD = function(myhand, mydiv){
var dtext = "";
for(var i = 0; i < myhand.length; i++){
dtext += '[s]' + myhand[i] + ', ';
}
mydiv.html(dtext);
for(var i = 0; i < myhand.length; i++){
$('#dd'+i).click(function(){
selection = i;
console.log("sel: " + selection + " i: " + i);
});
}
}
You want to take a look at JavaScript closure inside loops – simple practical example. As the answer to that question says, you can create a function to return one, or you can use inline function invocation in the for loop like so:
for(var i = 0; i < myhand.length; i++) {
$('#dd'+i).click((function(x) {
return function () {
selection = x;
console.log("sel: " + selection + " x: " + x);
}
}(i)));
}
Because the value of i is determined at the time the click handler is run. So it will always have the value of myhand.length - 1, which is the state you left i in after the for-loop.

Programmatically setting the name of a variable

Is there a shortcut for writing the following 100 assignments?
variable_1 = 1;
variable_2 = 2;
variable_3 = 3;
...
variable_100 = 100;
I have tried
for(var i = 1; i <= 100; i++) {
variable_ + i = i;
}
but I get the error message "Invalid left-hand side in assignment". Any ideas?
Here are a few methods:
Method 1: use eval
Here is the most direct method:
for(var i = 1; i <= 100; i++) {
eval("var variable_" + i + " = " + i);
}
variable_1; // => 1
Disclaimer for the above method: I don't think this problem is a good candidate for using eval. If you do use eval, you should never allow user input to go into what you are evaling, or you could open your site to security risks. That mistake is the main reason people say eval is evil.
Method 2: use dynamically generated object properties
This is a much, much better way:
// If you want these variables to be global, then use `window` (if you're
// in a browser) instead of your own object.
var obj = {};
for(var i = 1; i <= 100; i++) {
obj["variable_" + i] = i;
}
obj.variable_1; // => 1
About the note in the comment about using window to create global variables: I would recommend against this, as it is a quick way to pollute your global scope and step on variables unwittingly.
Method 3: use an array
David suggested using an array. This is another great idea, and, depending on what you are trying to do, may be preferred:
var arr = [];
for(var i = 1; i <= 100; i++) {
arr.push(i);
}
arr[0]; // => 1
This will do it:
for(var i = 1; i <= 100; i++) {
eval("variable_" + i + " = " + i + ";");
}
eval is basically evil, but for such purpose it's OK to use it. (reference)
Live test case.
You are better off using an array
var variable = [];
for (var i=1; i <= 100; i++) {
variable[i] = i;
}
Later, you can access the values using variable[1], variable[2] etc.
If it is like that why not to define array of the objects
var a = new Array();
for(i=0;i<100;i+=)
a[i] = i;
Why not using an array instead like this?
<script language="javascript">
var arrayVar = new Array();
for (var i=0; i<100; i++) {
arrayVar["variable_" + i] = i;
}
</script>
Use an array:
var variable = [];
for(var i = 1; i <= 100; i++) {
variable[i] = i;
}
By way of analogy, you'd want to use an array instead of 100 variables for the same reason you'd want
<div class="variable"></div>
<div class="variable"></div>
<div class="variable"></div>
//and so on
instead of
<div id="variable_1"></div>
<div id="variable_2"></div>
<div id="variable_3"></div>
//and so on
<div id="variable_100"></div>
Invalid left-hand side in assignment
This error gets generated because variable_ + i is an expression. The interpreter thinks you are trying to add two variables instead of concatenating a variable name and a string. An expression cannot be on the left-hand side of an assignment operation.
for(var i = 1; i <= 100; i++) {
window["variable_" + i] = i;
}
alert( variable_50 );
alert( variable_34 );
Assuming you're on a browser you can do:
global[variable] = 'hello'
console.log(variable) -> hello

Categories

Resources