Reputation: 63
I am having troubles with getting a popup to come up at the exact location of my mouse click. I want to be able to click a DIV (that is inside a table cell) and for a popup to show up whenever this div is clicked.
Right now I have the popup working, however no matter what I try the popup DIV is half way down the page.
HTML
<ul class="customdropdown" style="display:none;" id="xxx<?php echo $fetch['unit_no']; ?>" role="menu" aria-labelledby="menuitems">
<li role="presentation"><a role="menuitem" tabindex="-1" href="#"</i>Link 1</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1" href="#"</i>Link 2</a></li>
</ul>
JQUERY
$(document).ready(function () {
$('#myDiv<?php echo $fetch['unit_no']; ?>').click(function (e) {
var myDiv = document.getElementById('myDiv<?php echo $fetch['unit_no']; ?>');
var leftx = e.pageX-myDiv.offsetLeft;
var topy = e.pageY-myDiv.offsetTop;
$("#xxx<?php echo $fetch['unit_no']; ?>")
.css({
position: 'absolute',
left: leftx,
top: topy,
display: 'block'
})
});
});
Screenshot of what is happening below:
Is this to do with the positioning of the DIV?
Upvotes: 2
Views: 13166
Reputation: 206008
It depends whether your popup (tooltip) is position absolute
or fixed
and the parent has any position
other than static
set, and if the popup is inside the wrapper or not; or is a direct child of body
.
Three examples ahead:
<body>
(Best)Case: position: absolute;
popup inside a position: relative;
parent.
overflow
value other than visible
)X = Event.pageX + Parent.scrollLeft - Parent.offsetLeft
Y = Event.pageY + Parent.scrollTop - Parent.offsetTop
// !! Bad practice !!
X = Event.pageX - Parent.offsetLeft
Y = Event.pageY - Parent.offsetTop
Example:
const el = (sel, par) => (par||document).querySelector(sel);
const elArea = el("#area");
const elPopup = el("#popup");
const showPopup = (evt) => {
const elBtn = evt.currentTarget;
Object.assign(elPopup.style, {
left: `${evt.pageX + elBtn.scrollLeft - elBtn.offsetLeft}px`,
top: `${evt.pageY + elBtn.scrollTop - elBtn.offsetTop}px`,
display: `block`,
});
};
elArea.addEventListener("click", showPopup);
body {
height: 300vh; /* Just to force some scrollbars */
}
#area {
position: relative; /* this is needed! */
height: 150px;
background: #eee;
margin: 40px;
}
#popup {
position: absolute;
height: 30px;
background: gold;
display: none;
}
<div id="area">
Click here to shop popup, and scroll the window
<div id="popup">Popup</div>
</div>
Pros:
Cons:
target
- to allow inner content to be interacted with and the popup not to move.SomeClickedElement.append(EL_popup)
.#area
) needs a position
set (other than static
), i.e: position: relative;
Case: position: fixed;
popup outside (or inside) of parent, but usually as child of <body>
.
X = Event.clientX
Y = Event.clientY
Example:
const el = (sel, par) => (par||document).querySelector(sel);
const elArea = el("#area");
const elPopup = el("#popup");
const showPopup = (evt) => {
Object.assign(elPopup.style, {
left: `${evt.clientX}px`,
top: `${evt.clientY}px`,
display: `block`,
});
};
elArea.addEventListener("click", showPopup);
body {
height: 300vh; /* Just to force some scrollbars */
}
#area {
/* position: relative; /* not necessary any more */
height: 150px;
background: #eee;
margin: 40px;
}
#popup {
position: fixed;
height: 30px;
background: gold;
display: none;
}
<div id="area">
Click here to shop popup, and scroll the window
</div>
<div id="popup">Popup fixed</div>
Pros:
#area
) - no additional code is necessary to prevent it from moving if the click initiated inside of it.#area
) does not need position
.Event.clientX
and Event.clientY
is all it takes to move it to the new position.Cons:
Case: position: absolute;
popup as immediate child of body
X = Event.clientX + window.scrollX
Y = Event.clientY + window.scrollY
Example:
const el = (sel, par) => (par||document).querySelector(sel);
const elArea = el("#area");
const elPopup = el("#popup");
const showPopup = (evt) => {
Object.assign(elPopup.style, {
left: `${evt.clientX + window.scrollX}px`,
top: `${evt.clientY + window.scrollY}px`,
display: `block`,
});
};
elArea.addEventListener("click", showPopup);
body {
height: 300vh; /* Just to force some scrollbars */
}
#area {
/* position: relative; /* not necessary any more */
height: 150px;
background: #eee;
margin: 40px;
}
#popup {
position: absolute;
height: 30px;
background: gold;
display: none;
}
<div id="area">
Click here to shop popup, and scroll the window
</div>
<div id="popup">Popup fixed</div>
Pros:
Event.currentTarget
is the entire "body"
) the popup will not change position if its contents are clicked.Event.target.closest("#popup") !== EL_popup
(Close the popup if true
). The same sometimes applies for Context-menus, but rarely for window-centered Modals — which have a dedicated close button.Best practice example:
// DOM utility functions:
const el = (sel, par) => (par||document).querySelector(sel);
// Popup:
let elPopup; // To remember the currently active popup
const handlePopup = (evt) => {
// Get clicked target
const elTarget = evt.target;
// Clicked a popup, do nothing (Comment this line if not needed)
if (elTarget.closest(".popup")) return;
// Close currently open popup (if any):
if (elPopup) elPopup.classList.remove("is-active");
// Get initiator button
const elBtn = elTarget.closest("[data-popup]");
// Not a valid button
if (!elBtn) return;
// Get the popup
elPopup = el(elBtn.dataset.popup);
// No matching popup in this page, do nothing
if (!elPopup) return;
// Get its parent (BODY) so that we can calculate the min max
// available space
const elParent = elPopup.parentElement;
// Position:
const absX = evt.clientX + window.scrollX;
const absY = evt.clientY + window.scrollY;
const bcrParent = elParent.getBoundingClientRect();
const bcrPopup = elPopup.getBoundingClientRect();
const maxX = bcrParent.width - bcrPopup.width;
const maxY = bcrParent.height - bcrPopup.height;
const x = Math.max(0, Math.min(absX, maxX));
const y = Math.max(0, Math.min(absY, maxY));
// Show popup
Object.assign(elPopup.style, {
left: `${x}px`,
top: `${y}px`,
});
elPopup.classList.add("is-active");
};
el("body").addEventListener("click", handlePopup);
/*QuickReset*/ * { margin: 0; box-sizing: border-box; }
body {
min-height: 300vh; /* just to force some demo scrollbars */
}
#area {
background: #eee;
padding: 10px;
}
.popup {
position: absolute;
background: gold;
visibility: hidden;
pointer-events: none;
min-width: 100px;
padding: 1rem;
}
.popup.is-active {
visibility: visible;
pointer-events: auto;
}
<div id="area" data-popup="#popup-one">Show "popup one"</div>
<button data-popup="#popup-two" type="button">Show "popup two"</button>
<br>Click anywhere to close an open popup
<br>Click inside a popup. It will not close ()
<div class="popup" id="popup-one">Popup one!</div>
<div class="popup" id="popup-two">Popup TWO!</div>
Upvotes: 10