I'm working on creating a toggle function to 'favorite' an item from a list of many. I've got working script to toggle the item in and out of a user-specific favorites list, communicate that change to a database, and populate the rest of the site accordingly. That all works fine, it's mostly PHP and Ajax.
However, my javascript is ass. I'm stuck on a conditional to change the icon from a filled heart to an empty one. For some reason it never reaches the else statement even when the if statement is false. If I reverse the conditions, it still handles the if fine but never the else.
the image is:
<img src="includes/icons/fave-<?php echo $favStatus; ?>.png" id="faveToggle" class="faveIcon" onClick="toggleFave()">
the conditional, located in toggleFave() is:
if(document.getElementById('faveToggle').src.toString().indexOf("fave-false.png")){
document.getElementById('faveToggle').src = "includes/icons/fave-true.png";
} else {
document.getElementById('faveToggle').src = "includes/icons/fave-false.png";
}
So, uhh, whuddo I do?
indexOf returns the 0-based index of where the substring is found, or -1 if not. -1 happens to be "truthy".
That means you have two possibilities, it's either in the string and has a positive (truthy) position, or it's not and you get a truthy -1. Either way, it will always go into the first block. You want:
if(document.getElementById('faveToggle').src.toString().indexOf("fave-false.png") > 0){
You only need to fetch the element once:
var toggle = document.getElementById("faveToggle");
if (toggle.src.indexOf("fave-false.png") >= 0) {
toggle.src = "includes/icons/fave-true.png";
}
else {
toggle.src = "includes/icons/fave-false.png";
}
The .indexOf() function returns the position of the searched-for substring, or -1 if it isn't found.
You can checkif it is greater than -1
if(document.getElementById('faveToggle').src.toString().indexOf("fave-false.png")>-1){
alert('1')
document.getElementById('faveToggle').src = "includes/icons/fave-true.png";
} else {
alert("2")
document.getElementById('faveToggle').src = "includes/icons/fave-false.png";
}
jsfiddle
Here's my simplified version using a ternary operator:
var toggle = document.getElementById('faveToggle'),
newState = toggle.src.toString().indexOf("fave-false.png") == -1 ? true : false;
toggle.src = "includes/icons/fave-"+newState+".png";
You can use the bitwise not ~ operator for checking.
~ is a bitwise not operator. It is perfect for use with indexOf(), because indexOf returns if found the index 0 ... n and if not -1:
value ~value boolean
-1 => 0 => false
0 => -1 => true
1 => -2 => true
2 => -3 => true
and so on
if(~document.getElementById('faveToggle').src.toString().indexOf("fave-false.png")){
Related
I am trying to compare a string with a set of strings stored in an array. Here is the block of code:
then(op => {
if (op[0].probability > FILTER_THRESHOLD) {
if (FILTER_LIST.indexOf(op[0].className) > 1) {
console.log("EUREKA! EUREKA! EUREKA!")
console.log(op[0].className)
return true;
}
}
return false;
})
The second if statement should evaluate to true in some cases but it is not. The return is always false.
op[0].className should be a string and I am also able to get the value from op[0].probability correctly.
What could be the reason?
I have tried debugging and cannot seem to get why the 'if' statement is not being true.
Here is the FILTER_LIST array:
var FILTER_LIST = ["Hello", "Please", "Simple"];
Please advise how I can fix this!
Thank you!
indexOf(...) > 1 asks "did it find a match at the third element or later?" You'll get false if it matched at index 0 or 1. If you want just "it found one anywhere", you want !== -1, >= 0, or to use includes instead of indexOf.
if (FILTER_LIST.indexOf(op[0].className) !== -1) {
// or
if (FILTER_LIST.indexOf(op[0].className) >= 0) {
// or
if (FILTER_LIST.includes(op[0].className)) {
I am trying to write a Javascript function that returns a single element given the name. I found this question and modified the answer to use the ternary operator.
function getField(fieldName)
{
var elements = document.getElementsByName(fieldName);
return elements.length && elements.legth > 0 ? elements[0] : null;
}
My question is about the case where document.getElementsByName(fieldName) doesn't find any matches. Does it return undefined or 0? When I output elements.length as an alert message, the value in the alert is 0 but the console Chrome's DevTools says undefined. When I call console.log(elements.length) from the console, it ouputs 0 and undefined.
I know that my function handles either case, but what am I missing here? What is Javascript actually doing?
Thanks in advance for helping me understand this.
EDIT: Sorry for posting a picture instead of actual code and thanks for the syntax clarification.
As your question seems to be what document.getElementsByName is returning when it isn't found, it would be an empty NodeList, with a length of 0 (so not undefined)
Therefor, the easiest would be as dandavis suggested in his comment to simply return the first element of the nodelist. If it is empty, it will be undefined, if not it would the first element (not sure if that always matches your case though)
so your function might as well be
function getFieldAt(fieldName, index = 0) {
return document.getElementsByName(fieldName)[index];
}
if you don't use optional parameters, you could change it to
function getFieldAt(fieldName, index) {
return document.getElementsByName(fieldName)[index || 0];
}
Your misunderstanding about the devtools are thoroughly explained in the comments and the other answer as well :)
elements.length is equal to 0 in your case.
Your understanding of console.log is misleading:
console.log(elements.length);
> elements.length is printed. It evaluates to 0, so 0 is printed
> console.log(elements.length). It evaluates to undefined, so undefined is printed.
no need to test length value simply use:
function getField(fieldName)
{
let elements = document.getElementsByName(fieldName);
return (elements[0]) ? elements[0] : null;
}
console.log( getField('div-A') );
console.log( getField('neverExist') );
<div name="div-A"> div-A 1 </div>
<div name="div-A"> div-A 2 </div>
<div name="div-A"> div-A 3 </div>
This question already has answers here:
indexOf always returning true for document.location
(3 answers)
Closed 4 years ago.
I have the following javascript function to check a link and based on the link being a track or album, it should extract the id from the link, but it'll never go pass the first if statement, it will always alert found track in link, even though I provide a album link, any help would be appreciated.
the links look like the following :
https://open.spotify.com/album/1XGo0OD90wIlRccwLe29L9?si=egthfx9CRfuPIhLJ1uXCPA
https://open.spotify.com/track/1SWyGZhn3nyLUZRfWvQ0to?si=Tl-fwsqxQf-J8gZWps2PqQ
function getLinkID(link) {
if(link.indexOf("spotify")) {
if(link.indexOf("track")) {
alert("found track in link");
var linkID = link.split('track/').pop().split('?')[0];
} else if(link.indexOf("album")) {
alert("found album in link");
var linkID = link.split('album/').pop().split('?')[0];
}
} else if(link.indexOf("apple")) {
}
return linkID;
}
indexOf returns the index number of the match and if there is no match it return -1. Also, if there is a match at first index, then it will return 0 which is false which will break your code. You should use includes.
Update from
link.indexOf("spotify")
to
link.includes("spotify")
Note, includes will not work if you are using IE. For IE either you will need to create a polyfill or update the condition to link.indexOf("spotify") !== -1
function always return the first element
The problem here is that the first condition will be always true, only if the index is 0, even if the word doesn't appear in the link, because link.indexOf("spotify") will return an index or -1.
So when the word isn't not found it will return -1 which is a truthy value, that's why the condition will be true, you can confirm this by typing Boolean(-1) in the console.
You need to check that the returned index is higher than -1, so the condition can be evaluated correctly:
if(link.indexOf("spotify")>-1)
IndexOf returns -1 if it’s not found and -1 is a truth value. Therefore it will always return true.
Try this :)
function getLinkID(link) {
if (link.indexOf("spotify") > -1) {
if (link.indexOf("track") > -1) {
alert("found track in link");
var linkID = link.split('track/').pop().split('?')[0];
} else if(link.indexOf("album") > -1) {
alert("found album in link");
var linkID = link.split('album/').pop().split('?')[0];
}
} else if(link.indexOf("apple") > -1) {
}
return linkID;
}
if($('#this').val().indexOf('4289')){
Do something
else
Do something.
This works only with that 4289,
When I try to add other numbers to be indexed next to it using 'or', it doesn't work. How should I put other number. E.g
IndexOf('4289||78843')
I want this to check this numbers and if the number in the input field is not one of this, to echo error.
Here's more which happens to die when one revisits the field.
$('#Zip').blur(function(){
if (($(this).val().indexOf('0860') > -1)||($(this).val().indexOf('0850') > -1)){
$('#Status_Zip').html("No way.")
$(this).alterClass('*_*', 'Success')
return false;
}else{$('#Status_Code').hide()
$(this).alterClass('*_*', 'Error')
$(this).css('border-color', '#F00').css('background-color', '#FFC').effect("pulsate",{times:4},2)
return true;
}
})
That's because it would be looking for the string '4289||78843', which doesn't exist in the target I'm assuming. Logical operators can't just be tossed in anywhere, only where there are actual values to logically operate on. Something like this:
if(($('#this').val().indexOf('4289') > -1) ||
($('#this').val().indexOf('78843') > -1))
The return value of the indexOf() function is the numeric index of that value in the target value, or -1 if it's not found. So for each value that you're looking for, you'd want to check if it's index is > -1 (which means it's found in the string). Take that whole condition and || it with another condition, and that's a logical operation.
Edit: Regarding your comment, if you want to abstract this into something a little cleaner and more generic you might extract it into its own function which iterates over a collection of strings and returns true if any of them are in the target string. Maybe something like this:
function isAnyValueIn(target, values) {
for (var i = 0; i < values.length; i++) {
if (target.indexOf(values[i]) > -1) {
return true;
}
}
return false;
}
There may even be a more elegant way to do that with .forEach() on the array, but this at least demonstrates the idea. Then elsewhere in the code you'd build the array of values and call the function:
var values = ['4289', '78843'];
var target = $('#this').val();
if (isAnyValueIn(target, values)) {
// At least one value is in the target string
}
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.)