preventDefault but activate links - javascript

In my navigation menu, I have a dropdown that I want to use. The actual dropping down is fine, and I've prevented the automatic bubbling by using preventDefault(); but now all the links within the dropdown no longer work.
How do I make it so that the dropdown works, doesn't bubble and all the links within the dropdown work?
Edit: I've also used event.stopPropagation() to no effect either. What's going on here?!
This is my code:
// Toggle dropdowns
$('.menu-item-has-children').click(function(e){
e.preventDefault();
$(this).find('.sub-menu').toggleClass('open');
});

To stop bubbling, use event.stopPropagation().
Only use event.preventDefault() to prevent the default action of the event from happening.
Ah, now I see your problem. The issue is that when clicking a menu item to open a submenu, since the item is an anchor pointing to #, the document will scroll to top.
To avoid that, I suggest getting rid of href="#".
Alternatively, you can use preventDefault only if the clicked element was that element, not a descendant:
$('.menu-item-has-children').on('click', function(e){
if(this == e.target) e.stopPropagation();
// ...
});
Demo

You can check which element was clicked by using e.target, and if the clicked element was a sub menu link, don't preventDefault

Related

Hide context menu on outside click

I have a context menu that appears when you right click inside the <div>, and it only goes away if the user click left click inside the <div> again.
How do i hide it when a user click anywhere on the page?
my fiddle
change these two functions to the following:
_onPageClick: function(e) {
e.stopPropagation()
if (this.refs.contextMenu.getDOMNode() !== e.target){
this.contextMenu.setState({contextMenuLocation: ''});
}
},
componentDidMount: function(){
this.contextMenu = this.refs.contextMenu;
document.addEventListener('click', this._onPageClick)
},
all we have to do is to move _onPageClick from the wrapper div to a listener on the document. the above code will close the menu if the user clicks anywhere that is not inside the context menu. if you want it to close if the user clicks the context menu as well, then change _onPageClick to:
_onPageClick: function(e) {
e.stopPropagation()
this.contextMenu.setState({contextMenuLocation: ''});
}
(also, the wrapper div should no longer have the onClick handler)
http://jsfiddle.net/yikevinqu/eeu9unhm/1/
Check out Ben Alman's clickoutside jQuery plugin. Even if you are not using jQuery, you can review his mechanism for catching these click events as they bubble up.
http://benalman.com/projects/jquery-outside-events-plugin/
All click events get bubbled up through the DOM, so if you click an inner element, if you don't event.stopPropagation(), it will bubble up to the parent element. So just catch the click on the parent element (can even be document to hide your context menu).
Check out my fiddle for a pure JS example: http://jsfiddle.net/jsc8zLaj/
There's actually an existing React mixin on npm you can use for this:
https://github.com/Pomax/react-onclickoutside
Since mixins have fallen out of favour now, you may want to implement it as a wrapper component instead, but this is an excellent starting point.

Attach Click Event to HTML after Menu Click

I've got a dropdown menu on a site I'm working on that has different classes added to it when it is activated. It's built into a WordPress theme so I'm having to build a workaround on it for iPad landscape mode. The site navigation displays the same as desktop when on landscape so I need to create some touch events to mimic the behavior.
The first thing I'm having to do is to deactivate the link on the first click and then reactivate it if it's clicked again. This works fine.
if ('ontouchstart' in document.documentElement) {
$('#masthead nav ul > li:has(.sub-menu) a').click(function(e){
e.preventDefault();
$('li.clicked_link').on('click', function(){
var this_link = $(this).find('> a').attr('href');
document.location.href=this_link;
});
$(this).parent().addClass('clicked_link');
});
}
The problem is that when you click on the li.sub-menu it adds a class of sub-hover onto it which activates a dropdown menu. It's a CSS/jQuery dropdown menu so it's based on the presence of that sub-hover class. It works fine when I first click but I then want to remove that class when you click outside of it. It deactivates it if I click another link in the dropdown but I'd like to bind a click event to the html or body elements so if you click anywhere outside of it, it will remove the sub-hover class, thus removing the dropdown.
I tried to do this:
if ('ontouchstart' in document.documentElement) {
$('#masthead nav ul > li:has(.sub-menu) a').click(function(e){
e.preventDefault();
$('li.clicked_link').on('click', function(){
var this_link = $(this).find('> a').attr('href');
document.location.href=this_link;
});
$(this).parent().addClass('clicked_link');
$('html').on('click',function(){
$('#masthead nav ul > li:has(.sub-menu)').removeClass('sub-hover').removeClass('clicked_link');
});
});
}
It doesn't work though because it seems to fire the first click function and then immediately fire the html click function. I tried to use bind as well but the same thing happened. I also tried it outside of the initial click function but, as you can guess, it fires the two click events simultaneously which doesn't work.
How do I get it to bind but not fire the click event only after the initial click event takes place?
This is not a direct fix for the problem, but something that might help you fix the problem (Too long for a comment).
You are binding click handlers inside another click handler without unbinding the previous ones, So everytime you click a matching <li> new click handlers are being added to <html> and other matching elements. I strongly believe you're not doing it on purpose and isn't aware of it.
Leaving that, You're trying to bind a click for .clicked_link using
$('li.clicked_link').on('click', function(){
This code looks for matching elements currently present in DOM, and probably finds no matching elements since you're actually adding the class clicked_link after it.
You actually need to delegate this handler to check for matching elements in future.
The events are bubbled up till the root element, in other words: you if you click an anchor, starting from the anchor, all the parents till html will receive a click event, which you can prevent using e.stopPropagation();.
Ideally you're code should look something like this:
if ('ontouchstart' in document.documentElement) {
$('#masthead nav ul > li:has(.sub-menu) a').click(function(e){
e.preventDefault();
$(this).parent().addClass('clicked_link');
});
$(document).on('click','li.clicked_link', function(){
var this_link = $(this).find('> a').attr('href');
document.location.href=this_link;
});
$('html').on('click',function(){
$('#masthead nav ul > li:has(.sub-menu)').removeClass('sub-hover').removeClass('clicked_link');
});
}
(Can't go any further with the currently available info. Minimal code such as respective html structure or a demo would be helpful)

How to prevent click event to trigger show/hide?

There is a clickable layer. On click it reveals/hides some extra content. Within this layer there is a link which triggers another page to load in the browser.
When this link is clicked the clickable layer is clicked too because it contains the link. How can I avoid that?
I want the link to work but while the user clicks on it the extra content should not be shown.
I tried with
$('.link').click(function(event){
return false;
});
but this disables both hide/show and the link to work. Any ideas? Here is my fiddle: http://jsfiddle.net/ZkPLD/
Use stopPropagation to avoid events bubbling up:
$('.link').click(function(event){
event.stopPropagation();
});

The best way to do :not in jQuery?

I have a menu in jQuery when you click on a link it opens up, but I want it so when you click somewhere else, anywhere else that is not the menu, it becomes hidden.
At the moment I'm binding a click event to
$(':not(#the_menu)')
But this seems like I'm binding a click event to the entire minus the menu, is there a more efficient way of doing something like this?
The best way to do this is with bubbling capture, like this:
$(document).click(function() {
//close menu
})
$("#the_menu").click(function(e) {
e.stopPropagation();
});
How this works is every click bubbles (unless you stop it, usually by return false; or event.stopPopagation()), so whatever you click bubbles all the way up to DOM...if a click does that, we close the menu. If it came from inside the menu, we stop the bubble...so the click doesn't bubble up, triggering a close. This approach uses only 2 event handlers instead of 1 on everything but the menu, so very lightweight :)
Attach event to document's body ($(body)). Also attach another event to #the_menu that's block event propagation:
$(document.body).click(function() {
//close menu if opened
});
$("#the_menu").click(function(e) {
//code heere
e.stopPropagation();
});
How about binding the menu display to hovering over the element in which it is contained?
$("#parentId").hover(
function() { //!! on hovering in
$("#targetId").attr("display","block") ;
} ,
function() { //!! on hovering out
$("#targetId").attr("display","none") ;
}
) ;
If it fits your goal, this seems easier to maintain.

Is it possible to have jQuery.click trigger on the top element only?

I'm trying to make a site where the user can click on any element to edit it's CSS. I use the following to add the click function to all <li>, <div> and <ul>.
$('li,div,ul').click(function () {
alert(this.id);
});
The problem is if I click on a <li> element, then I get the alert for that and any element underneath it. (all the containers).
Is it possible to have only the top element trigger when clicked?
You want to stop event propagation, you do this in jQuery by calling the stopPropagation method on the event object.
$('li,div,ul').click(function (e) {
e.stopPropagation();
alert(this.id);
});
I believe you'd want to use stopPropagation(); inside the click function.
It sounds to me like you're looking for .stopPropagation(). Calling stopPropagation will prevent the event from "bubbling" up to parent containers.

Categories