I'm working on a script to create a custom right-click menu when you click on images inside a contenteditable div. This is the part that does it:
$('.editor-text img').bind('contextmenu', function (event) {
// Do stuff
});
This works on elements already there. But if you move one of the images, thereby making it a newly added dynamic element, jquery can no longer find it. I thought the solution should be this:
$(document).on("contextmenu", ".editor-text img", function (event) {
// Do stuff
});
But this doesn't work either. How can I adjust the code so it will work with dynamic elements?
To see the problem:
Go here: http://jsfiddle.net/YsW8D/4/ using a webkit browser
Right Click an image to see the menu
After closing the menu, click and drag the image somewhere else in the text
Right clicking again will not work.
Line 9 of the js is the related code.
You can try this.
first step: prevent default.
second step: set the position to mouse pointer.
third step: show the menu.
(check the example below.)
function openMenu(e) {
var h1 = document.getElementsByTagName("h1")[0];
var menu = document.getElementById("menu");
e.preventDefault();
menu.style.left = e.pageX + "px";
menu.style.top = e.pageY + "px";
menu.style.display = "block";
document.addEventListener("click", function() {
menu.style.display = "none";
});
}
body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
#menu {
position: absolute;
width: 100px;
height: 100px;
background-color: white;
}
#menu a {
height: 50px;
border: 1px solid black;
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
#menu a:hover {
background-color: #D3D3D3;
}
<!DOCTYPE html>
<html>
<body>
<h1 oncontextmenu="openMenu(event)">Right click on me.</h1>
<div id="menu" style="display: none;">
Google
Stackoverflow
</div>
</body>
</html>
Related
I'm currently developing an e-catalogue. Following the suggestion of a Stackoverflow contributor, I now have a navigation bar with a dropdown that produces a two-container menu. Next, I am trying to set it in such a way that when one hovers over a specific element in one container (the left container), the contents of the other container (the right one) of the dropdown, change and allows the user, interact with the changed contents of the right container.
I am using 'mouseenter' and 'mouseleave' event handlers, and yes, you've guessed it, the moment I leave the element (in the left container) I hovered over to trigger the change (in the right container), the contents of the right container reverse back to their original contents!
To get 'round' this problem, within the mouseenter eventlistener function, I wrote in a second mouseenter eventlistener function, this time for the right container itself. I hoped that if I left the element (in the left container), that triggered the change in the right container, and went into the right container, I would be able to interact with the altered contents of the right container. Alas, it worked it!
The problem now, however, is that ANYTIME I hover over the right container, the contents change as if I had hovered over the specific element in the left container, regardless of whether or not I had actually hovered over that element in the left container.
I have tried numerous approaches, including, 'mouseout' (which 'bubbles'), and also tried giving the two mouseenter event functions, names, so that the 'inner' function for the mouseenter event of the right container only executes when the 'outer' function for the mouseenter event of the element in the left container has executed (a very Pythonistic way of thinking!), but nothing has worked.
I need to keep a mouseout or mouseleave event of some sort for the element in the left container; otherwise, the change in the right container will persist as you move the mouse on to other elements in the left container.
Ultimately, I want each element in the left container to trigger different changes in the contents of the right container, much like what you see in the dropdown here.
A minimal working version of my code is shown below:
// block-1h selectors
const breakingLine = document.querySelector(".breakingline");
const breaking = document.querySelector(".breaking");
// block-2h selector(s)
const block2H = document.querySelector(".block-2h");
const drop2HCaptionText = document.querySelector(".drop-2h-captiontext");
// Event listeners
breakingLine.addEventListener("mouseenter", function() {
drop2HCaptionText.textContent = "Camon C2000 Rotavator";
block2H.addEventListener("mouseenter", function() {
drop2HCaptionText.textContent = "Camon C2000 Rotavator";
})
})
breaking.addEventListener("mouseleave", function() {
drop2HCaptionText.textContent = "Boss Ladderspan 3T Scaffold Tower (Single Width)";
block2H.addEventListener("mouseleave", function() {
drop2HCaptionText.textContent = "Boss Ladderspan 3T Scaffold Tower (Single Width)";
})
})
.nav-list {
list-style: none;
border: 1px solid blue;
display: flex;
flex-flow: row wrap;
align-items: left;
justify-content: left;
color: white;
background-color: #429CD9;
}
#hire-dropdown {
position: absolute;
cursor: pointer;
padding-right: 3em;
padding-left: 3em;
}
.hdrop,
.block-1h,
.block-2h {
display: none;
}
#hire-dropdown:hover * {
display: grid;
}
#hire-dropdown .hdrop {
grid-template-areas: "block-1h block-2h";
grid-template-columns: 1fr 1fr;
}
.block-1h {
grid-area: "block-1h";
height: 30em;
}
.block-2h {
grid-area: "block-2h";
background-color: white;
border: 1px solid black;
height: 40em;
}
.drop-1h-list {
list-style: none;
display: block;
border: 1px solid black;
background-color: white;
height: 40em;
}
.drop-most-popular-hire,
.drop-2h-captiontext {
color: #3D3F41;
}
.drop-most-popular-hire {
padding-left: 3em;
}
<nav>
<ul class="nav-list">
<li>Nothing</li>
<li class="to-hire">
<div id="hire-dropdown">To Hire
<div class="hdrop">
<div class="block-1h">
<ul class="drop-1h-list">
<li>Access</li>
<li class="breakingline"><a class="breaking" href="#">Breaking</a></li>
<li class="compactionline"><a class="compaction" href="#">Compaction</a></li>
</ul>
</div>
<div class="block-2h">
<h3 class="drop-most-popular-hire">Our most popular product in this category</h3>
<p class="drop-2h-captiontext">Boss Ladderspan 3T Scaffold Tower (Single Width)</p>
</div>
</div>
</div>
</li>
</ul>
</nav>
Your (constructive) help will be most appreciated.
You could just remove the mouseleave event listener.
When you enter a li element in the left container, the mouseenter event listener will be fired and the text in the right container will be changed.
The content will be changed to another text only when you enter another element in the left container.
const drop2HCaptionText = document.querySelector(".drop-2h-captiontext");
let texts = [
"Boss Ladderspan 3T Scaffold Tower (Single Width)",
"Camon C2000 Rotavator",
"Boss Ladderspan 3T Scaffold Tower (Single Width)",
]
let listItems = document.querySelectorAll('.drop-1h-list li');
listItems.forEach((item, index) => {
item.addEventListener('mouseenter', () => {
drop2HCaptionText.textContent = texts[index];
})
})
.nav-list {
list-style: none;
border: 1px solid blue;
display: flex;
flex-flow: row wrap;
align-items: left;
justify-content: left;
color: white;
background-color: #429CD9;
}
#hire-dropdown {
position: absolute;
cursor: pointer;
padding-right: 3em;
padding-left: 3em;
}
.hdrop,
.block-1h,
.block-2h {
display: none;
}
#hire-dropdown:hover * {
display: grid;
}
#hire-dropdown .hdrop {
grid-template-areas: "block-1h block-2h";
grid-template-columns: 1fr 1fr;
}
.block-1h {
grid-area: "block-1h";
height: 30em;
}
.block-2h {
grid-area: "block-2h";
background-color: white;
border: 1px solid black;
height: 40em;
}
.drop-1h-list {
list-style: none;
display: block;
border: 1px solid black;
background-color: white;
height: 40em;
}
.drop-most-popular-hire,
.drop-2h-captiontext {
color: #3D3F41;
}
.drop-most-popular-hire {
padding-left: 3em;
}
<nav>
<ul class="nav-list">
<li>Nothing</li>
<li class="to-hire">
<div id="hire-dropdown">To Hire
<div class="hdrop">
<div class="block-1h">
<ul class="drop-1h-list">
<li>Access</li>
<li class="breakingline"><a class="breaking" href="#">Breaking</a></li>
<li class="compactionline"><a class="compaction" href="#">Compaction</a></li>
</ul>
</div>
<div class="block-2h">
<h3 class="drop-most-popular-hire">Our most popular product in this category</h3>
<p class="drop-2h-captiontext">Boss Ladderspan 3T Scaffold Tower (Single Width)</p>
</div>
</div>
</div>
</li>
</ul>
</nav>
I am working with a CMS. In order to show some list content I need a count on the list items to spread them evenly over the page. I did this function:
$(document).ready(function() {
$("button").click(function() {
var count = $("#page_menu ul li").length;
var breit = 945 / count;
$("#page_menu li").css("width", breit);
})
})
#page_menu ul {
margin: 0px;
top: 0px;
}
#page_menu li {
border: 1px solid #fff;
margin: 0 -1px 0 0;
display: flex;
justify-content: center;
text-align: center;
align-items: center;
}
#page_menu ul li {
height: 50px;
float: left;
list-style: none;
font-size: 13px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="page_menu">
<ul class="nav menu mod-list">
<li class="item-121"><a href="/index.php/about/querfloete" >Querflöte</li><li class="item-123"><a href="/index.php/about/autor" >Autor</li></ul>
</div>
<button>change</button>
This does exactly what it is supposed to do - TOO LATE!
To be precise: The page loads and builds the list. The javascript afterwards performs its counting function and releases it to the CSS. However, by then the CSS relating to the list doesn't get the information anymore. The button stays blank and without function regardless if it was placed before the javascript section or after. And it stays that way until I go a level down in the submenu with the same menu. Then it works fine.
And the other thing is, that I would like to load it by itself and not via clicking a button. How can I achieve this?
Sorry, I haven't been working this kind of stuff for the past 10 yrs. I am completely somewhat lost.
Building a website that will contain others’ work. I want to know if my background receives a click, did they click on something like a button or something with a click handler. I know how to stop events from propagating but I do not want to require that in their code. How would you advise I handle the problem to see if they click something inside the doc but want to know if they clicked on an element inside something interactive that already did something in response to the click?
If I'm reading your question correctly, you want to perform an action to certain elements so long as they aren't nested in elements with inherent on click functions?
If that's the case, you could recursively check all the tag types
function handleClick(e) {
const target = e.target;
function recursive(node) {
if (node.id === "stop") return true;
if (["A", "BUTTON"].indexOf(node.tagName) > -1) return false;
return recursive(node.parentNode)
}
console.log(recursive(target));
}
body {
width: 100%;
height: 100%;
background: lightgray;
display: flex;
flex-direction: column;
}
div,
a {
padding: 10px;
box-sizing: border-box;
display: flex;
flex-direction: column;
width: 100%;
height: 100px;
background: gray;
}
a {
background: darkgray;
}
p {
width: 100%;
background: white;
}
button {
width: 80px;
}
<body id="stop" onclick="handleClick(event)">
body
<a>
anchor
<button>buttonA</button>
<p>textA</p>
</a>
<div>
div
<button>buttonB</button>
<p>textB</p>
</div>
</body>
textB should return true since its in a div, while textA should return false since its in an anchor.
I've created a simple modal that is allowed to be closed when you click outside of the content area. This is by design but it has an unintended side-effect. If I click anywhere in the content area (for example in a text field) and drag the mouse to beyond the content area and then release the click it will close the modal. I often have a habit of doing this and I can see how average users will perceive this as a bug so I'm trying to nip it prior to release.
var modal = document.getElementById("modal-container");
function openModal() { modal.classList.add("active"); }
function closeModal() { modal.classList.remove("active"); }
window.onclick = function (event) {
if (event.target == modal)
closeModal();
}
html, body {
margin: 0;
height: 100%;
}
.modal-container.active { top: 0; }
.modal-container {
position: absolute;
top: -500vh;
left: 0;
width: 100%;
height: 100%;
display: grid;
background-color: rgba(0, 0, 0, 0.75);
}
.modal-content {
height: 50%;
width: 50%;
margin: auto;
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
}
<button onclick="openModal();">Open the Modal</button>
<div id="modal-container" class="modal-container">
<div class="modal-content">
<input type="text" />
</div>
</div>
To test it properly:
Click the 'Open the Modal' button.
Click in the text box at the center of the white panel.
Enter some text.
Press the left mouse button down in the text box.
Drag the mouse beyond the bounds of the white panel.
Release the mouse button.
The modal should now be closed.
Is there a way to prevent this without tracking the coordinates of the mouse?
Perhaps onmousedown instead of click?
That worked! Just need more coffee this morning I suppose. Going to write up a thorough answer later today for future readers.
Before you answer yourself with a valid cause (as noted in your Question Edit) -
take in consideration:
onmousedown might not always be the desired UX. (Sometimes experienced users to undo a mousedown not being registered as a click they on purpose move the mouse over another element for the mouseup event just to retain the current state.)
Remove inline JavaScript
Assign listeners using Element.addEventListener() to any button having the data-modal attribute
Use data-modal="#some_modal_id" even no the container element
Finally: use if (evt.target !== this) return;
const el_dataModal = document.querySelectorAll('[data-modal]');
function toggleModal(evt) {
if (evt.target !== this) return; // Do nothing if the element that propagated the event is not the `this` button which has the event attached.
const id = evt.currentTarget.getAttribute('data-modal');
document.querySelector(id).classList.toggle('active');
}
el_dataModal.forEach(el => el.addEventListener('click', toggleModal));
html, body {
margin: 0;
height: 100%;
}
.modal-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: grid;
background-color: rgba(0, 0, 0, 0.75);
opacity: 0; /* ADDED */
transition: 0.26s; /* ADDED */
visibility: hidden; /* ADDED */
}
.modal-container.active {
opacity: 1; /* ADDED */
visibility: visible; /* ADDED */
}
.modal-content {
height: 50%;
width: 50%;
margin: auto;
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
}
<button data-modal="#modal-container">Open the Modal</button>
<div id="modal-container" class="modal-container" data-modal="#modal-container">
<div class="modal-content">
<input type="text">
<br><br>
<button data-modal="#modal-container">CLOSE MODAL TEST</button>
</div>
</div>
This is working example. Think, it matches that one you need))
var clickTarget = null;
var modal = document.getElementById("modal-container");
function openModal() {
modal.classList.add("active");
document.body.addEventListener('mousedown', onModalMouseDown, false);
document.body.addEventListener('mouseup', onModalMouseUp, false);
}
function closeModal() {
modal.classList.remove("active");
document.body.removeEventListener('mousedown', onModalMouseDown);
document.body.removeEventListener('mouseup', onModalMouseUp);
}
function onModalMouseDown(event) {
clickTarget = event.target;
}
function onModalMouseUp() {
if (clickTarget === modal) {
closeModal();
}
}
html, body {
margin: 0;
height: 100%;
}
.modal-container.active { top: 0; }
.modal-container {
position: absolute;
top: -500vh;
left: 0;
width: 100%;
height: 100%;
display: grid;
background-color: rgba(0, 0, 0, 0.75);
}
.modal-content {
height: 50%;
width: 50%;
margin: auto;
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
}
.modal-trigger-btn {
margin: 20px;
font-size: 16px;
}
<button onmousedown="openModal();" class="modal-trigger-btn">Open the Modal</button>
<div id="modal-container" class="modal-container">
<div class="modal-content">
<input type="text" placeholder="Start to drag outside..."/>
</div>
</div>
To answer this question myself, I thought about how the onclick event was working. A click is defined as the mouse button being pressed down, and then released. Both of those points have to occur to cause the onclick event to be raised (though you can't really have one without the other happening at some point before or after).
I haven't found any real documentation on the execution path below so it based on logical deduction. If you have any documentation on this please link it in a comment so that I can review it and adjust my answer for future readers.
User presses down the mouse button.
The onmousedown event is raised.
User releases the mouse button.
The onmouseup event is raised.
The onmouseclick event is raised.
I did write a test up to verify these results:
var ePath = document.getElementById("executionPath");
document.body.onmousedown = function (event) { ePath.innerHTML += "On Mouse Down<br>"; }
document.body.onmouseup = function (event) { ePath.innerHTML += "On Mouse Up<br>"; }
document.body.onclick = function (event) { ePath.innerHTML += "On Click<br>"; }
html, body { height: 100%; }
<p id="executionPath">Click the Window<br></p>
I believe the unintended behavior is caused by when the target is set for the onclick event. I think there are three possibilities (below from most to least likely) for when this is set, none of which I can confirm or deny:
The target is set when the mouse button is released.
The target is set when the mouse button is pressed down, then again when the mouse button is released.
The target is set continuously.
After analyzing my thoughts I determined that for my scenario onmousedown is likely to be the best solution. This will ensure that the modal closes only if the user initiates the click outside of the content area. A good way to couple this with onmouseup to ensure a full click is still achieved is demonstrated below. Though in my case I am okay with simply using onmousedown:
var initialTarget = null;
var modal = document.getElementById("modal-container");
function openModal() { modal.classList.add("active"); }
function closeModal() { modal.classList.remove("active"); }
window.onmousedown = function (event) { initialTarget = event.target; }
window.onmouseup = function (event) {
if (event.target == initialTarget)
closeModal();
}
html, body { height: 100%; }
.modal-container.active { top: 0; }
.modal-container {
position: absolute;
top: -500vh;
left: 0;
width: 100%;
height: 100%;
display: grid;
background-color: rgba(0, 0, 0, 0.75);
}
.modal-content {
height: 50%;
width: 50%;
margin: auto;
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
}
<button onclick="openModal();">Open the Modal</button>
<div id="modal-container" class="modal-container">
<div class="modal-content">
<input type="text" />
</div>
</div>
The snippet above ensures that the click starts and ends on the modal container prior to closing the modal. This prevents the modal from closing if the user accidentally initiates a click outside of the content area and drags their mouse into the content area to complete the click. The same is true in the reverse order, and the modal will only close if the click is initiated and completed on the modal container.
The only thing I can't figure out is when the target for onclick is set which is probably more important in a proper explanation on the root cause of this issue so feel free to let me know!
I am trying to build a simple dropdown plugin for small project of mine. I do not want to use ready plugins, I want to learn by making one on my own.
html:
<div>
<span class="dropdown_triger">press</span>
<div class="content dropdown-closed">
</div>
</div>
css:
span{
display:inline-block;
background: green;
padding: 5px;
}
.content{
position: absolute;
top: 40px;
border: solid 1px black;
width: 200px;
height: 100px;
}
.dropdown-closed { display: none; }
.dropdown-open { display: block; }
and JS:
$(document).ready(function(){
$('body').on('click', '.dropdown_triger', function(e){
var $wrapper = $(this).parent();
var $content = $(this).next();
var $triger = $(this);
if($triger.hasClass('selected')){
$(document).off('mouseup.dropdownDocClick');
console.log('hasClass');
}
$triger.toggleClass('selected');
$content.toggleClass('dropdown-closed dropdown-open');
$(document).on('mouseup.dropdownDocClick',function (e){
console.log('fire');
if (!$wrapper.is(e.target) && $wrapper.has(e.target).length === 0){
if($content.hasClass('dropdown-open')){
$content.toggleClass('dropdown-closed dropdown-open');
$(document).off('mouseup.dropdownDocClick');
}
}
});
});
});
Everything works except for this place:
if($triger.hasClass('selected')){
$(document).off('mouseup.dropdownDocClick');
console.log('hasClass');
}
I expect that mouseup event would not fire anymore but it does. Here is a fiddle, just try it. If I open dropdown, mouseup event is attached to document and keeps firing until I have clicked outside container thus closed dropdown.
But if I close dropdown by clicking again on triger button(span in my example) event is not removed and I can not understand why?