I'm trying to write a function, to make a visual object come on and off, on and off, as the user clicks on it. Then add a click event listener in the class, called button btn-sauce.
So far my code doesn't work :
function renderWhiteSauce() {
if (element.classList) {
element.classList.toggle("btn-sauce.active");
} else {
var classes = element.className.split(" ");
var i = classes.indexOf("btn-sauce.active");
if (i >= 0)
classes.splice(i, 1);
else
classes.push("btn-sauce.active");
element.className = classes.join(" ");
}
document.querySelector('.btn-sauce.active').addEventListener('click', () => {
state.sauce = !state.sauce;
renderEverything();
});
You can just add and remove classes with methods classList.add('classname') and classList.remove('classname'). Define class which makes btn active and just add or remove it.
const elem = document.querySelector('.btn-sauce')
elem.addEventListener('click', () => {
if(elem.className.indexOf('className') < 0) {
elem.classList.add('className')
} else {
elem.classList.remove('className')
}
});
btn-sauce and active are two separate classes, but you are writing your code like they are one. Remove btn-sauce. (including the dot) from everything above the querySelector line and you will be able to toggle the active class on and off.
If the element is not "active" to begin with, you should also change document.querySelector('.btn-sauce.active') to document.querySelector('.btn-sauce').
One last note, you are calling renderEverything() in your click handler, which I assume is another function that calls renderWhiteSauce(), but I thought I'd mention it in case this was just a typo and they were meant to be the same function.
Related
I am trying to convert a small script from javascript to jquery, but I don't know where I should be putting the [i] in jquery?. I am nearly there, I just need someone to point out where I have gone wrong.
This script expands a search input when focused, if the input contains any values, it retains it's expanded state, or else if the entry is removed and clicks elsewhere, it will snap back.
Here is the javascript:
const searchInput = document.querySelectorAll('.search');
for (i = 0; i < searchInput.length; ++i) {
searchInput[i].addEventListener("change", function() {
if(this.value == '') {
this.classList.remove('not-empty')
} else {
this.classList.add('not-empty')
}
});
}
and converting to jquery:
var $searchInput = $(".search");
for (i = 0; i < $searchInput.length; ++i) {
$searchInput.on("change", function () {
if ($(this).value == "") {
$(this).removeClass("not-empty");
} else {
$(this).addClass("not-empty");
}
});
}
Note the key benefit of jQuery that it works on collections of elements: methods such as .on automatically loop over the collection, so you don't need any more than this:
$('.search').on("change", function() {
this.classList.toggle('not-empty', this.value != "");
});
This adds a change event listener for each of the .search elements. I've used classList.toggle as it accepts a second argument telling it whether to add or remove the class, so the if statement isn't needed either.
I am dynamically creating a table where i am adding onclick function to each column.
for (var x = 0; x < r.length; x++) {
//Setting the columns
if (i === 1) {
var headerCell = document.createElement("TH");
headerCell.innerHTML = r[x];
headerCell.id = x;
headerCell.onclick = function () {
sortTable(this.id, name);
}
row.appendChild(headerCell);
}
}
In a specific situation I want to disable the onclick function. Here is the code and it works.
$('#errorTable TH').prop("onclick", null).off("click");
and in another situation i want to reattach the onclick function. And that doesn't work. I want to enable the original function....
Any ideas ?
The way you created your table and adding/removing events are not easily maintainable. I also have some suggestions:
Review your code and define code click handler separately.
If you use jQuery in your project use it every where, if not, do not use it anywhere.
In your code i is undefined.
Add Remove Event Listener with jQuery
First define your handler function:
var myClickHandler = function(){
// this is your click handler
alert('Yes!!!');
}
Select your element and assign to a variable. <div id="clickable">Click Me!</div> must be in the DOM at the time of below script executed.
var element = $('#clickable');
// assign event listener
element.on('click',myClickHandler);
// remove event listener:
element.off('click',myClickHandler);
note that you must have to inform jQuery which handler should be removed.
See a sample https://codepen.io/softberry/pen/BEpove
An alternative is to build a click handler that checks a "kill switch".
var tableClickable = true;
headerCell.onclick = function () {
if (tableClickable) {
sortTable(this.id, name);
}
}
//In a specific situation I want to disable the onclick function.
something.addEventListener('someEvent', function () {
tableClickable = false;
});
//and in another situation i want to reattach the onclick function.
something.addEventListener('someOtherEvent', function () {
tableClickable = true;
});
I have a left sidebar menu which has submenus, i want each menu item to toggle a classname "active" so the submenu will open i have CSS for it.
The thing is i am using document.getElementsByClassName to select and iterate all of the menu items and is only working for the first element, i have been searching and it has something to do with closures and i am trying different solutions but its not working.
i am making the function so i can use it to toggle a classname of another div and not the one clicked, in that case i use and ID.
var toggleClassname = function (otherDiv, sameDiv) {
var divToToggleClass;
//are we going to use ID and toggle the classname of another div ?
if (sameDiv) {
divToToggleClass = this;
} else {
divToToggleClass = document.getElementById(otherDiv);
}
console.log(divToToggleClass);
var className = divToToggleClass.className + ' ';
if (~className.indexOf(' active ')) {
divToToggleClass.className = className.replace(' active ', '');
} else {
divToToggleClass.className += ' active';
}
};
var MenuItemsArray = document.getElementsByClassName("classOfMyMenuItems");
for (var i = 0; i < subMenuItemsArray.length; i++) {
MenuItemsArray[i].addEventListener('click', function () { toggleClassname(null, true) }, false);
}
i have been trying using [].forEach.call or wrapping the function in another that returns the function, not working.
I am doing this in pure javascript, cant use the new .classList.toggle i would also use attachEvent to be more backwards compatible (old IE).
The problem is that within your toggleClassname() function this is not equal to the clicked element. It will actually be either undefined or window depending on whether your code is running in strict mode or not.
A click handler bound with addEventListener() will have this set to the clicked element, so within the following anonymous function:
function () { toggleClassname(null, true) }
...the value of this is the element in question. But then you call toggleClassname() and don't pass it a reference to the clicked element or set its this value. You can explicitly set it using .call():
function () { toggleClassname.call(this, null, true) }
Further reading:
this in JavaScript
.call()
This answer might help you:
addEventListener using for loop and passing values
Without going too deep into your code, I'd say if you try and make it
for (var i = 0; i < subMenuItemsArray.length; i++) {
(function () {
var k = i;
MenuItemsArray[k].addEventListener('click', function () { toggleClassname(null, true) }, false);
}()); // immediate invocation
}
That should work.
I'm working on a JavaScript and CSS3 responsive HTML menu for mobile devices. The JS is quite simple. It gets the element ID of the body and if the menu is closed it changes class to menuOpen, and vice versa. The CSS handles all the transitions and displays of the actual menu.
<script type="text/javascript">
function menuButton() {
if( document.getElementById("htmlbody").className == "menuClosed" ) {
document.getElementById("htmlbody").className = "menuOpen";
} else {
document.getElementById("htmlbody").className = "menuClosed";
}
}
</script>
The problem I ran into is this. Instead of removing the entire class and replacing the entire class, how can I remove a single class and then add a single class?
Since an element will contain multiple classes I need to change the condition to see IF it contains the className, as opposed to being the actual className. I believe I can simply add the new class by doing the JS below but I don't know how to remove the old class.
document.getElementById("htmlbody").className += "menuOpen";
Hope I've provided enough details.
EDIT
Thanks for help. I've managed to get this to work great. Does just what I need to.
<script type="text/javascript">
function menuButton() {
var htmlBody = document.getElementById("htmlbody");
if( htmlBody.classList.contains("menuClosed") ) {
htmlBody.className = htmlBody.className.replace(/(?:^|\s)menuClosed(?=\s|$)/g, " menuOpen");
} else {
htmlBody.className = htmlBody.className.replace(/(?:^|\s)menuOpen(?=\s|$)/g, " menuClosed");
}
}
</script>
I use regular expressions for this. I have a macro that does the following
(node).className = (node).className.replace(/(?:^|\s)cl(?=\s|$)/g,"");
where cl is replaced with the actual class to be removed. That handles the remove. You can code your insert as you said but be aware your className can grow as you get duplicate class invocations.
Function Definition
Node.prototype.hasClass = function (className) {
if (this.classList) {
return this.classList.contains(className);
} else {
return (-1 < this.className.indexOf(className));
}
};
Node.prototype.addClass = function (className) {
if (this.classList) {
this.classList.add(className);
} else if (!this.hasClass(className)) {
var classes = this.className.split(" ");
classes.push(className);
this.className = classes.join(" ");
}
return this;
};
Node.prototype.removeClass = function (className) {
if (this.classList) {
this.classList.remove(className);
} else {
var classes = this.className.split(" ");
classes.splice(classes.indexOf(className), 1);
this.className = classes.join(" ");
}
return this;
};
Usage :
myElement = document.getElementById("htmlbody");
Now you can call myElement.removeClass('myClass')
or chain it: myElement.removeClass("oldClass").addClass("newClass");
This will remove a single class
function menuButton() {
var htmlBody = document.getElementById('htmlbody');
if( htmlBody.className.test(/menuClosed/)) {
htmlBody.className.replace('menuClosed', 'menuOpen');
} else if (htmlBody.className.test(/menuOpen/)){
htmlBody.className.replace('menuOpen', 'menuClosed');;
}
}
Use the String.replace function. And i sugest to use class names as -. Not camel cased as is not the standard, but it depends of your team.
I'll keep this short - I've got a list of buttons, that I create using a loop, and when one of them gets clicked I want to be able to pass its id attribute to another file in order to dynamically generate a new page.
Here's the code:
for (var i in data.contacts) {
var temp = document.createElement('div');
temp.className = "contacts";
var dude = document.createElement('input');
dude.type = "button";
dude.value = data.contacts[i];
dude.id = data.contacts[i];
dude.className = "dude_button" + data.contacts[i];
dude.addEventListener('click', function(event) { gotoProfile(dude.id); }, false);
temp.appendChild(dude);
temp.appendChild(document.createElement('br'));
theDiv.appendChild(temp);
}
// and now in another file, there's gotoProfile():
function gotoProfile(x) {
var username = document.getElementById(x).value;
if (xmlHttp) {
try {
.... etc.
Now see this works, sort of, but the problem is that when I click any button, it only passes the last dude.id value from the list data.contacts. Obviously I want every button's addEventListener to pass its own data.contacts[i] value, instead of just the last one.
Help appreciated, thanks guys.
Because JavaScript has no block scope, dude will refer to the last assigned element (because the loop finished) when the event handler is called. You have to capture the reference to the current dude by e.g. using an immediate function:
dude.addEventListener('click', (function(d) {
return function(event) {
gotoProfile(d.id);
}
}(dude)), false);
This is a common error when creating functions in a loop.
But you can make it even easier. The event object has a property target that points to the element the event was raised on. So you can just do:
dude.addEventListener('click', function(event) {
gotoProfile(event.target.id);
}, false);
And with that said, you don't need to add a handler for every button. As you are doing the same for every button, you could attach the same event handler above to the parent of the buttons (or a common ancestor) and it would still work. You just have to filter out the clicks that don't happen on a button:
parent.addEventListener('click', function(event) {
if(event.target.nodeName == 'INPUT' && event.target.type == "button") {
gotoProfile(event.target.id);
}
}, false);