Disable contextmenu for a specific container and its children - javascript

I need to disable contextmenu only inside a specific div #wrapperand its children, and not the whole page. This is what I'm doing:
document.addEventListener('contextmenu', function (event) {
console.log(event.target.id);
if (event.target.id === 'wrapper') {
event.preventDefault();
}
});
.. but it doesn't seem to work.

You're approaching this the wrong way: you're adding the listener to the document, which may be ok, but it's easier to add it to the element itself, and you are checking event.target.id, which is the ID of the current clicked element (e.g. a children of your wrapper), not the wrapper.
To make this work you can easily do something like this instead:
var myWrapper = document.getElementById('wrapper');
myWrapper.addEventListener('contextmenu', function(e) {
e.preventDefault();
}, true);

This is a solution that work fine
<div id="wrapper" oncontextmenu="return false">
#wrapper
<div class="childds">
</div>

The code you have in your question works perfectly. One possibility of why the context menu still showed up is that you in fact clicked on a child of #wrapper, instead of clicking on the element itself:
HTML
<div id="wrapper">
#wrapper
<div class="inner">
#wrapper .inner
</div>
</div>
Working example demonstrating this issue on JSFiddle.
You can overcome this by attaching the event handler to the desired element directly instead. This way, right-click events on child-elements will bubble up to #wrapper, and thus fire the event as expected:
JavaScript
document.getElementById('wrapper').addEventListener('contextmenu', function (event) {
event.preventDefault();
});
Working example on JSfiddle.

Related

Handle tab press for any HTML element in jQuery/JavaScript

I'm building a Screen Reader and I'm wanting to navigate through HTML elements on the page using Tab and Shift+Tab presses.
So far, I've got the following code, which works fine:
$('a, input, button').keyup(function(e) {
// code
});
However, the problem I have is the above code will only work for a tags, inputs, and buttons. If I want to add code for a span, or a summary tag, I would have to change the code to:
$('a, input, button, span, summary').keyup(function(e) {
// code
});
I was wondering if its possible to re-write this so that it handles every HTML element. I tried something like:
$('*').keyup(function(e) {
// code
});
But that didn't work. Any help would be appreciated!
Please first read about event propagation. Your event (if not stopped by e.stopPropagation() or e.stopImmediatePropagation()) will be spread to all parents, so it's absolutely enough to do the following.
$(document).keyup(function(e) {
// code
});
Of course you can catch the event on any other level if you need to handle the event on some area but not on whole document. Here's an example of that case when just A1 and A2 buttons wrapped in .containerA div will be handled.
$(function(){
$('.containerA').keyup(function(e) {
if (e.key == 'Tab') {
console.log($(e.target).text());
}
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="containerA">
<button>A1</button>
<button>A2</button>
</div>
<div class="containerB">
<button>B1</button>
<button>B2</button>
</div>
Talking about the tabbing, you probably need to capture on focus event, but that's not clear so I will stop with this immediate answer of your question.
Thanks to #Reflective for their contribution. Using parts of their answer, I've formed the below, which works:
$(document).keyup(function(e) {
if (e.key == 'Tab') {
console.log($(e.target).text());
}
});
You can then use the $(e.target) to access what you want from the current element.

Jquery toggle div, allow clicking outside of div to close, also allow everything inside the div clickable

I have a toggle event on a div, and I've seen many questions regarding mine but nothing seems to work for what I'm trying to do, it either makes my div disappear in a few seconds, or it just doesn't work. Here's my jsfiddle.
I have a div that needs to toggle when another <div> is clicked. The toggled div has inputs in it that need to be filled out, and a submit button inside it as well. So I need clicks inside the div to be allowed, but only inside my div. So I want the div to show unless the user clicks outside of this div.
I'm using this query which toggles fine:
$('#MyDiv').click(function (event) {
$("#ToggledDiv").slideToggle();
});
And then this coding to hide it when clicked outside of the div which doesn't work:
$(window).click(function () {
$("ToggledDiv").hide();
});
I've tried solutions with e.preventDefault(); but that doesn't work, or $(document).click, even mousedown but it just doesn't flow how I want, it'll hide it within a few seconds, or it will prevent the toggle from even working so I'm lost.
The reason behind this behavior is Event Bubbling and Capturing of HTML DOM API. You can use event.stopPropagation() OR event.cancelBubble = true to prevents the event from bubbling up to the DOM tree, preventing any parent handlers from being notified of the event.
Another good article: events order
$('#MyDiv').click(function(event) {
$("#ToggledDiv").show();
disabledEventPropagation(event);
//console.log('2nd event');
});
$('#ToggledDiv').click(function(event) {
disabledEventPropagation(event);
//console.log('3rd event');
});
$(document).click(function() {
$("#ToggledDiv").hide();
//console.log('1st event');
});
function disabledEventPropagation(event) {
if (event.stopPropagation) {
event.stopPropagation();
} else if (window.event) {
window.event.cancelBubble = true;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="MyDiv" style="background-color:yellow">
click me to open
</div>
<div id="ToggledDiv" style="display: none;background-color:yellow">
<input type="text" />
<input type="submit" />
</div>
Take a look at the event when you click inside the #targetDiv. There are two properties you can use to evaluate what action to perform: event.target and event.currentTarget. In this case:
$('#ToggledDiv').on('click', function(event) {
console.log(event.target, event.currentTarget);
});
This is a good way to see if what clicked is actual target or a child element in the target.
To add to Chris' answer, you can see here that I check that the e.target is not inside the form using vanilla Node.contains, and also not the button...
https://jsfiddle.net/jmLdp45s/3/
var $button = $('button');
var $form = $('form');
$button.click(function() {
$form.slideToggle();
});
$(window).click(function(event) {
if (
!$form.get(0).contains( event.target ) // target is not inside form
&& event.target !== $button.get(0) // target is not button
) $form.hide();
});

Is there any way to make these link inside another link work

I have the following markup:
<a class="list-group-item hola" id="competition-card-<%= competition.id %>" href="a_url_to_a_site">
<div class='row'>
<div class='col-xs-11'>
Some text
</div>
<div class='col-xs-1 shows-submenu'>
Some text
</div>
</div>
</a>
I'm trying to achieve:
If user clicks the 'a' tag he is directed to a site, except when...
user clicks the '.shows-submenu' div a hidden div is displayed.
My jQuery is as follows:
$('body').on('click', '.shows-submenu', function(e) {
event.stopImmediatePropagation();
if ($(this).closest('a.list-group-item').next('.hidden-group-item').hasClass('hide')) {
$('.hidden-group-item').addClass('hide');
$(this).closest('a.list-group-item').next('.hidden-group-item').toggleClass('hide');
}
else {
$(this).closest('a.list-group-item').next('.hidden-group-item').toggleClass('hide');
}
});
Currently, if I click '.shows-submenu' div the 'a' tag is still called and I'm directed to the site.
As I'm using bootcards plugin I can't change the 'a' tag to a div, for example.
Is there any way to stop 'a' only when clicking the '.shows-submenu' div. I've tried using stopPropagation in both 'a' and the 'div' without success.
What you are looking for is event.stopPropagation() that will prevent bubbling and event.preventDefault that will avoid the default behavior for links onClick.
Fortunately, in jQuery returning false inside the click handler function will fire both. More on the subject here: https://stackoverflow.com/a/1357151/670377
So you just need to modify your function as:
$('document').on('click', '.shows-submenu', function(e) {
if ($(this).closest('a.list-group-item').next('.hidden-group-item').hasClass('hide')) {
$('.hidden-group-item').addClass('hide');
$(this).closest('a.list-group-item').next('.hidden-group-item').toggleClass('hide');
}
else {
$(this).closest('a.list-group-item').next('.hidden-group-item').toggleClass('hide');
}
// This will be equivalent to
// e.preventDefault; e.stopPropagation;
return false;
});
event.stopImmediatePropagation has another purpose than just stopping the default functionality, it will also prevent all the remaining listeners of the element from being called.
I think what you want is event.preventDefault(). Both event.stopPropagation() and event.stopImmediatePropagation() will affect other event handlers but not the default behavior of an anchor. Also you should declare the argument as event if that's how you're referring to it inside the function:
$('.show-submenu').on('click', function(event) {
event.preventDefault();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="http://example.com">this follows link
<div class="show-submenu">this does not</div>
</a>

Event propagation issue

I have a series of click events which are causing me some propagation issues. The data for this container is loaded in via ajax hence the body on click method. Here's the code I have:
$(function () {
$("body").on("click","#top-div",function(){
console.log("top event");
});
$("body").on("click","#middle-div",function(e){
e.stopPropagation();
});
$("body").on("click","#bottom-div",function(){
console.log("bottom event");
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="top-div">
top
<div id="middle-div">
middle
<div id="bottom-div">
bottom
</div>
</div>
</div>
The top div has an event which the middle one needs to not inherit hence the stopPropagation(). However the bottom one needs to have an event of its own but the stopPropagation() from the middle one is stopping it from executing the event (if I remove the stop propagation the bottom event triggers but coincidentally so does the top)
How can I get around this bubbling issue?
I think what you want to happen is that the propagation is only stopped if it originated from the middle div; if it originated from the bottom div you want the event to bubble all the way to the top.
You need to call stopPropagation conditionally. If the event did not originate on or inside #bottom-div, you want to call it. You can test for this using closest:
if (!$(e.target).closest('#bottom-div').length) {
e.stopPropagation();
}
It is better to use closest for this, rather than testing the element's id directly, because with closest it will continue to work as expected even if there are other elements (a or em, for example) within the various divs.
$(function () {
$("body").on("click","#top-div",function(){
console.log("top event");
});
$("body").on("click","#middle-div",function(e){
if (!$(e.target).closest('#bottom-div').length) {
e.stopPropagation();
}
});
$("body").on("click","#bottom-div",function(){
console.log("bottom event");
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="top-div">
top
<div id="middle-div">
middle
<div id="bottom-div">
bottom
</div>
</div>
</div>
Bubbling works from most nested element to tree root, I think You misunderstanding it in some way. So for example click in bottom-div will:
call event on bottom-div --> call event on middle-div --> call event on top-div.
As I understand the need You want to some how check what div was clicked, so to have control on it just create single event and check clicked target like that:
$("body").on("click","#top-div",function(e){
if ( e.target.id ==='top-div'){
//click in top div
}
if ( e.target.id ==='middle-div'){
//click in middle div
}
if ( e.target.id ==='bottom-div'){
//click in bottom div
}
});
In this proposition You know exactly which div was clicked, but it will always be the most nested one, so target will be middle-div only when will be clicked part of middle-div which is not bottom-div, it will be for example padding space, the same is with top-div it will be target only if click will be in space without child elements.
More about event bubbling You can find in http://maciejsikora.com/standard-events-vs-event-delegation/.
Instead of attaching event to every div. You can consider doing it as follows
check this snippet
$(document).ready(function() {
$("body").on("click", "div", function(event) {
console.log(event.currentTarget.id + "event");
if (event.target.id === "middle-div") {
event.stopPropagation()
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="top-div">
hello
<div id="middle-div">
hi
<div id="bottom-div">
bott-mid-v
</div>
</div>
</div>
Hope it helps

Is there any unclick event in jquery?

I want to style my div using jquery, i want to make it when i click the background become blue, when i unclick(i clicked on other part other than div in my page) the background become red. Sorry for my bad english
With a click handler on document you can capture all the click events, and if the click is not on your div, you can revert to red
$('#yourdiv').click(function() {
$(this).css('background-color', 'blue');
});
$(document).click(function(e) {
if(!$(e.target).is('#yourdiv'))
$('#yourdiv').css('background-color', 'red');
});
See example: http://jsbin.com/oyunin/1/edit
Im not sure about performace, but you can do something like:
$("*").not("your element selector").on("click",function(){
//other elements click
});
a good practice will be doing this:
$("*","some container").not......
if you want to change the div when you click on another div, you can add a click event on the other div, or perhaps you want to add the click event to the entire body of the document so that if a click event is not captured and stopped by another listener, it will go through to the body's event listener.
Try this,
Jquery Code:
$(document).mouseup(function (e){
var wrapDiv = $(".wrapper");
if(wrapDiv.has(e.target).length === 0){
wrapDiv.css('background-color','red');
}
$('.wrapper').click(function(event){
$('.wrapper').css('background-color','blue')
});
});
CSS code:
<style type="text/css">
.wrapper{
background-color:#0FF;
width:200px;
height:200px;
}
</style>
Html code:
<div class="wrapper">
</div>
The way I solved it in my site was adding a click event to the div in question, and another click event to the document in general. The method stopImmediatePropagation() makes it work as expected:
Here's a jsFiddle with a working example.
HTML:
<div class="colored"></div>
JQUERY:
$(document).click(function(e){
$(".active").removeClass("active");
});
$(".colored").live("click",function(e){
$(this).addClass("active");
e.stopImmediatePropagation();
});
CSS:
.colored{background:red}
.colored.active{background:blue}

Categories