contextmenu on Safai/OSX getting closed by the click that opened it - javascript

I have a contextmenu over boxes e.g. for Copy/Cut/Paste that is working as expected with Chrome and IE on Windows, but on Safari (OSX) it is not. The contextmenu appears, but as soon as I let go of the mouseclicker (whether over the window or not) it is hidden and no click on its <li>s are caught.
I suspect that what is happening is the initial click (of the control-click?) of the contextmenu event is getting propagated to the $(document) and then closing it. Here are relevent parts of the code. Thanks for any help!
<select class="mySelectionsSelects"></select>
<select class="mySelectionsSelects"></select>
<ul id="custom-menu" style="top: 194px; left: 884px; display: none;">
<li id="customMenuCopy" data-action="copy">Copy</li>
<li id="customMenuCut" data-action="cut">Cut</li>
<li id="customMenuPaste" data-action="paste">Paste</li>
</ul>
event handling:
jQuery(document).ready(function ($) {
$('.mySelectionsSelects').on("contextmenu", function(event) {
event.preventDefault(); event.stopPropagation();
console.log('building context menu');
$("#custom-menu").css({"top": event.pageY + "px", "left": event.pageX + "px"});
$("#custom-menu").show();
console.log('added context menu at top: ' + event.pageY + '/left: ' + event.pageX);
});
// catch click anywhere else, to close it.
$(document).on("click", function(event) {
console.log('caught click on document, hiding custom-menu');
$("#custom-menu").hide();
});
$("#custom-menu li").on("click", function(event) {
event.preventDefault(); event.stopPropagation();
console.log('caught click in li');
// This is the triggered action name
switch($(this).attr("data-action")) {
case "copy": editButton.doCopy(); break;
case "cut": editButton.doCut(); break;
case "paste": editButton.doPaste(); break;
};
// Hide it AFTER the action was triggered
$("#custom-menu").hide(100);
});
});
css:
#custom-menu {
display: none;
z-index: 1000;
position: absolute;
overflow: hidden;
border: 1px solid #CCC;
white-space: nowrap;
font-family: "Open Sans", sans-serif;
font-size: 10pt;
background: #FFF;
color: #333;
border-radius: 5px;
padding: 0;
}
#custom-menu li {
padding: 6px 9px;
cursor: pointer;
list-style-type: none;
transition: all .3s ease;
user-select: none;
}
#custom-menu li:hover {
background-color: #DEF;
}

Your suspicion is correct! Add this line at the beginning of your context menu-closing function:
if (event.ctrlKey) return;
Source: https://stackoverflow.com/a/27973894/7503963 (I didn't have the rep to just comment.)

Related

How to add dynamic listener and get value of target

I've created a custom dropdown and would like to get the text content of the clicked element within.
Dropdown elements are created dynamically as are the event listeners but the listeners seem not to be working correctly.
Dropdown example:
I can see the listeners on each div within the dev tools.
Event listener of child div:
The first div in the dropdown fills the input with it's value but the others do not.
(function() {
let departments = ['Accounting', 'Human Resources', 'IT', 'Warehouse'];
let element = document.getElementById('dd-Department');
departments.forEach(v => {
let div = document.createElement('div');
div.appendChild(document.createTextNode(v));
div.addEventListener('click', () => {
element.parentNode.querySelector('input').value = v;
});
element.appendChild(div);
});
})();
.form-question {
display: flex;
flex-direction: column;
justify-content: center;
margin: 0 0 3rem;
min-height: 3rem;
}
.form-question__title {
color: #342357;
font-size: 1.5rem;
padding: 1rem;
}
.input-container {
border-bottom: solid 2px #333333;
position: relative;
}
input[readonly] {
cursor: pointer;
}
.input-container input {
border: none;
box-sizing: border-box;
outline: 0;
padding: .75rem;
position: relative;
width: 100%;
}
.input-container:focus-within .dropdown {
transform: scaleY(1);
}
.dropdown {
background: #ffffff;
box-shadow: 0 5px 12px #333333;
left: 0;
max-height: 300px;
overflow-y: auto;
padding: 0;
position: absolute;
right: 0;
top: calc(100% + 2px);
transform: scaleY(0);
transform-origin: top;
transition: transform .3s;
z-index: 10;
}
.dropdown div {
border-bottom: 2px solid #777777;
cursor: pointer;
padding: 8px;
z-index: 20;
}
.dropdown div:hover {
background: #dddddd;
font-weight: 800;
}
<div class="form-question">
<div class="form-question__title">
<span>Department</span>
</div>
<div class="form-question--dropdown input-container">
<input type="text" name="Department" readonly="readonly"></input>
<div id="dd-Department" class="dropdown"></div>
</div>
</div>
I also took a stab at event delegation, but could not get the text content of the clicked div. The target is the parent of the intended div, thus the text content was all child values combined.
let element = document.getElementById('dd-Department');
element.addEventListener('click', e => {
if (e.target && e.target.classList.contains('dropdown')) {
e.target.parentNode.parentNode.querySelector('input').value = e.target.textContent;
}
}, true);
Event Delegation on click of child div:
Am I missing something here?
UPDATE
Thank you #dawn for pointing out css as the problem.
I've worked around this by changing
.input-container:focus-within .dropdown
to
.input-container.active .dropdown
and adding the active class with javascript.
document.querySelectorAll('.input-container').forEach(v => {
v.onclick = () => v.classList.toggle('active');
});
Issue now is that on click of anything other than the input-container the dropdown is still active.
The following works but feels like a hack.
document.querySelectorAll('.input-container').forEach(v => {
v.addEventListener('focus', () => v.classList.add('active'), true);
v.addEventListener('blur', () => setTimeout(() => v.classList.remove('active'), 75), true);
});
Are there more elegant solutions?
This situation is a problem with css,When you click on the div,The first thing that triggers is "transform: scaleY(0)" and the ".dropdown" has invisible,so Cannot trigger click event.
Don't use input:focus-within to control the Visibilityof the drop-down box, because when you click the drop-down box, the input has lost focus.

Delete item on the second click on mobile devices (jQuery)

I have a UL with a hover effect on its LIs. When hovering there is a red div appears on the left and when you click on the LI it toggles a class to the text inside of it. It's all ok on the desktop version but when I switch to the mobile version the first tap should activate only the hover effect. Instead of that it acticates the hover and toggle a class immediately. I want to have on mobile version the first tap activates hover and then if it's still active next taps toggle the class. Pls try the code on mobile version it's all clear there what I mean.
Thanks
$('ul').on('click', 'li', function () {
$(this).toggleClass('completed');
});
$('ul').on('click', 'div', function (event) {
$(this).parent().fadeOut(250, function () {
$(this).remove();
});
//prevent event bubbling
event.stopPropagation();
});
.container {
width: 360px;
margin: 0 auto;
border: 1px solid black;
}
ul{
list-style: none;
padding:0;
margin:0;
}
li{
width: 100%;
border: 1px solid black;
display: flex;
}
li div{
background-color:red;
width:0;
transition: 0.1s linear;
display:inline-block;
}
li:hover div{
width: 40px;
}
.completed {
color: grey;
text-decoration: line-through;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<h1>Groceries</h1>
<ul>
<li><div></div><p>carrot</p></li>
<li><div></div><p>onion</p></li>
<li><div></div><p>tomato</p></li>
</ul>
Update: I'm almost got it, got the first toggle on the second tap, the problem is the next toggle occurs on the double tap too:
var isMobile = false;
var isClicked = 0;
$('ul').on('click', 'li', function () {
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera
Mini/i.test(navigator.userAgent) ) {
isMobile = true;
isClicked++;
}
if(!isMobile || isClicked > 1){
$(this).toggleClass('completed');
isClicked = 0;
}
});
First of all disable hover with CSS using #media queries for mobile devices.
Second create a jquery method for tap/click on that at which you want to show red div. you can do something like this
$('li').on('click' , function(){
if($(window).width() <= 760){ // Enter maximum width of mobile device here for which you want to perform this action
// show red div here
}
return false;
});
Also keep in mind #media and $(window).width() should have same pixels/size
EDIT:
try replacing this
var isMobile = false;
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera
Mini/i.test(navigator.userAgent) ) {
isMobile = true;
}
$('ul').on('click', 'li', function () {
if(isMobile){
$(this).toggleClass('completed');
}
});
And let the other code in the click function and try again
You should use touchstart event to handle touch event on mobile but don't forget to check if ontouchstart is available.
$('ul').on('click', 'li', function() {
if ('ontouchstart' in document.documentElement) {
if ($(this).hasClass('hover')) {
$(this).toggleClass('completed');
}
} else {
$(this).toggleClass('completed');
}
});
$('ul').on('touchstart', 'li', function(e) {
var link = $(this); //preselect the link
if (link.hasClass('hover')) {
return true;
} else {
link.addClass("hover");
$('li').not(this).removeClass("hover");
e.preventDefault();
return false; //extra, and to make sure the function has consistent return points
}
});
$('ul').on('click', 'div', function(event) {
$(this).parent().fadeOut(250, function() {
$(this).remove();
});
//prevent event bubbling
event.stopPropagation();
});
.container {
width: 360px;
margin: 0 auto;
border: 1px solid black;
}
ul {
list-style: none;
padding: 0;
margin: 0;
}
li {
width: 100%;
border: 1px solid black;
display: flex;
}
li div {
background-color: red;
width: 0;
transition: 0.1s linear;
display: inline-block;
}
li:hover div,
li.hover div{
width: 40px;
}
.completed {
color: grey;
text-decoration: line-through;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<h1>Groceries</h1>
<ul>
<li>
<div></div>
<p>carrot</p>
</li>
<li>
<div></div>
<p>onion</p>
</li>
<li>
<div></div>
<p>tomato</p>
</li>
</ul>
</div>

StopPropagation allowing click on parent

I have the following code where I have a label where I prevent the default action so that I can instead focus on the an editable span when it is clicked instead of an input. However, if the user clicks on the span, I want it to ignore the click event bound to the parent so I use stopPropagation.
However, it doesn't seem to work and the parent click event is still fired:
var $quantitySpan = $('.quantity-span'),
$quantityTextbox = $('.textbox'),
$quantityHolder = $('.product-checkbox__quantity');
$quantitySpan
.on('click', e => {
e.stopPropagation(); // I thought this would stop the bubbling up to the parents click event
console.log('span clicked');
})
.on('keyup', () => {
$quantityTextbox.val($quantitySpan.text());
})
.on('blur', () => {
const textVal = $quantitySpan.text();
if (isNaN(textVal) || textVal === '') {
$quantitySpan.text("0");
$quantityTextbox.val("0");
}
});
$quantityHolder.on('click', (e) => {
e.preventDefault();
console.log(e.target, e.currentTarget); // this seems to suggest that the target is the label that has been clicked allthough it changes the target to the input and not the span (even though I have prevented the default action of the label and stopped propagation on the span)
});
* {
box-sizing: border-box;
}
.product-checkbox__quantity {
display: block;
padding-top: 1rem;
}
.product-checkbox__quantity-holder {
margin-top: 0.5rem;
display: flex;
flex-direction: row;
border: 1px solid #c6c6c6;
background: #FFF;
border-radius: 5px;
padding: 1rem 1.25rem;
width: 100%;
overflow: hidden;
position: relative;
}
.product-checkbox__quantity-holder .off-screen {
position: fixed;
left: 105%;
top: 125%;
border: 0;
outline: 0;
}
.product-checkbox__quantity-holder .quantity-span {
outline: none;
flex-shrink: 1;
padding-right: 0.5em;
max-width: 80%;
white-space: nowrap;
overflow: hidden;
}
.product-checkbox__quantity-unit {
flex-grow: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<label class="product-checkbox__quantity">
Quantity:
<br>
<span class="product-checkbox__quantity-holder">
<input type="text" name="Quantities[VARIANT2]" autocomplete="off" class="product-checkbox__quantity-input textbox off-screen" id="quantity-variant2" value="0" data-unit="m2" data-rule-required="true" data-msg-required="required" data-rule-integer="true" data-msg-integer="Integers only"><span class="quantity-span" contenteditable="true">0</span>
<span class="product-checkbox__quantity-unit">m<sup>2</sup></span>
</span>
<span class="field-validation-valid" data-valmsg-for="quantity-variant2" data-valmsg-replace="true"></span>
</label>
How do I change the above code so that when you click on the 0, it registers as a click on the span instead of on the label / input (or how do I see I have clicked the span in the label click event)
Weirdly, if you inspect the 0, it says that it is the span, so not sure why the target is changed to the input in the console.log
You also need to add preventDefault() together with stopPropagation().
$quantitySpan
.on('click', e => {
e.stopPropagation();
e.preventDefault();
console.log('span clicked');
})

Not able to remove parent div [duplicate]

This question already has answers here:
Event binding on dynamically created elements?
(23 answers)
Closed 5 years ago.
I am working on a project and there I am getting a value from input tag and then insert input value in a div and appending on screen with children element.
And What I want that when user click on children element then parent div would be removed and for this I'm using a function. That is working when I am using by default a section but when I append a section and then click on that's children where a function call like when user click on it's children parent section would be removed but my functionality not working.
$('#btn').click(function() {
var menuFieldName = $('#text').val();
$('.div').append('<div class="a">' + menuFieldName + '<span>X</span></div>');
$('#text').val('');
});
$('.div .a span').on('click', function() {
$(this).parent().remove();
});
.a {
display: inline-block;
padding: 5px 35px 5px 10px;
position: relative;
background: #eee;
border-radius: 20px;
margin: 10px;
}
.a span {
position: absolute;
top: 0;
right: 0;
background: #333;
color: #fff;
height: 28px;
width: 28px;
text-align: center;
line-height: 28px;
border-radius: 30px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="div">
<div class="a">Test <span>X</span></div>
</div>
<input type="text" id="text">
<button id="btn">Add</button>
https://jsfiddle.net/jafaruddeen/rag71ma0/
You are appending elements dynamically but you are not attaching any event handler to the newly added elements. To solve this you can use event delegation, you can attach events to .div like $('.div').on('click', '.a span', function() {
$('#btn').click(function() {
var menuFieldName = $('#text').val();
$('.div').append('<div class="a">' + menuFieldName + '<span>X</span></div>');
$('#text').val('');
});
$('.div').on('click', '.a span', function() {
$(this).parent().remove();
});
.a {
display: inline-block;
padding: 5px 35px 5px 10px;
position: relative;
background: #eee;
border-radius: 20px;
margin: 10px;
}
.a span {
position: absolute;
top: 0;
right: 0;
background: #333;
color: #fff;
height: 28px;
width: 28px;
text-align: center;
line-height: 28px;
border-radius: 30px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="div">
<div class="a">Test <span>X</span></div>
</div>
<input type="text" id="text">
<button id="btn">Add</button>
The reason you can't remove it is because when you register the event, the element on which you try to register it doesn't exist yet.
You need to use event delegation
$('body').on('click', '#my-element', function(){...});
you have to call click function after append.. or call click function globally(body element) and match the span..
$(document).ready(function(){
$('#btn').on('click',function(){
var menuFieldName = $('#text').val();
$('.div').append('<div class="a">'+menuFieldName+'<span class="close">X</span></div>');
$('.div .a span').on('click', a);
$('#text').val('');
});
$('.div .a span').on('click', a);
function a() {
$(this).parent().remove();
}
});
check your js fiddle. fixed it -> https://jsfiddle.net/jafaruddeen/rag71ma0/

Why is the context menu not working some of the time?

I have created a context menu item but it does not work all of the time. For instance, when I click on pause, it pauses, and when I click play, it plays again, and that happens one more time. After that, it does not do anything when I click on it. Then, it goes back to where it started. Here is my code:
<script>
var x = 1;
</script>
<script>
// JAVASCRIPT (jQuery)
// Trigger action when the contexmenu is about to be shown
$(document).bind("contextmenu", function (event) {
// Avoid the real one
event.preventDefault();
// Show contextmenu
$(".custom-menu").finish().toggle(100).
// In the right position (the mouse)
css({
top: event.pageY + "px",
left: event.pageX + "px"
});
});
// If the document is clicked somewhere
$(document).bind("mousedown", function (e) {
// If the clicked element is not the menu
if (!$(e.target).parents(".custom-menu").length > 0) {
// Hide it
$(".custom-menu").hide(100);
}
});
// If the menu element is clicked
$(".custom-menu li").click(function(){
// This is the triggered action name
switch($(this).attr("data-action")) {
// A case for each action. Your actions here
case "first": alert("first"); break;
case "second": alert("second"); break;
case "third": alert("third"); break;
}
// Hide it AFTER the action was triggered
$(".custom-menu").hide(100);
});
</script>
<script>
function changeContextMenuIn() {
</script>
<style>
/* CSS3 */
/* The whole thing */
.custom-menu {
display: none;
z-index: 1000;
position: absolute;
overflow: hidden;
border: 1px solid #CCC;
white-space: nowrap;
font-family: sans-serif;
background: #FFF;
color: #333;
border-radius: 5px;
padding: 0;
}
/* Each of the items in the list */
.custom-menu li {
padding: 8px 12px;
cursor: pointer;
list-style-type: none;
}
.custom-menu li:hover {
background-color: #DEF;
}
</style>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.js"></script>
<video width="640" id="vidPlayerMain" autoplay="" controls="" onclick="if (x == 1) {document.getElementById('vidPlayerMain').pause(); document.getElementById('pauseContextMenu').innerHTML='Play'; x = 0;} else {document.getElementById('vidPlayerMain').play(); document.getElementById('pauseContextMenu').innerHTML='Pause'; x = 1;}" name="media">
<source src="https://video-lax1-1.xx.fbcdn.net/hvideo-xpa1/v/t42.1790-2/1063787_495053737243964_500266464_n.mp4?efg=eyJybHIiOjYzNSwicmxhIjo3MzZ9&rl=635&vabr=353&oh=d763b8ecfa2a823b9971c497732e4b69&oe=55C8F2C9" type="video/mp4">
</video>
<br />
<ul id='contextMenu' class='custom-menu'>
<li data-action="first">Pause</li>
</ul>
It is because the function to pause and play video is bound to contextMenu's <a> element but function to alert data-action attribute is bound to contextMenu's <li> element and <a> element is not fully occupying width and height of <li> element. (The video does not play/pause at times when you click on context menu button outside of its area occupying text)
One simple solution to this is to change the CSS of these two element such that <a> element is occupying full width and height.
CSS:
/* CSS3 */
/* The whole thing */
.custom-menu {
display: none;
z-index: 1000;
position: absolute;
overflow: hidden;
border: 1px solid #CCC;
white-space: nowrap;
font-family: sans-serif;
background: #FFF;
color: #333;
border-radius: 5px;
padding: 0;
}
/* Each of the items in the list */
.custom-menu li {
cursor: pointer;
list-style-type: none;
}
.custom-menu li:hover {
background-color: #DEF;
}
.custom-menu li a {
padding: 8px 12px;
display:inline-block;
}

Categories

Resources