two onClick() events happening simultaneously? - javascript

What I am expecting from my code is this:
When clicking a button, a menu of options appears at the pointer position. Any following click, whether on a menu item or elsewhere in the browser, should close the menu. Clicking on a menu item closes the menu, but not clicking anywhere else. When I uncomment $(document.body).one('click', function() {menu.remove()} the menu never appears in the first place, and I suspect that I somehow have it arranged so that the click to bring up the menu actually closes the menu as well. Here is the code:
render : function() {
this.$el.html(this.template(this.model.toJSON()));
var that = this;
if (this.model.attributes.memberType != 'OWNER') {
this.$('.memberTypeSelector').button({
icons : {
secondary : "ui-icon-triangle-1-s"
}
}).click(function(event) {
that.showPermissions(that.model, event, that);
});
...
},
showPermissions : function(member, event, view) {
var levels = ['ADMIN', 'CONTRIBUTOR', 'VIEWER'];
var menu = $('<ul>');
$.each(levels, function() {
if(member.attributes.memberType !== this) {
var item = $('<li>').appendTo(menu);
$('<a>').attr('href', '#').text(this).appendTo(item).click(function() {
menu.remove();
view.changePermission(member, this.text, view);
});
}
});
menu.menu().css({
position : 'absolute',
left : event.clientX,
top : event.clientY
});
$(document.body).append(menu);
/*$(document.body).one('click', function() {
menu.remove();
});*/
}
Thanks in advance for your help.

If you delay binding to the document by 10ms, that should be enough time for the event to propagate to the body so that it doesn't immediately close the menu, then the next click on the menu will result in the body click handler triggering.
setTimeout(function(){
$(document.body).one('click', function() {menu.remove();});
},10)
you can't use stop propagation or anything similar because that would also stop the 2nd click on the menu.

While delaying the second event binding will probably work 99.999% of the time, I can't help but feel that nagging 'what if' for the one time that something lags and it doesn't work.
This question provides a more satisfactory (at least to me) solution

Related

Cancel hover action when no longer hovering?

I have a situation where when a user hovers a button, it should display a little popup after a slight delay.
tt = angular.element(element);
...
showTooltipFn = function() {
setTimeout(function() {
tt.tooltip("show");
}, 700);
};
However, when you say hover the mouse quickly over the other buttons, it will display all the buttons you hovered. I only want to display the popup on the button that it is currently hovering. If I am no longer hovering cancel the show.
Is there a way to do this with AngularJS? Check if I'm still hovering something?
ngMouseLeave lets you evaluate expressions upon mouseleave.
Example from AngularJS Docs
<button ng-mouseleave="count = count + 1" ng-init="count=0">
Increment (when mouse leaves)
</button>
count: {{count}}
For your case...
var timeoutPromise;
showTooltipFn = function() {
timeoutPromise = $timeout(function() {
tt.tooltip("show");
}, commonOptions.delay.show);
tt.on("mouseleave", function() {
$timeout.cancel(timeoutPromise);
});
};
}

testing for click event with jasmine?

I am required to write a Jasmine.js test to test a menu icon for what will happen when it is clicked, (the menu bar slides in when it is clicked for the first time, and out when it is clicked for the second time which it does, but my test fails to demonstrate it)
I came up with this idea but the spec-runner shows (expected false to be true). any help on what could be the problem?
describe('The menu', function () {
/* TODO: Write a test that ensures the menu changes
* visibility when the menu icon is clicked. This test
* should have two expectations: does the menu display when
* clicked and does it hide when clicked again.
*/
it('the menu changes visibility when the menu icon is clicked', function () {
var menuIconClicked, menuChangesWhenClicked = false,
menuChangesWhenClickedAgain = false;
$(".menu-icon-link").click(function(){
var $this = $(this);
$(this).data('clicked', true);
if($(this).data('clicked')) {
menuIconClicked=true // if menu icon is clicked, set the menuIconClicked value to true
if (menuIconClicked && $('body').hasClass(null)) {
menuChangesWhenClicked=true;
}
}
if($this.data('clicked') && menuIconClicked===true) {
menuIconClicked=false // if menu icon is clicked when it already has been clicked aka menuIconClicked===true
if (!menuIconClicked && $('body').hasClass('menu-hidden')) {
menuChangesWhenClickedAgain=true;
}
}
});
expect(menuChangesWhenClicked).toBe(true);
expect(menuChangesWhenClickedAgain).toBe(true);
});
});
It looks like you are already using Jasmine and JQuery, so I would suggest using also the jasmine-jquery.js library to help you with tracking states?
This was a good reference: Testing That A DOM Event Was Fired
To get the code below to work, just include jasmine-jquery.js in your project folder, link the <\script> via your index.html's <\head> and your set. Hope this helps.
describe('The menu', function() {
// Add a spyOnEvent
let spyEvent, menu;
beforeEach(function() {
// I assumed your menu icon has a unique ID of 'menuIconID'
// so I passed onto a spy listener.
spyEvent = spyOnEvent('#menuIconID', 'click');
});
it('the menu changes visibility when the menu icon is clicked', function() {
// Click once
$("#menuIconID").trigger("click");
expect('click').toHaveBeenTriggeredOn('#menuIconID');
expect(spyEvent).toHaveBeenTriggered();
menu = $('body').attr('class'); // assign the new class
expect(menu).toBe('');
// Click again
$("#menuIconID").trigger("click");
expect('click').toHaveBeenTriggeredOn('#menuIconID');
expect(spyEvent).toHaveBeenTriggered();
menu = $('body').attr('class'); // update the new class
expect(menu).toBe('menu-hidden');
});
});

Logic for showing elements and hiding them on body click

I have some piece of code.
This code on button click open menu.
When i click on button again, menu is hidden (i remove .show class, show class has display:block rule, so i toggle visibility of this item by clicking on button).
In next line, i have event, which check what element is clicked. If i "click" outside" of menu, menu become hidden, beacuse i remove .show class.
And now i have a problem, it looks like first part of code dont work anymore (button.on('click')) - i mean, work, but second part of code is also executed, and this logic is now broken.
Have you got any idea for workaround?
Thanks
var menu = $('.main-menu');
var button = $('.burger');
button.on('click',function() {
if (menu.hasClass('show')) {
menu.removeClass('show');
$(this).removeClass('opened');
} else {
menu.addClass('show');
$(this).addClass('opened');
}
});
$(document).bind( "mouseup touchend", function(e){
var container = menu;
if (!container.is(e.target)
&& container.has(e.target).length === 0) {
container.removeClass('show');
button.removeClass('opened');
}
});
maybe use jQuery toggle() method ? For example:
button.on('click',function() {
menu.toggle();
});
You need to bind an outer click event only when the button click event has been triggered, and remove the outer click event when the outer click event has been triggered:
var menu = $('.main-menu');
var button = $('.burger');
button.on('click',function() {
if (menu.hasClass('show')) {
menu.removeClass('show');
$(this).removeClass('opened');
} else {
menu.addClass('show');
$(this).addClass('opened');
}
var butbindfunc = function(e){
var container = menu;
container.removeClass('show');
button.removeClass('opened');
$(this).unbind("mouseup touchend", butbindfunc);
};
$(document).not(button).bind( "mouseup touchend", butbindfunc);
});
Note, that I have removed your condition in the document binding callback, and simple excluded it from the select set.

Javascript function interacts with other function

I am completely new to javascript (and jquery) and have been experimenting with drop down menus the past couple of days. I found this one fancy notification menu, and I tried to see what happens when I have two of them on the page. Anyways, I made a quick example of my problem here:
http://jsfiddle.net/rgt03mu4/24/
The problem is that I can have both notification containers open up if I click on both.
If I am already clicked on one of the bells, then I click on the other, it should close the other one. Instead it keeps it open, and even when you click on the other container one, it still doesn't close it. You have to click off the page or click the notification bells. I am trying to make it to where you can only have one open at a time. So in order to do this, I tried changing the names of the functions:
As you can see:
$(function() {
var nContainer = $(".notification-popup-container");
//notification popup
$("#notification-link").click(function() {
nContainer.fadeToggle(300);
return false;
});
//page click to hide the popup
$(document).click(function() {
nContainer.hide();
});
//popup notification bubble on click
nContainer.click(function() {
return false;
});
});
I added the next function to be called test(), which you would think, since it's an entirely new function it would work differently. Instead, the error still persists.
What am I doing wrong? I even gave the the new bell it's own divs and link name. I also renamed container to container2.
Set the global variable for your container:
var nContainer = $(".notification-popup-container");
var nContainer2 = $(".notification2-popup-container");
$(function() {
var nContainer = $(".notification-popup-container");
//notification popup
$("#notification-link").click(function() {
nContainer.fadeToggle(300);
nContainer2.hide(); //hide the second container
return false;
});
//page click to hide the popup
$(document).click(function() {
nContainer.hide();
});
//popup notification bubble on click
nContainer.click(function() {
return false;
});
});
And you can do same with other function.
DEMO
There is no need to give the popup containers different classnames.
I would give the a-tags a common classname instead of an id. The href can be used to identify the target popup, so the binding between the link and the target popup is set in the origin of action. The JS part would be abstracted and could be reused.
<a class='notification-link' href='#firstpopup'>X</a>
<a class='notification-link' href='#secondpopup'>X</a>
<div class='notification-popup-container' id="firstpopup">
... firstpopup
</div>
<div class='notification-popup-container' id="secondpopup">
... secondpopup
</div>
The click handler first hides all the popups before opening a new one.
$(".notification-link").click(function () {
$(".notification-popup-container").hide();
var targetId = $(this).attr('href');
$(targetId).fadeIn(300);
return false;
})
working example: http://jsfiddle.net/qyLekdwk/
The problem here is how the event propgation is handled
$(function () {
var nContainer = $(".notification-popup-container");
//notification popup
$("#notification-link").click(function () {
nContainer.fadeToggle(300);
});
//page click to hide the popup
$(document).click(function (e) {
if (!$(e.target).closest('#notification-link, .notification-popup-container').length) {
nContainer.hide();
}
});
});
$(function test() {
var nContainer2 = $(".notification2-popup-container");
//notification popup
$("#notification2-link").click(function test() {
nContainer2.fadeToggle(300);
});
$(document).click(function (e) {
if (!$(e.target).closest('#notification2-link, .notification-popup-container').length) {
nContainer2.hide();
}
});
});
Demo: Fiddle

Detect click outside element?

Similar to this question, but taking it a step further. I would like to detect clicks outside of a set of items, which I am handling in the following way:
$('#menu div').live('click', function() {
// Close other open menu items, if any.
// Toggle the clicked menu item.
$('body').one('click', function(event) {
// Hide the menu item.
event.stopPropagation();
});
});
This works like a charm, unfortunately, when another menu item is open and a
second is clicked, it requires two clicks to open the second item. The first
click hides the first menu item that was open, the second shows the second menu
item.
The "correct" behavior works in the following way:
Clicking a menu item opens it.
Clicking the same menu item (or it's children) closes it.
Clicking another menu item closes the first, opens the second.
Clicking away from (open) menu items closes them.
I have tried the following in place of the above $('body').one() order to ignore clicks on menu items with little success:
// Captures click on menu items in spite of the not.
$('*').not('#menu *').one('click', function() { // Hide menu }
$('*:not(#menu)').one('click', function() { // Hide menu }
As always, thanks for any help!
Just move the body click handler outside and do something like this:
$('body').bind('click', function(e) {
if($(e.target).closest('#menu').length == 0) {
// click happened outside of menu, hide any visible menu items
}
});
It was incorrectly pointed out in the comments that e.target does not work in IE; this is not true as jQuery's Event object fixes these inconsistencies where necessary (IE, Safari).
I wrote this a long time ago, before the glory days of jQuery...
function clickedOutsideElement(elemId) {
var theElem = getEventTarget(window.event);
while(theElem != null) {
if(theElem.id == elemId)
return false;
theElem = theElem.offsetParent;
}
return true;
}
function getEventTarget(evt) {
var targ = (evt.target) ? evt.target : evt.srcElement;
if(targ != null) {
if(targ.nodeType == 3)
targ = targ.parentNode;
}
return targ;
}
document.onclick = function() {
if(clickedOutsideElement('divTest'))
alert('Outside the element!');
else
alert('Inside the element!');
}

Categories

Resources