jQuery object info retrieval issue [duplicate] - javascript

So I'm working with the Handsontable jQuery plugin for a project at the moment, and I've written some custom functions to work with it.
The function I'm currently having trouble with is one I've written to return the currently selected cell(when the user has only selected one, not multiple, and yes that is checked for).
Here is my code:
function getCurrentCell(){
var selection = $('div.current');
var left = selection.offset().left;
var right = left + selection.width();
var top = selection.offset().top;
var bottom = top + selection.height();
$('div.active').find('table').find('td').each(function(){
if($(this).offset().left >= left && $(this).offset().left <= right && $(this).offset().top >= top && $(this).offset().top <= bottom){
return this;
}
});
return false;
}
However, whenever I call the function such as:
var cell = getCurrentCell();
And then attempt to alert(cell) or console.log(cell), I get a false return value.
My initial thought would be that somehow the coordinates would be off, and therefore no element would be found matching the criteria, so I attempted to check by adding...
$(this).css('background-color', 'black');
...right before the return this line. That way, if the right table cell is found, it will show up on screen before actually returning in code. Funny thing is, the correct cell always has its background color changed properly. So, this function is finding the correct cell, and it is executing the code within the if loop, but when I try and capture the return value into a variable, that variable is always false.
Any help would be great! Thanks SO!

You are using .each() with a function
.each(function(){
...
return this;
});
return false;
This will return from the callback (and maybe stop the each-loop if this was false), but never break out and return from the outer getCurrentCell function! So, that one will always return false.
Quick fix:
var result = false;
<...>.each(function(){
if (<condition>) {
result = <found element>;
return false; // break each-loop
}
});
return result; // still false if nothing found

Currently there is a better way to get currently selected in Handsontable. Just use the following methods from Handsontable:
handsontable('getSelected') - Returns index of the currently selected cells
as an array [topLeftRow, topLeftCol, bottomRightRow, bottomRightCol]
handsontable('getCell', row, col) - Return element for given row,col
All methods are described here: https://github.com/warpech/jquery-handsontable

Related

console.log of undefined doesn't log

I am trying to fetch data from www.matchbook.com using this script
function scrape(){
var row = document.querySelectorAll(".mb-runner")[0]; //get rows
console.log(row.textContent); //log row content
}setInterval(scrape, 1000);
This code produces inline data ArsenalArsenal1.347$2591.349$20,8171.352$4811.363$11,2331.368$11,4571.379$9,948 without delimiter. Do you know how should I separate this data?
Here I am posting screen how data looks on a web
When the contents of that cell changes to MAKE OFFER, it no longer has the mb-price__odds class, so the selector matches the next cell.
Instead of selecting by this cell's class, use the class of the container, which I don't think goes away.
Then check whether this matched anything before trying to access .textContents.
function scrape() {
var cell = document.querySelector(".mb-price:first-of-type .mb-price__odds");
if (!cell) {
console.log("Log me null please, do not skip to another cell !");
} else {
console.log(cell.textContents);
}
}
Finally I solved my problem via this javascript code:
function scrape(){
var radek = document.querySelectorAll(".mb-runner")[2];//row
var bunka = radek.querySelectorAll(".mb-price")[0]; //cell
console.log(bunka.textContent); //cell value
}setInterval(scrape, 1000); //run every second
Try this.
No need to querySelectorAll if you only need the first element
Find the parent first and look for the price only if the element exists
Put null check on the element && the text content
function scrape(){
var x = document.querySelector(".mb-price");
var y = x && x.querySelector('.mb-price__odds');
if (y && y.textContent){
console.log(y.textContent);
}
else {
console.log('No Content');
}
}

Recursive function (maze solver) - can't find a bug;(((

I am studying javascript and all is pretty easy to me except for some things like recursive functions. I do understand the way they work but while working with an example, I realized I can't capture the bug that prevents it from functioning...
I have an array (map) below (0 is a closed cell, 1 means path is open) and the recursive function I am trying to use to "find" path out of this "maze" by going from its top left cell to the bottom-right one.. Basically just make the function to "find" this path of 1s. But it fails;(
var map = [
[1,1,0,0],
[0,1,1,0],
[0,0,1,0],
[0,0,1,1]
]
function findpath(x,y) {
if (x<0 || x>3 || y<0 || y>3) return false; //if it is outside of map
if (x==3 && y==3) return true; // if it is the goal (exit point)
if (map[y][x]==0) return false; //it is not open
map[y][x]=9; //here marking x,y position as part of solution path outlined by "9"
if (findpath(x,y-1) == true) return true;
if (findpath(x+1,y) == true) return true;
if (findpath(x,y+1) == true) return true;
if (findpath(x-1,y) == true) return true;
map[y][x]=8; //unmark x,y as part of solution path outlined by "8"
return false;
};
findpath(0,0);
The description of "it fails" is rarely, if ever, a useful error report.
In order for someone to help you, they need much more detail than that.
In this case, the import details came out of the JavaScript error console. You should always include any error messages in your question.
However, since your code was quite short I was able to cut-and-paste it into my console where I got the message:
RangeError: Maximum call stack size exceeded
This means that your function is recursing too deeply. You either
Have bad logic in your puzzle and you are recursing into the same values over and over again
The puzzle is too complicated and you can't solve it recursively like that.
You need to add console.log statements and observe what the code is doing and see why it is going so deep.
If it is a logic error, fix the logic error. (Hint: I'm pretty sure it is -- you never mark on the map where you've been so it freely goes back and forth and back and forth over the same spot).
If it isn't, then you need to use some more advanced trick to work around the recursion, such as using a generator function and storing the changes you do in the map separately.
Quick answer:
Its locking in a loop because the order of the checks.
Start from 0:0 then try 0:1. Then from 0:1 --"Ummm... 0:0 looks promising. Let's go there". So go back to 0:0... so it locks...
Try leaving backtracking last :
if(findpath(x+1,y)) return true;
if(findpath(x,y+1)) return true;
if(findpath(x,y-1)) return true;
if(findpath(x-1,y)) return true;
This get you out of the lock just by swapping the issue around. If you start from 3:3 trying to reach 0:0 you'll be locked again.
Whats missing its a way to mark visited squares.
I think you are trying to implement an a* algorithm
UPDATE:
Here is your idea working. Just added the backtracking checks you almost implemented.
<html>
<head>
</head>
<body>
<script>
var map = [
[1,1,0,0],
[0,1,1,0],
[1,1,1,0],
[1,0,1,1]
]
var goalx = 0;
var goaly = 3;
console.log();
function findpath(x,y) {
// illegal move check
if (x < 0 || x > (map[0].length -1) || y < 0 || y > (map.length - 1)) return false; //if it is outside of map
if (map[y][x]==0) return false; //it is not open
// end move check
if (x== goalx && y== goaly){
console.log('Reached goal at: ' + x + ':' + y);
return true; // if it is the goal (exit point)
}
if(map[y][x] == 9 || map[y][x] == 8)
return false;
console.log('Im here at: ' + x + ':' + y);
map[y][x]=9; //here marking x,y position as part of solution path outlined by "9"
if(findpath(x+1,y))
return true;
if(findpath(x,y+1))
return true;
if(findpath(x,y-1))
return true;
if(findpath(x-1,y))
return true;
map[y][x]=8; //unmark x,y as part of solution path outlined by "8"
return false;
};
findpath(3, 3);
</script>
</body>
</html>

jquery each loop only looping once and if using else code stops

I've got two problems with the following javascript and jquery code.
The jquery each loop only iterates once, it gets the first element with the right ID does what it needs to do and stops.
The second problems is that when I use the else in the code the one inside the each function, it doesn't even tries the next if, it just exits there.
I'm probably doing something fundamental wrong, but from the jquery each function and what I'd expect from an else, I don't see it.
Javascript code:
var $checkVal;
var $checkFailed;
$("#compliance").live("keypress", function (e) {
if (e.which == 10 || e.which == 13) {
var checkID = $(this).parents('td').next().attr('id');
var checkVal = $(this).val();
$('#' + checkID).each(function () {
var cellVal = $(this).text();
if (checkVal == cellVal) {
$(this).removeClass("compFail").addClass("compOk");
} else {
$(this).removeClass("compOk").addClass("compFail");
var checkFailed = True;
}
});
if (checkFailed == 'True') {
(this).addClass("compFail");
} else {
(this).addClass("compOk");
}
}
});
How could I get the each loop to iterate through all instances of each element with the id assigned to the variable checkID, and get the code to continue after the else, so it can do the last if?
An id should appear on a page only once. If you want to have multiple elements with same id, then use a class, not an id.
Your each loop iter only once because you are selecting by id thus you are selecting only one element in the page. If you change you elements to a class it should work like you expect.
This is to illustrate what I'm talking about in my comment, so that you do not remove the wrong var:
var checkVal;
var checkFailed;
$("#compliance").live("keypress", function (e) {
if (e.which == 10 || e.which == 13) {
var checkID = $(this).parents('td').next().attr('id');
//HERE is the first edit
checkVal = $(this).val();
$('#' + checkID).each(function () {
var cellVal = $(this).text();
if (checkVal == cellVal) {
$(this).removeClass("compFail").addClass("compOk");
} else {
$(this).removeClass("compOk").addClass("compFail");
//HERE is the second
checkFailed = True;
}
});
if (checkFailed == 'True') {
(this).addClass("compFail");
} else {
(this).addClass("compOk");
}
}
});
Normally, the way you have it would cause a compile-time error (in a typed language like C#) for redeclaring a variable. Here, it's not clear to me if it will be used as a local variable (ignoring your global variable) or if javascript will combine them and consider them the same. Either way, you should use it as I have shown so that your intent is more clear.
EDIT: I have removed the $ from your variables (var $checkVal) as on jsFiddle it was causing issues. SO if you do not need those $'s, then remove them. Also, note that testing on jsFiddle indicates that you do not need to change your code (other than possibly removing the $ from your declaration) as javascript appears to consider them the same variable, despite the redeclaration, which I find a bit suprising tbh.
The jquery each loop only iterates once, it gets the first element
with the right ID does what it needs to do and stops.
Yes, this is absolutely right for the code you're using:
$('#' + checkID).each(function(){};)
ID attributes are unique. There must be only one element with a given ID in the DOM. Your selector can match only one element. You are iterating over a collection containing just 1 item.

cannot get a variable to pass from function called onclick

I am calling a function onclick secVar(sec1); which should run it through the script below, but it does not seem to be doing so, can someone tell me what I am doing incorrectly. I am new to javascript, and only have a little experience into scripting, and this code seems to be doing less and less of what I want it to.
<script type="text/javascript">
var sec1=0;
var sec2=0;
var sec3=0;
function secVar(){
if(sec1) {
sec1++;
document.getElementById('sec1text').innerHTML = sec1;
}
if(sec2) {
sec2++;
document.getElementById('sec2text').innerHTML = sec2;
}
if(sec3) {
sec3++;
document.getElementById('sec3text').innerHTML = sec3;
}
}
function largestVar(){
if (sec1 >= sec2 && sec1 >= sec3) {
//a
document.getElementById('rig').innerHTML = 'Test1';
} else if (sec2 >= sec1 && sec2 >= sec3) {
//b
document.getElementById('rig').innerHTML = 'Test2';
} else {
//c
document.getElementById('rig').innerHTML = 'Test3';
}
}
</script>
If this helps, The old code was the code below, before I tried to add in the script to determine the largest of the variables. It was incrementing the variables onclick, but no longer so. The onclick contained sec1Var() at that point.
<script type="text/javascript">
var sec1=0;
var sec2=0;
var sec3=0;
function sec1Var(){
sec1++;
document.getElementById('sec1text').innerHTML = sec1;
}
function sec2Var(){
sec2++;
document.getElementById('sec2text').innerHTML = sec2;
}
function sec3Var(){
sec3++;
document.getElementById('sec3text').innerHTML = sec3;
}</script>
If someone can explain to me what I am doing wrong I would greatly appreciate it.
I think it's hard to tell what your intention is. Sparticus has it right IF what you're trying to do is see if sec1, 2, and 3 are currently true or false (0 or 1). Since they are currently false, the code will never do anything as Sparticus correctly points out.
However, I'm not convinced that's actually what you MEAN to do. It looks like the condition you want to check is whether or not you're trying to increment sec1, 2, or 3. In other words, "If you are passing me sec1, increment it and update a piece of HTML".
But variables don't work that way. When you say secVar(sec1) what you are actually saying is `secVar(0)'. I don't think that's your intention.
So, a big waste of my time if I'm wrong, but because I'm already rolling along, let's pretend I'm right:
secVar needs to be able to accept a parameter, but right now you've declared it void. Changing it to accept a parameter is a first step:
function secVar(param) { ... };
But this still won't do anything. Because when you're still passing it "0" with your existing syntax. You need to pass it something that can be checked, like a string:
secVar('sec1');
When you do this, you can now update your conditions to check which string is being passed
if (param === 'sec1') { ... }
Here's a fiddle: http://jsfiddle.net/ch4yk/
Notes:
The fiddle includes jQuery just for easy brute-force event binding on the buttons. It's just an example. You don't need jQuery; bind your events however you want.
It is currently not doing anything with the largest value function, even though the code is in the fiddle
None of your counters will increment in this implementation.
When secVar() gets run, all the counters are at zero. They never increment because they start at zero.

JavaScript: Do I need a recursive function to solve this problem?

In this fiddle http://jsfiddle.net/5L8Q8/28/, if you click the black button, it randomly selects one of two values (red or blue) from an array. The randomly selected value is assigned to ran. In my real life application, there will be 16 elements in that array.
If you the pink "playagain" button, it chooses a random element from the same array but I want to make sure it's not the same one chosen as last time.
Therefore, when I click playagain, I assign ran to lastran and compare it to the next randomly chosen value from the array and, if they are the same, choose randomly again. However, the way I have it isn't guaranteeing that (upon the completion of playagain) ran is different.
I think I need a recursive function where comment 2 is in the code below, but I keep breaking my code when I try to create it.
Can you comment on the 3 comments in the code below?
Note, I'm a relative newbie, so this code is probably awful...
$("#playagain").click(function(){
lastran = ran;
ran = getRandom(myArray, true);
if (ran === lastran) {
ran = getRandom(myArray, true); //1. do I need to return this?
//2. want to test ran === lastran again.. How to set up recursive function?
} else {
return; //3.is this the right thing to do here?
}
});
while( (ran = getRandom(myArray, true)) === lastran)
;
Is what you want. The statement
ran = getRandom(myArray, true)
does not only set ran to getRandom(), but returns the value of ran. (This is a fairly common idiom in JavaScript, a carry over from C.)
So your full code can be:
$("#playagain").click(function(){
/*var */lastran = ran;
while( (ran = getRandom(myArray, true)) === lastran)
;
// update UI here
});
You can use a while loop instead of the if.
while(ran == lastran)
{
ran = getRandom(myArray, true);
}
It'll keep trying until it gets a different value.
After each run, simply remove that "key" from array and push lastran to the end of it. Then the updated getRandom function as following could be used both for #button and #playagain. http://jsfiddle.net/ghostoy/5L8Q8/32/
function getRandom(array, getVal) {
var key = Math.floor(Math.random() * array.length),
value = array[key];
if (lastran) {
array.push(lastran);
}
array.splice(key, 1);
lastran = value;
if (getVal) {
return value;
}
return key;
}
I think your approach is not the best way to deal with this. In theory you could get the same number many times in a row making this a 'slow' algorythm and you are making it more complex than needed.
An alternative approach in text:
- if no previous element has been picked pick a number between 0 and the number of elements in your array (16) otherwise pick a number between 0 and #elements-1 (15)
- if the chosen element is greater or equal to the last element picked add 1 to it
- store this index number as the last picked element
- return the array[picked-element]'s value
You could make getRandom itself recursive:
function getRandom(array, getVal, lastRan) {
var key = Math.floor(Math.random() * array.length);
if ((!getVal && key == lastRan) || (getVal && array[key] == lastRan))
return getRandom(array, getVal, lastRan);
return getVal ? array[key] : key;
}
Call it passing the last random value:
getRandom(myArray, true, lastran)
It works like this. You always pass getRandom the last random value that was retrieved. In the first conditional, we check to see if we just generated a duplicate of this value (either using the key itself or its corresponding value in the array, depending on whether getVal is true). If so, we return the result of calling getRandom again, once again passing the last random number that was used. This can happen as many times as necessary.
When one of these calls to getRandom produces a new number, then the expression in the first conditional will be false. In this case, we return the wanted value (via the second return statement) and all of the recursive calls to getRandom are "unrolled". (Remember, we returned the value of each call to getRandom at each step.)

Categories