This is a piece of code from a simple Bookmarker App I made. I am kind of confused about something here.
Look at the 3rd line of the code. Isn't there supposed to be == instead of = after classname?
Because = is an assignment operator. What I need is true which == or === should give and it indeed does from the console.log.
However when I use === inside the if statement the function no longer works. But it works with the = which is not making any sense to me. It would be great if someone could clarify what's the problem here.
If anyone would like to check the full code including the HTML and CSS, here it is: https://github.com/magnetickode/Bookmarking-App
document.querySelector('.bookmarks').addEventListener('click', deleteBookmark);
function deleteBookmark(e) {
if (e.target.className = 'delete') {
e.target.parentNode.parentNode.removeChild(e.target.parentNode);
console.log(e.target.className === 'delete');
//console.log(e.target.parentNode.getElementsByTagName('p')[0].textContent);
for (let i = 0; i < bookmarks.length; i++) {
if (bookmarks[i].name === e.target.parentNode.getElementsByTagName('p')[0].textContent) {
bookmarks.splice(i, 1);
break;
}
}
}
}
change this:
if (e.target.className = 'delete') {
to
if (e.target.classList.contains('delete')) {
Inside if statement we should use anything, which converts to boolean, so, as you mentioned, simple assignment doesn't make sense, because it always returns true. e.target.className contains all classes of the element, so you can't just do e.target.className == 'delete' or e.target.className === 'delete' if there is more than one class, because strings will not be equal ("q w e" includes "q", but "q" isn't equal to "q w e"). I see, you're using ES6, so you can do e.target.className.includes('delete'), it checks, does the element contain class delete, (for more info about includes method for strings: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes). It is also possible to use String.indexOf like element.className.indexOf('class') !== -1 to test for the existence of a class (or === -1 to test for it's absence).
And here is the example:
const bookmarks = document.querySelectorAll('.bookmarks');
for (let i = 0; i < bookmarks.length; i++) {
bookmarks[i].addEventListener('click', deleteBookmark);
}
function deleteBookmark(e) {
alert("Target contains 'delete' class? " + e.target.className.includes('delete').toString().toUpperCase());
/*if (e.target.className.includes('delete')) {
e.target.parentNode.parentNode.removeChild(e.target.parentNode);
for (let i = 0; i < bookmarks.length; i++) {
if (bookmarks[i].name ===e.target.parentNode.getElementsByTagName('p')[0].textContent) {
bookmarks.splice(i, 1);
break;
}
}
}*/
}
<div class="bookmarks delete">CLICK (contains "delete" class)</div>
<div class="bookmarks">CLICK (doesn't contain "delete" class)</div>
Related
I have came across an odd problem with getting JSON data like the following.
[
{
"type":"ripe",
"red":137,
"green":68,
"blue":40,
"strftime(\"%H:%M:%S\", time)":"18:46:37"
},
]
I was not able to compare this data by type using JavaScript, they both successfully went through my if statement for some odd reason. The total count for both variables is equal to 2.
let counterLoop = function() {
for (let i = 0; i < data.length; i++) {
let fruitType = JSON.stringify(data[i].type);
sortFruit(fruitType.toLowerCase());
}
}
let sortFruit = function(fruitType) {
if (fruitType.localeCompare('ripe') === 0){} {
totalRipeFruit++;
$("#totalRipeFruit").text(totalRipeFruit);
}
if (fruitType.localeCompare('unripe') === 0){} {
totalUnripeFruit++;
$("#totalUnripeFruit").text(totalUnripeFruit);
}
}
Any idea why this could be the case?
Thank you very much!
You have two problems here; First of all there is no need for the JSON.stringifyon the type, just leave it out as it will return the string containing useless quotes.
Secondly, your if statements are messed up: You have a second pair of brackets behind each one, so simply change
if (fruitType.localeCompare('ripe') === 0){} {
totalRipeFruit++;
$("#totalRipeFruit").text(totalRipeFruit);
}
if (fruitType.localeCompare('raw') === 0){} {
totalRawFruit++;
$("#totalRawFruit").text(totalRawFruit);
}
To:
if (fruitType.localeCompare('ripe') === 0) {
totalRipeFruit++;
}
if (fruitType.localeCompare('raw') === 0) {
totalRawFruit++;
}
I have a loop that checks the content of an element's data-attribute and then uses this content to match it in a switch function. I want to simplify this code because the data-attribute value may vary in the next future and every time I have to add a case to the switch.
What I have now (this works fine):
var a = $('#el').attr('data-x'); // String
for (var i = 0; i <= 2; i++) {
switch (a) {
case "abc":
abc++; // variable initially set to 0
$('#abc').text(abc); // Overwrite the content with the new value
break;
case "dfg":
dfg++;
$('#dfg').text(dfg);
break;
[ ...Many cases like the ones listed here ]
}
if (i === 0) { a = $('#el').attr('data-x1')}
else if (i === 1) { a = $('#el').attr('data-x2');}
else if (i === 2) { a = $('#el').attr('data-x3');}
}
What I want to achieve is take the 'a' value, use the value taken as a var name and then work on the variable content.
EDIT: Mostly my need is a function non-depending from the cases, the way I've used in the code above it's ok for now but every 2 months I have to add another case to make it work. The question is: how can I change my code above to make it more "universal" and "elegant" in less line of code?
Something like:
var a = $('#el').attr('data-x');
for (var i = 0; i <= 2; i++) {
a++; // 'a' must be the name of the var, the name of the variable it's the same as $('#el').attr('data-x').
// So, 'a' var will change every time the function it's executed as in the switch version of the first block of code above (abc++ or dfg++).
$('#'+a).text(a);
if (i === 0) { a = $('#el').attr('data-x1')}
else if (i === 1) { a = $('#el').attr('data-x2');}
else if (i === 2) { a = $('#el').attr('data-x3');}
}
Here lies my problem: 'a' will be a string ('abc' for example), abc is a global var with the number needed to be incremented and placed in $('#abc'). Here we have for example:
abc = 0; (global var)
a = abc; (scoped var)
abc++; (abc === 1)
$('#abc').text(abc); (equivalent to $('#abc').text(1);)
So I need to call in the loop the 'a' value as the name of the global var, increment it, and then use 'a' as a jquery selector to text the value of the incremented global var inside it.
How can I do that?
If I understand what you need to do, you should try to use eval()
The eval() function evaluates JavaScript code represented as a string.
var a = $('#el').attr('data-x');
for (var i = 0; i <= 2; i++) {
eval(a + "++");
$('#'+a).text(eval(a));
if (i === 0) { a = $('#el').attr('data-x1')}
else if (i === 1) { a = $('#el').attr('data-x2');}
else if (i === 2) { a = $('#el').attr('data-x3');}
}
I wanted to use two Not and one and in booleans to test if the variable is neither upper case nor lower case.
I used this code so far but it didn't work as required:
else if ((x[i]) !== (x[i].toUpperCase()) && (x[i]!== x[i].toLowerCase()) ){
x.splice(x[i], 1);
}
This code was for a function that sorts entered strings yet uppercase are sorted first.
Here is the full code, I am also open to understanding better ways to create this function apart from boolean logic and the array methods I used.
function alpha(str){ // United States
var x = str.split(""); // [U,n,i,t,e,d,S,t,a,t,e,s]
var cap = [];
var small = [];
for (var i = 0; i<x.length; i++){
if (x[i] == x[i].toUpperCase()){
cap.push(x[i]);
}
else if ((x[i]) !== (x[i].toUpperCase()) && (x[i]!== x[i].toUpperCase()) ) {
x.splice(x[i], 1);
}
else {small.push(x[i]);}
}
var z = cap.sort();
var y = small.sort();
return z.concat(y).join("");
}
Please note the second else if statement is only useful because the code adds an empty space string at the beginning of the output, I'm not sure where it comes from, so please let me know if you have any idea how to sort this even without using the second else if.
In the ASCII table, upper case letters come first. That's why they come first when you sort alphabetically. Here's a link to a page on Wikipedia that shows the table with the upper case letters appearing first and their numerical equivalents. It's even printable.
Also, I took the liberty of simplifying your code a little. Seems like .splice() was not necessary.
function alpha( str ) {
var x = str.split(""); // [U,n,i,t,e,d,S,t,a,t,e,s]
var cap = [];
var small = [];
var length = x.length;
for (var i = 0; i < length; i++) {
if (x[i] === x[i].toUpperCase()) {
cap.push(x[i]);
} else if (x[i] === x[i].toLowerCase()) {
small.push(x[i]);
}
}
return cap.sort().concat(small.sort()).join("");
}
Maybe explain what you're trying to do? It most likely has been done before in some form and you definitely came to the right place to find an answer.
Is this what you want to do?
var str = "United States";
function alpha(str) {
return str.split('').sort().join('');
}
alert(alpha(str));
In all programming languages (as far as i know), boolean expressions are always evaluated from the left to the right with brackets of course.
So in the following example my_func() is called first, and then if there is the chance that the complete expression becomes true my_other_func() is called
if (my_func() && my_other_func()) {
// I only get here if my_func() AND my_other_func() return true
// If my_func() returns false, my_other_func() is never called
}
The same is true for the "or" operator in the following example
if (my_func() || my_other_func()) {
// I only get here if my_func() OR my_other_func() return true
// If my_func() returns true, my_other_func() is not called
}
So back to your code, in details this part (I reformated it a bit for better readability):
if (x[i] == x[i].toUpperCase()){
// only uppercase here
cap.push(x[i]);
} else if (x[i] !== x[i].toUpperCase() && x[i] !== x[i].toUpperCase()) {
// tested twice the same thing, so Im really sure that its not uppercase :D
// only lowercase here
x.splice(x[i], 1);
} else {
// I will never reach this
small.push(x[i]);
}
Im not sure what you want to do, but I hope the comments help to understand your code.
enter code hereI am trying to make something editable online with a function like this
function toggle_editable (div, cssclass) {
var classToEdit = document.getElementsByClassName(cssclass)
for (i = 0;classToEdit.length; i++) {
if (classToEdit[i].contentEditable == false) {
classToEdit[i].contentEditable = true ;
}
if (classToEdit[i].contentEditable == true) {
classToEdit[i].contentEditable = false ;
}
}
}
classToEdit is a collection of HTML elements with the same class name or whatever document.getElementsByClassName(cssclass) returns
when going through the debugger it jumps over the line
classToEdit[i].contentEditable == true
as well as over the line
classToEdit[i].contentEditable == true
and does not execute the code in the braces following the if statements
this works however - meaning it sets the contenteditable property without hesitation
classToEdit.contenteditable = true;
as well as this
classToEdit.contenteditable = false;
(well obviously)
also this seemed to have no effect
classToEdit.contenteditable = !classToEdit.contenteditable
ideas anyone?
ps why is the loop
You've created an infinite loop here:
for (i = 0;classToEdit.length; i++) {
Should be:
for (var i = 0; i < classToEdit.length; i++) {
But, if you say classToEdit.contenteditable = true "works", you've to define "not working/is working" since the snippet doesn't definitely do what you expect it to do, if classToEdit is a HTMLCollection.
It looks like you'd want to toggle contentEditable values, you can do it like this:
for (var i = 0; i < classToEdit.length; i++) {
if (classToEdit[i].contentEditable == false) {
classToEdit[i].contentEditable = true ;
} else { // Notice else here, no need for another check
classToEdit[i].contentEditable = false;
}
}
Or simply without ifs in the loop:
classToEdit[i].contentEditable = !classToEdit[i].contentEditable;
Your current code will switch the value back to it's original in a case the value was false.
HTMLElement.contentEditable returns a string and not a boolean.
Hence, what you want to identify the state of your editable field is:
// Incorrect
classToEdit[i].contentEditable == true
// Coorect
classToEdit[i].contentEditable === 'true'
What's even better if you want to know the state of your fields is to use HTMLElement.isContentEditable
which returns a boolean:
classToEdit[i].contentEditable = !element.isContentEditable
Another way to refactor the above:
function toggleContentEdit() {
var editableFields = document.getElementsByClassName('editable');
[].forEach.call(editableFields, function(field){
var isEditable = field.isContentEditable;
field.setAttribute('contenteditable', !isEditable);
});
};
JSFiddle: http://jsfiddle.net/6qz3aotv/
Here I have a hidden input field
<input type="hidden" value="php,php mysql" id="tags"/>
and I also have a normal input box which users could add their new tags, now I want to check if the new tag which user wants to add is already added for him or not, if it's already added, alert('already there');
here is my code:
var already_added = $('#tags_final').val().toLowerCase();
new_tag = new_tag.toLowerCase();
if (already_added.indexOf(new_tag) < 0){
// add it, everything is OK
}else{
alert('already there');
}
the above works just fine for normal values, for example now if i try to add "php", this is gonna alert('already there'), the problem is, if I add "mysql", this also sends the alert since it finds it in "php mysql", but "mysql" is another tag and it needs to be added. what's solutions come to mind for this?
thanks for your help
I would think you'd want to break this up into separate pieces and perform a full text comparison on the actual tag itself.
var tags = $('#tags_final').val().toLowerCase().split(',');
new_tag = new_tag.toLowerCase();
if ($.inArray(new_tag, tags) < 0) {
// add it
} else {
alert('already there');
}
var tags = $('#tags_final').val().toLowerCase().split(',');
new_tag = new_tag.toLowerCase();
if (tags.indexOf(new_tag) < 0) {
// add it
} else {
alert('already there');
}
Edit: Courtesy of #nybbler, this would be more accurate using the jquery inArray method as linked in his comment.
I think using indexOf directly on the string is faster than either a regex or splitting into an array (even more so when relying on $.inArray).
Simply wrap your already_added string of tags with commas, then do the same for the tag when searching for it with indexOf:
var already_added = ',' + $('#tags_final').val().toLowerCase() + ',';
new_tag = new_tag.toLowerCase();
if (already_added.indexOf(',' + new_tag + ',') < 0) {
// add it
} else {
alert('already there');
}
I got this idea from a similar trick used by jQuery.
Save the tags between braces [php][php mysql]
and search for it
var already_added = $('#tags_final').val().toLowerCase();
new_tag = new_tag.toLowerCase();
if (already_added.indexOf("["+new_tag+"]") < 0){
// add it, everything is OK
}else{
alert('already there');
}
You can use a function like this to look for a key in a comma separated string of keys:
function contains(keys, key) {
var i = 0, k = key.length;
for (var i = 0;(i = keys.indexOf(key, i)) != -1; i += k) {
if ((i == 0 || keys.charAt(i - 1) == ',') && (i + k == keys.length || keys.charAt(i + k) == ',')) {
return true;
}
}
return false;
}
Demo: http://jsfiddle.net/Guffa/UHH9V/