Trouble with click not unbinding - javascript

I'm building a small adventure to help me understand some concepts of JQuery/Javascript and I've hit a snag.
What I'm trying to do is by clicking on a "take" button and then on an item on the screen to move it to the inventory. Good news is, that all works. Bad news is that if I press another item without using the "take" button first it goes straight to my inventory.
Same problem with "look". You should have to press "look" first and then the item to get a description, but once it's pressed it'll give every item you click a description straight away.
You can imagine that once you've pressed them both every item you click without clicking another button will give you a description and end up in your inventory straight away.
This is how I'm trying to get it to unbind:
$("#take").click(function () {
$("#zilverenKogel").click(function () {
$("#zilverenKogel").fadeOut(1200, 0);
$("#invKogel").fadeTo(1500, 1);
$("#take").unbind("click");
Any help will be greatly appreciated!
Mofx solved it for me. End, and working result, is:
var state = null;
$( "#take" ).click(function () {state = "take";})
$( "#look" ).click(function () {state = "look";})
$( "#zilverenKogel" ).click(function () {
if (state == "take") {
$("#zilverenKogel").fadeOut(1200, 0);
$("#invKogel").fadeTo(1500, 1);
state=null;
} else if (state == "look") {
alert("blablabla");
state=null;
}
});
thanks a lot Mofx!

You unbind the wrong event. You want to unbind the event on "#zilverenKogel", because that is the one doing the "take-action".
I also suspect, you connect events to other elements as well within "$("#take").click(function () {". You would have to disconnect them all. After the action was taken or another action was selected.
Maybe a statemachine would be a better solution here. Something like:
var state = null;
$("#take").click(function () {state = "take";}
$("#look").click(function () {state = "look";}
$("#zilverenKogel").click(function () {
if (state == "take") {
moveToInventory();
} else if (state == "look") {
showDescription();
}
}

Related

React if else statement in component runs 2 scenarios when I only want 1 to run

I am making a simple drop down menu app.
My goal is to update the look of the menu through DOM manipulation using an if else statement for specific keyboard key clicks.
Here is the code in question along with an explanation of what is happening currently as well as what I want the code to actually do.
componentDidMount() {
window.addEventListener('keydown', e => {
// This listens for a key press
if (e.key === 'Enter') {
if (this.state.menuIsOpen === false) {
menuOptions[0].style.backgroundColor = 'cornflowerblue'
this.setState(() => ({
menuIsOpen: true,
height: '350px'
}));
// ^ This opens the menu and changes a state property.
} else if (this.state.menuIsOpen === true) {
// This does the opposite.
h1.innerHTML = menuOptions[count].innerHTML;
menuOptions[count].style.backgroundColor = '#a9c4f5'
this.setState(() => ({
menuIsOpen: false,
height: '50px'
}));
}
}
})
}
I know what the issue is, my problem is how to get around it. Right now what is happening is when the 'Enter' key is clicked, the code checks the state to see if the condition is false. Because it is, the state gets changed to true, which in turn causes the next if else block to run because now the condition is true.
What I want is for 1 block to run on the 'Enter', once if the menu is closed and once is the menu is already opened. Not both simultaneously.
Thank you in advance for the help :)
I believe that it called multiple times because this event fires not only when you hit some key, but also when you hold it. So it is probably fires few times when you hit enter once.
Maybe try to just change the event that you listen to.
Simple fix is to set event to keyup, so you know for sure that it would be fired only once.
Also if I would have a whole picture, we would can to find out some solution by changing the UX (I believe that you might not want close any menu by hitting enter, so solution might be just to make menu close handler on esc button for example)
I figured it out!
As someone else stated, the event listener was firing twice (I suspect, correct me if I am wrong). To fix this I simply added 2 lines of code which I have highlighted.
Heres an explanation for the 2 lines:
.preventDefault()
.stopImmediatePropagation()
Thank you to everyone who tried to help :)
componentDidMount() {
window.addEventListener('keyup', e => {
e.preventDefault(); // Added this line
e.stopImmediatePropagation(); // And this one
if (e.key === 'Enter') {
if (this.state.menuIsOpen === false) {
menuOptions[0].style.backgroundColor = 'cornflowerblue'
this.setState(() => ({
menuIsOpen: true,
height: '350px'
}));
} else if (this.state.menuIsOpen === true) {
h1.innerHTML = menuOptions[count].innerHTML;
menuOptions[count].style.backgroundColor = '#a9c4f5'
this.setState(() => ({
menuIsOpen: false,
height: '50px'
}));
}
}
})
}

Jquery issue with val not working as expected

I have a couple of forms on a site. On the first form I used the code below to add a border color if the input field is not blank and remove it if it is blank. This works just fine no issues. But I've found that when I try to use the same method on other forms, to do something else using the same logic, it does not work.
I have read through many forums and what I'm seeing is that the code is only read on page load. But I have forms that run the function after the page is far past loading. Can someone give some light to this? I'm really trying to understand the way this works fully.
Code that works on form:
var checkErrorIn;
jQuery(document).ready(function ($) {
checkErrorIn = setInterval(CheckErrorInput, 0);
});
function CheckErrorInput() {
if (jQuery('body').is('.page-id-6334')) {
// First Name, Last Name validation colors
var pasdFName = jQuery('#first_name').val();
var pasdLName = jQuery('#last_name').val();
if (pasdFName != '') {
jQuery('#first_name').addClass('formConfirm_cc');
} else {
jQuery('#first_name').removeClass('formConfirm_cc');
}
if (pasdLName != '') {
jQuery('#last_name').addClass('formConfirm_cc');
} else {
jQuery('#last_name').removeClass('formConfirm_cc');
}
if (pasdFName != '' & pasdLName == '') {
jQuery('#last_name').addClass('formError_cc');
} else {
jQuery('#last_name').removeClass('formError_cc');
}
if (pasdFName == '' & pasdLName != '') {
jQuery('#first_name').addClass('formError_cc');
} else {
jQuery('#first_name').removeClass('formError_cc');
}
}
}
Code that is not working:
if (jQuery('body').is('.woocommerce-page')) {
var checkActiveName = jQuery('.woo_login_form > form > #username').val();
jQuery('.woo_login_form').on('input', function(){
jQuery('.woo_login_form').addClass('cdc_keep_active');
});
if (checkActiveName =='') {
jQuery('.woo_login_form').removeClass('cdc_keep_active');
}
}
What I am trying to do is fix an issue with a form becoming hidden if not hovered over even when the input has characters. Based on my research I figured I'd do the .on to get the class added when the input got characters. That works but the removal of the characters isn't removing the class. The logic looks right to me. What am I missing?
Thank you in advance for your help and insight.
Update:
Ok so I ended up doing this:
jQuery('.woo_login_form').on('click', function () {
jQuery('.woo_login_form').addClass('cdc_keep_active');
});
jQuery('.custom-login-box > a').on('click', function () {
jQuery('.woo_login_form').toggle();
});
For some reason my class would not add with any of the methods suggested individually so I combined the logic. The first part adds the class that makes the form visible but then the form won't close if clicked out of regardless of the 'removeClass'. So I added a toggle (thank you commenters) method to the "hovered link" to allow users to close the box if not needed.
Would still like to understand why the first method worked in one instance but not the other. Any and all insight appreciated. Thank you.
In your current code example you immediately check for the value of the username field.
var checkActiveName = jQuery('.woo_login_form > form > #username').val();
The thing with this is that checkActiveName will never change, unless it is reassigned elsewhere in the code.
What you need to do is to check the current value after every input of the user. That means moving that line of reading the value of the input inside the input event listener.
if (jQuery('body').is('.woocommerce-page')) {
var $wooLoginForm = jQuery('.woo_login_form');
var $userName = jQuery('#username'); // This ID should only exist once, so no need for complex selectors.
$wooLoginForm.on('input', function() {
var checkActiveName = $userName.val();
if (checkActiveName =='') {
$wooLoginForm.removeClass('cdc_keep_active');
} else {
$wooLoginForm.addClass('cdc_keep_active');
}
});
}
On a sidenote: using setInterval to validate your form is a bad practice. This would basically run infinitely. It doesn't have to. You only have to check if a form is valid after the user enters a value.
Apply the same technique with the event listener like in your second code snippet.
var $document = jQuery(document);
$document.ready(function ($) {
/**
* It might even be better to listen for the input event on the form
* that has to be validated, but I didn't see it in your code.
* Right now it listens for input on the entire page.
*/
$document.on('input', CheckErrorInput);
});

Problem with changing button text content

I'm making a battleship game in Javascript and I have a problem with a function that changing button text content. I want to do that when the user click the button the text content of the button changes.
function changePosition(eventBtn){
if(eventBtn.target.textContent=='perpendicularly'){
eventBtn.target.textContent='horizontally';
}
else{
eventBtn.target.textContent='perpendicularly';
}}
But when I click on the button nothing changes. I think the problem is with the else statement because when I delete this statement all work.
Make sure that you are adding the listener to the button appropriately.
function changePosition(event) {
if (event.target.textContent === 'Perpendicularly') {
event.target.textContent = 'Horizontally';
} else {
event.target.textContent = 'Perpendicularly';
}
}
document.querySelector('.direction').addEventListener('click', changePosition);
<button class="direction">Perpendicularly</button>
In regards to the following statement:
But when I click on the button nothing changes. I think the problem is with the else statement because when I delete this statement all work.
Your event may be firing twice. It could be calling your event listener twice, effectively reverting the change it just made to the text.
Suggested improvement
Magic strings are bad, and I would recommend the use of enumerable values instead.
const Direction = {
PERPENDICULARLY: 'Perpendicularly',
HORIZONTALLY: 'Horizontally'
};
const changePosition = event => {
event.target.textContent =
event.target.textContent === Direction.PERPENDICULARLY
? Direction.HORIZONTALLY
: Direction.PERPENDICULARLY
};
document.querySelector('.direction').addEventListener('click', changePosition);
<button class="direction">Perpendicularly</button>

jQuery slideToggle menus based on mouseenter/leave

I've mostly got a little menu system working, but having a couple quirks I can't figure out. There don't seem to be any questions I can find with this same issue.
The functional example is at http://louisnk.com/photography - the only menu that has pretty much all the code in place is 'USA'.
on click, the menu shows, and I want it to:
A) delay, then slide back up if the mouseenter event never fires
B) not slide back up if the mouseenter event does fire
C) delay, then slide back up when the mouseleaves
I have it pretty well down, except I believe I have some event delegation issues...
The first time I click, it all works great. The second time I click on any menu, it seems like 'poked' (the variable which gets set as true/false depending on the switch case) is being set to false, and so automatically causing the 'noPoke' function to close the menu regardless of the mouseenter event. Subsequent clicks obviously cause stranger behavior.
I have the following code all wrapped in a document ready function:
var menuSwitch = function(menu) {
$(menu).on('mouseenter mouseleave', function(e) {
switch(e.type) {
case 'mouseenter':
poked = true;
console.log('probed');
return false;
break;
case 'mouseleave':
$(this).delay(1500).slideUp(500,function() {
$(menu + 'li').hide();
console.log('switched');
});
poked = false;
break;
default:
poked = false;
console.log('defaulting');
break;
}
});
}
var noPoke = function(menu) {
if (poked == false) {
$(menu).delay(2000).slideUp(500,function() {
$(menu + 'ul').hide();
console.log('no pokes given');
});
poked = true;
}
}
var poked = false;
$('.usa').click(function(e) {
e.preventDefault();
poked = false;
$('.usaMenu').slideToggle(500);
menuSwitch('.usaMenu');
console.log(poked);
$('.usaMenu li').on('mouseenter', function() {
if ($(this).children() != false) {
$(this).children().fadeTo(200,1);
}
else {
$('.usaMenu li>ul').fadeTo(200,0).hide();
}
});
noPoke('.usaMenu');
});
So, after spending another 6 hours researching and trying different things, I ended up with the following, which solved the problems I was having.
Hopefully this is useful to people trying to do this in the future.
var menuSwitch = function(menu) {
if ($(menu).is(':hover') === false) {
var hideMe = setTimeout(function() {
$(menu).slideToggle(500);
},4000);
}
$(menu).on('mouseenter mouseleave', function(e) {
switch(e.type) {
case 'mouseenter':
console.log('probed');
clearTimeout(hideMe);
$(this).stop();
$(this).slideDown(200);
break;
case 'mouseleave':
$(this).delay(2000).slideUp(500,function() {
$('li>ul',menu).hide();
console.log('switched');
});
break;
}
});
}
var babies = function(menu) {
$('li',menu).unbind('mouseenter mouseleave click');
$('li',menu).each(function() {
if ($('li',menu).children() != false) {
$(this).on('click',function(e) {
e.preventDefault();
$(this).children('ul').slideToggle(300);
});
}
});
}
var menuCtrl = function(menu) {
$(menu).slideToggle(500);
$(menu).unbind('mouseenter mouseleave');
menuSwitch(menu);
babies(menu);
}
$('.usa').click(function(e) {
e.preventDefault();
menuCtrl('.usaMenu');
});
Ultimately it turned out that because the initial click handler was running the functions every time it fired, it was creating duplicates of the mouseenter/leave event handlers, and therefore causing all sorts of problems. I was also trying to make the menu close if the mouse never entered, in the wrong way.
To solve the most obnoxious issue (duplicate event handlers), I simply used the unbind() api, passing it the events to unbind at the start of the function, so that when they were re-bound, there was only one instance. This may not be the most efficient way, but it works.
To solve the timing/hiding if no mouseenter issue, I used a setTimeout function, which was then cleared if mouseenter-ed.

Problem with event.target in IE

I'm writing js for a status update system to be used on various pages throughout a app that I'm working. I am really just starting to get more comfortable with javascript so it has been somewhat of a challenge to get to the point where I have everything now.
The status system is basically a facebook clone. For the most part everything is supposed to function the way that facebook's status updates and status comments do. The intended behavior is that when the user clicks in the status textarea, the div under the status textarea slides out revealing the submit button as well as some other checkboxes.
If the user clicks anywhere else on the page except a link or any element that has the class prevent_slideup the div slides up hiding the submit button and any checkboxes.
I'm using a document.body click function to determine what the user clicked on so I know which form elements to hide if I should even hide them. I do not want this slideup to take place on a textarea if that textarea has focus or the user is selecting a checkbox that goes with that form. Hence the prevent_slideup class. I also do not want to bother running the slideup logic if the user has clicked on a link. I'd prefer they just leave the page without having to wait for the animation.
The code that I was using to accomplish this task can be found in the $(document.body).click(function (e) section below where I'm doing a .is('a') check on the event target.
This code works as expected in chrome and firefox, however in ie when a link is clicked for the first time it seems that the element stored in var target is actually a div instead of an anchor. What ends up happening is that the submit div slides up and the user is not taken to the link that they just clicked on. If a link is clicked a second time the user is taken to the page as you would expect.
It seems to me that there's some kind of a lag in ie as to what the current event being fired is.
The entire status module is working other than this one strange ie bug regarding the users click on the link not being carried out the first time that they click a link after opening the status textarea. Does anything jump out in this script that would explain this behavior or does anyone have any other advice?
Thanks in advance for your help.
$(document).ready(function(){
$("textarea.autoresize").autoResize();
});
$(document.body).click(function (e){
var target = e.target || e.srcElement;
console.log(target);
console.log($(target).is('a'));
if($(target).hasClass('prevent_slideup') || $(target).is('a'))
{
return true;
}
else
{
var active_element = document.activeElement;
var active_status_id = $(active_element).attr('data-status_id');
var active_has_data_status_id = (typeof active_status_id !== 'undefined' && active_status_id !== false) ? true : false;
$('textarea').each(function(){
if($(this).hasClass('status_comment_textarea'))
{
var status_id = $(this).attr('data-status_id');
if($('#comment_textarea_'+status_id).val() === '' && (!active_has_data_status_id || active_status_id !== status_id))
{
hide_status_comment_submit(status_id);
}
}
else if($(this).attr('id') === 'status_textarea')
{
if($('#status_textarea').val() === '' && $(active_element).attr('id') !== 'status_textarea')
{
$('#status_textarea').html($("#status_textarea").attr('placeholder'));
hide_status_submit();
}
}
});
return true;
}
});
$("#status_textarea").live('click', function(){
if($('#status_textarea').val() === $("#status_textarea").attr('placeholder'))
{
$('#status_textarea').html('');
}
show_status_submit();
return false;
});
$(".comment_toggle").live('click', function(){
var status_id = $(this).attr('data-status_id');
show_status_comment_submit(status_id);
return false;
});
$(".status_comment_submit").live('click', function(){
var status_id = $(this).attr('data-status_id');
$('#status_comment_submit_wrapper_'+status_id).addClass('status_comment_submit_successful');
return false;
});
$(".show_hidden_comments").live('click', function(){
var status_id = $(this).attr('data-status_id');
$('#status_hidden_comments_'+status_id).show();
$(this).hide();
return false;
});
function hide_status_submit()
{
$("#status_textarea").removeAttr('style');
$("#status_textarea").blur();
$("#status_block").removeClass('padding_b10');
$("#status_submit_wrapper").slideUp("fast");
return false;
}
function show_status_submit()
{
if ($("#status_submit_wrapper").is(":hidden"))
{
$("#status_block").addClass('padding_b10');
$("#status_submit_wrapper").slideDown('fast');
}
return false;
}
function hide_status_comment_submit(status_id)
{
if(!$('#status_comment_submit_wrapper_'+status_id).is(":hidden"))
{
$('#status_comment_submit_wrapper_'+status_id).hide();
$('#fake_comment_input_'+status_id).show();
$('#comment_textarea_'+status_id).removeAttr('style');
}
return false;
}
function show_status_comment_submit(status_id)
{
if($('#status_comment_submit_wrapper_'+status_id).is(":hidden"))
{
$('#fake_comment_input_'+status_id).hide();
$('#status_comment_submit_wrapper_'+status_id).show();
$('#comment_textarea_'+status_id).focus();
}
return false;
}
function status_comment_submit_successful()
{
hide_status_comment_submit($('.status_comment_submit_successful').attr('data-status_id'));
$('.status_comment_submit_successful').removeClass('status_comment_submit_successful');
return false;
}
I figured out that there were two main issues with my script...
1.) The document.body function and the #status_textarea live click funtioins were conflicting with each other.
2.) After adding the logic for the #status_textarea function into the document.body function I noticed that the script still didn't quite work as expected in internet explorer unless I had an alert in the function. The problem at this point was that the autoresize plugin that I'm using on the textarea was also conflicting with the document.body function.
I was able to rectify the situation by adding a dummy text input and hiding the status textarea. On click of the dummy text input the status textarea is shown and the the dummy text input is hidden. I have no idea why this worked, but it seems to have solved my problems.

Categories