Prevent text caret from changing position on contextmenu event - javascript

I am trying to find a way to prevent the text cursor/caret in an editable element from changing position when I right-click on text in the element.
I have an example implementation here:
https://jsfiddle.net/pv20xLsf/52/
This is the HTML
<p id="editable-content" contenteditable=true>
editable text for testing
</p>
<div id="context-menu" style="display: none; position: absolute; height: 100px; width: 50px; border: 1px solid gray; border-radius: 5px; background-color: white;">
context menu.
</div>
and this is the JS code:
const editableElement = document.getElementById('editable-content');
const contextMenu = document.getElementById('context-menu');
// hide the context menu on clicking outside the editable element.
window.addEventListener('mousedown', (event) => {
contextMenu.style.display = 'none';
contextMenu.top = 0;
contextMenu.left = 0;
});
// prevent the default context menu from showing.
window.addEventListener('contextmenu', (event) => {
event.preventDefault();
});
// show a custom context menu when I right-click on the editable element.
editableElement.addEventListener('contextmenu', (event) => {
event.preventDefault();
event.stopPropagation();
contextMenu.style.display = 'block';
contextMenu.style.left = event.clientX + 'px';
contextMenu.style.top = event.clientY + 10 + 'px';
});
My goal is to create a custom context menu which is triggered when a user right-clicks on text in an editable element. When the context menu is displayed, right clicks should not change the location of the text cursor, but when it is hidden, then the location of the cursor should change on right-click.
I noticed that the behaviour is different on different browsers and I would like to get consistent behaviour. Tested on chrome, firefox and safari.

Related

Attach mouse movement to a div with Iframe inside

In my JS code, I've added a EventListener on mousemove & touchmove, which makes it so that my div follows the cursor. (as such)
CSS:
<style>
body {
padding: 0;
margin: 0;
height: 100vh;
background: linear-gradient(135deg, #8fc7f1, #7173f5);
overflow: hidden;
}
#my-div {
width: 300px;
height: 250px;
background-color: #ffffff;
position: absolute;
}
</style>
HTML:
<div id="my-div">
<h1>Hello</h1>
</div>
JAVASCRIPT:
let myDiv = document.getElementById("my-div");
//Detect touch device
function isTouchDevice() {
try {
//We try to create TouchEvent. It would fail for desktops and throw error
document.createEvent("TouchEvent");
return true;
} catch (e) {
return false;
}
}
const move = (e) => {
//Try, catch to avoid any errors for touch screens (Error thrown when user doesn't move his finger)
try {
//PageX and PageY return the position of client's cursor from top left of screen
var x = !isTouchDevice() ? e.pageX : e.touches[0].pageX;
var y = !isTouchDevice() ? e.pageY : e.touches[0].pageY;
} catch (e) {}
//set left and top of div based on mouse position
myDiv.style.left = x - 150 + "px";
myDiv.style.top = y - 120 + "px";
// myDiv.style.opacity = 0;
// console.log(myDiv.getBoundingClientRect())
};
//For mouse
document.addEventListener("mousemove", (e) => {
move(e);
console.log("Mouse")
});
//For touch
document.addEventListener("touchmove", (e) => {
move(e);
});
JSFIDDLE CODE
What I'm trying to do is to add a iframe inside my div and the div should still follow the cursor, but when I add the iframe inside the div, then the EventListener stops working and my div stops following the cursor. I'm not sure what the issue is. (as such)
<div id="my-div">
<iframe src="https://example.com/" width="300" height="250">
</div>
JSFIDDLE CODE WITH IFRAME
Any help and suggestion is appreciated!
You forgot the closing iframe tag
<div id="my-div">
<iframe src="https://example.com/" width="300" height="250"></iframe>
</div>
I think you should change the position of your div to relative and the position of your iframe to absolute.

Listen For Keydown Event On Hovered Element

I would like to detect a control key hit, without losing the user's current focus.
For example (see below) a user is writing something into a textarea, the textarea is the current element focused.
Then my end user moves his mouse on a div (just hovered, not clicked), and if he presses a control key I would like to execute a function (the keyDown ones below).
To make it works I had to add a tabIndex to my div, and I had to uncomment the theDiv.focus() line which makes me sad because it causes a loss of focus on my active textarea.
How can I simultaneously detect if someone hits a key when his mouse is on a specific element, without losing the current element's focus?
var theDiv = document.getElementById("theDiv");
function entered(e) {
theDiv.addEventListener("keydown", keyDown);
//theDiv.focus();
}
function keyDown(e) {
alert(e.key)
}
theDiv.addEventListener("mouseover", entered);
#theDiv {
width: 100px;
height: 100px;
border: 1px silver solid;
}
#theDiv:hover {
border: 1px silver dashed;
}
<div id="theDiv" tabindex="-1">
</div>
<div>
<textarea id="a">Click here, then hover the div above and hold "Shift" key</textarea>
</div>
You can add mouseover event listener to the document to store the element that is being hovered over in a global variable. When there is a keydown event on the document, prevent the default action if the div if being hovered over (so no text will be printed into the textarea).
var theDiv = document.getElementById("theDiv");
var hoverTarget = document.body;
var res = document.getElementById("result");
document.addEventListener("mouseover", function(e){
hoverTarget = e.target;
});
function keyDown(e) {
res.innerText = "Key: "+e.key+" KeyCode: "+e.keyCode;
}
document.addEventListener("keydown", function(e){
if(hoverTarget===theDiv){
e.preventDefault();
keyDown(e);
}
});
#theDiv {
width: 100px;
height: 100px;
border: 1px silver solid;
}
#theDiv:hover {
border: 1px silver dashed;
}
<div id="theDiv" tabindex="-1">
</div>
<div>
<textarea id="a">Click here, then hover the div above and hold "Shift" key</textarea>
</div>
<span id="result">
</span>

How can I make the parent div draggable

I have a div containing three buttons. The div needs to be draggable, so that you can drag all three buttons around the screen together. That works fine, but the problem is that when I click on of the individual buttons it inherits the draggable id and it is draggable on it's own. I do not want that to happen. So my question is: how do I make my buttons draggable, but make them always stay together and keep them clickable. I added the code below, but here is a JSFiddle: http://jsfiddle.net/2ga50vvt/
So to be clear: the div also needs to be draggable through dragging one of the individual buttons, but then the rest of the div needs to stick with it. Now dragging an individual button only moves the button.
P.S. I do not want to use JQuery UI
HTML:
<div id="draggable" class="ui-widget-content">
<button ng-click="menu.shown = !menu.shown">MENU</button>
<br>
<button ng-click="disconnect()">CLOSE</button>
<br>
<button ng-click="">KEYS</button>
</div>
JS
$(document).ready(function() {
var $dragging = null;
$('body').on("mousedown", "#draggable", function(e) {
$(this).attr('unselectable', 'on').addClass('dragged');
var el_w = $('.dragged').outerWidth(),
el_h = $('.dragged').outerHeight();
$('body').on("mousemove", function(e) {
if ($dragging) {
$dragging.offset({
top: e.pageY - el_h / 2,
left: e.pageX - el_w / 2
});
}
});
$dragging = $(e.target);
}).on("mouseup", ".dragged", function(e) {
$dragging = null;
$(this).removeAttr('unselectable').removeClass('dragged');
});
});
CSS:
body {
padding: 50px;
}
.dragged {
background-color: yellow;
}
#draggable {
position: fixed;
width: 150px;
height 150px;
padding: 0.5em;
background: red;
background-color: black;
z-index: 1000;
cursor: move;
float: left;
}
Update 1
This is a working solution: http://jsfiddle.net/2ga50vvt/3/
However when I click on the div and start dragging the center of the div jumps to my cursor. It works great, but it looks a bit wonky. Is there a way to prevent the div from moving to my cursor?
Your help is most welcome.
You can read the target property of the event and return false to avoid all not #draggable to be draggable.
if(e.target.id !== "draggable") {
return false;
}
The edited fiddle:
http://jsfiddle.net/2ga50vvt/1/
It works perfectly, but one suggestion: don't target with ids because with this code you can't drag more of one element (ids must be unique), so the workaround is to write an attribute or a classname and play with it.
Good luck.
Use $dragging = $('#draggable'); instead of $dragging = $('e.target');
It will drag div if you try to drag using cursor on button. It will drag #draggable instead of target.
Working Fiddle
Presuming you're opposed to JQueryUI for it's file size, I'd still recommend a prebuilt solution because why reinvent the wheel?
Draggabilly is a really nifty library that I've used when resource size has been an issue. It's 20k minified (obviously even smaller gzipped) and available on a CDN - which in itself has lots of benefits e.g. caching.
$(function() {
$( "#draggable" ).draggabilly();
});
There's a few CSS hooks, different options, events etc.
JSFiddle here

Trigger specific mouse event on another element

I have a web application containing an image on which a mousewheel event is caught in order to zoom the image precisely on the point where the cursor is.
On top of this image, I display a custom tooltip which is just a div containing text. When I mousewheel on the tooltip, the image is not zoomed.
I tried to bind the image mousewheel event on the tooltip but then, the offsetX and offsetY of the event correspond to the cursor position relative to the tooltip and not relative to the image.
How can I:
either get the image offsetX and offsetY, even when the cursor is over the tooltip? or
prevent the tooltip to catch the mousewheel event before the image?
EDIT:
Actually, Before, I could select the tooltip by clicking on it; but by adding the CSS style 'pointer-events:none' (suggested by Aramil Rey), the click event has no effects (obviously).
Therefore, how can I:
prevent the tooltip to catch the mousewheel event before the image?
and still let the tooltip catch the click event?
Did you try using CSS: pointer-events:none; on the tooltip?
JS Fiddle
Try hovering on the red div, and you will notice that it won't trigger its attached event, and intead it will trigger yellow div on mouseeenter event if you enter that div, even if your cursor is over the red div.
$('.asd').on('mouseenter', function() {
alert('asd');
});
$('.ddaa').on('mouseenter', function() {
alert('ddaa');
});
.ddaa {
padding-top: 15px;
background-color: yellow;
}
.asd {
position: absolute;
width: 100%;
pointer-events:none;
padding-top: 10px;
top: 5px;
background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="ddaa">
</div>
<div class="asd">
</div>
I assume currently you are getting offsetX and offsetY from event.target. Set an ID to the image and replace event.target with $("#imageID")[0]. Better yet, jquery provides an offset function.
var image = $('#image');
image.add('.tooltip').bind('mousewheel', function (event) {
event.preventDefault();
var offset = image.offset();
var mouseX = event.pageX - offset.left;
var mouseY = event.pageY - offset.top;
// zoom stuff
});

jQuery dialog - div positioned on top of dialog box inputs disabled

The problem is this:
A dialog box is opened. I click the input field of the dialog box to open a div that is absolutely positioned over the dialog box. The input cannot be interacted with.
Here is the example on Jsfiddle
You can mouse over the input and the mouse cursor will change to the 'I' icon. You can even interact with the close anchor tag on the absolutely positioned div. It's not a z-index issue. It works in jQuery UI 1.9 and older. Strangely, after the absolute div has been added to the DOM, if you append an empty div to the end of the body html (using firebug to edit the code realtime), the input works.
Any ideas?
Thanks in advance,
Bontke
$('#open_dialog').click(function (event) {
var dialog_html = '<div id="dialog_body">' +
'<input type="text" name="test1"/>' +
'</div>';
$(dialog_html).dialog({
title: 'WTF Test',
height: 110,
width: 300,
modal: 'true',
close: function () {
$(this).remove();
$('#test_div').remove();
}
});
//dialog input click
$('input[name=test1]').click(function (event) {
var html = $('<div id="test_div" style="border: 1px solid red; z-index: 10000; position: absolute; left: 45%; top: 60%; width: 235px; height: 100px; background-color: blue;"><input name="foobar"/><a id="test_close" style="color: white;" href="#">close</a><br/><span style="color: white">super awesome text</span></div>'),
body = $(document.body);
if ($('#test_div').length === 0) {
//append div to body
html.appendTo(body);
//add close functionality to test_div
$('#test_close').click(function (event) {
event.preventDefault();
//remove test_div from DOM
$(event.currentTarget).parent().remove();
});
}
});
});
The dialog_html dialog is set to modal: 'true' which means it will deactivate everything else on the page. If you remove this it removes any problems. I think you're getting mixed results because you're adding to the DOM after jQuery has made the dialog modal and you really shouldn't be able to interact with the second popup at all, but it is breaking. You may want to try making the second pop-up modal, or adding it as a child of the first dialog rather than appending it to document.body
Sorry for the delayed reply, here is the solution:
http://jsfiddle.net/aY9ms/5/
I want the dialog box to be a modal. You are right, the adding the DOM is the because of how dialog box works. And in my case, it is better to add any html to the dialog box parent for better memory clean up. And adjusting the overflow on the dialog box allows the div to float over the dialog box as I want.
Thanks for all of the feedback and help!
var $ = $j;
var dialog_html = '<div id="dialog_body">' +
'<input type="text" name="test1"/>' +
'</div>';
$(dialog_html).dialog({
title: 'WTF Test',
height: 110,
dragable: true,
width: 300,
modal: 'true',
close: function () {
$(this).remove();
$('#test_div').remove();
}
});
//dialog input click
$('input[name=test1]').click(function (event) {
var html = $('<div id="test_div" style="border: 1px solid red; z-index: 10000; position: absolute; left: 45%; top: 60%; width: 235px; height: 100px; background-color: blue;"><input name="foobar"/><a id="test_close" style="color: white;" href="#">close</a><br/><span style="color: white">super awesome text</span></div>'),
dialog_box = $('#dialog_body').parent(),
body = $(document.body);
//adjust css
dialog_box.css({'overflow': 'inherit'});
if ($('#test_div').length === 0) {
//append div to body
//html.appendTo(body);
html.appendTo(dialog_box);
//add close functionality to test_div
$('#test_close').click(function (event) {
event.preventDefault();
//remove test_div from DOM
$(event.currentTarget).parent().remove();
});
}
});

Categories