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.
Related
i'm working on a small project where textarea are used.
And the problem with textarea is that the text always start at the top left corner...
And what i want is to have the content inside of that textarea always centered. And then if I add a line it'll expend from the middle, something like this :
I looked for thing like vertical align and thing like that, but i also saw things about inserting a div... But i was wondering if there was a solution with textarea
Thanks !
I don't think you can align the text in textarea vertically, but you can fake it!
HTML and CSS:
Create a resizable div
Nest a contenteditable p in that div
Add a placeholder for the p when it's empty
Use flexbox to set the p in the middle: align-items: center;
Add p {white-space: pre-line;} to prevent the overflow when typing
Add p {outline: 0px solid transparent;} to remove the border on focus
JavaScript:
Get the target (p)
Add an event on keypress
Check for pressing Enter
Prevent the default event and insert a line break (new line) instead
Add an event on click to the div to focus the p (textarea behavior)
// Get the target
let p = document.querySelector('p');
let div = document.querySelector('div');
// Add event onkeypress
p.addEventListener('keypress', (e) => {
let keyCode = e.code || e.key;
// Check for pressing 'Enter'
if(keyCode === 'Enter') {
// Prevent the default event and insert line break (new line) instead
document.execCommand('insertLineBreak');
e.preventDefault();
}
});
// Add event onclick
div.addEventListener('click', () => {
// Focus on p to get the textarea behavior
p.focus();
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
div {
resize: both;
width: 200px;
height: 150px;
border: 1px solid;
overflow: auto;
margin: 30px auto;
display: flex;
align-items: center;
cursor: text;
}
p {
white-space: pre-line;
width: 100%;
height: fit-content;
outline: 0px solid transparent;
}
p:empty::before {
content: attr(placeholder);
color: #666666;
}
<div>
<p contenteditable="true" placeholder="Type here..."></p>
</div>
I'm using a CSS framework for Blazor WebAssembly called Mudblazor.
I have added a button inside the drag and drop zone that will remove each image that has been uploaded. But when I click on the remove button, I only get the file manager up.
The problem is that the actual drag and drop zone is set to position: absolute none.
Is there a way to solve this?
Example of what this looks like. It is not possible to click on the remove button. File manager appears when I try to click the remove button.
CSS:
.drag-drop-zone {
display: flex;
align-items: center;
justify-content: center;
transition: all .4s;
/* min-height: 400px;
*/ border: 3px dotted;
min-height: 100px;
border: 2px dashed rgb(0, 135, 247);
}
.drag-drop-input {
position: absolute;
width: 100%;
height: 90%;
opacity: 0;
cursor: pointer;
z-index: 2;
}
.drag-enter {
box-shadow: var(--mud-elevation-10);
}
.list {
padding: 2em;
min-width: 100%;
}
Razor
<MudList Style="padding:2em;width:100%;" Dense="true">
#foreach (var file in fileNames)
{
<MudListItem #key="#file">
<MudChip Color="Color.Dark"
Style="width:60px; overflow:hidden;"
Text="#(file.Split(".").Last())" />
#file <MudButton Color="Color.Error" OnClick="() => Remove(file)" Style="position:unset;">Remove</MudButton>
</MudListItem>}
Remove method:
void Remove(string file)
{
var ret = fileNames.FirstOrDefault(x => x.Contains(file));
if (ret != null)
{
fileNames.Remove(ret);
}
}
I had the same problem when using the sample code from MudBlazor home page
https://mudblazor.com/components/fileupload#drag-and-drop-example
As Memetican states it seemed like the .drag-drop-input was overlaying the other content. I dont know what I am doing, so I will not give an explanation but the position: absolute; part caught my interest. Reading about it here led me to changing it from absolute to static. Together with some other minor changes from the sample code I have the following that seems to be working
#page "/tools/csvimport"
#inject ISnackbar Snackbar
<style>
.drag-drop-zone {
display: flex;
align-items: center;
justify-content: center;
transition: all .4s;
height: 100%;
}
.drag-drop-input {
position: static;
width: 100%;
height: 100%;
opacity: 0;
cursor: pointer;
z-index: 2;
}
.drag-enter {
box-shadow: var(--mud-elevation-10);
}
.list {
padding: 2em;
min-width: 100%;
}
</style>
<MudGrid Justify="Justify.Center" Spacing="4">
<MudItem xs="12" sm="12" md="6">
<MudPaper #ondragenter="#(()=>_dragEnterStyle="drag-enter")"
#ondragleave="#(()=>_dragEnterStyle=null)"
#ondragend="#(()=>_dragEnterStyle=null)"
Class=#("drag-drop-zone "+ _dragEnterStyle)
MinHeight="200px">
<InputFile OnChange="OnInputFileChanged" class="drag-drop-input" />
#if (file is null)
{
<MudText Typo="Typo.h6">Drag file here, or click to browse your files</MudText>
}
else
{
<MudChip Color="Color.Info">#file.Name</MudChip>
}
</MudPaper>
<MudGrid Justify="Justify.Center" Spacing="4" Class="mt-4">
<MudItem>
<MudButton OnClick="Upload" Disabled="#(file is null)" Color="Color.Primary" Variant="Variant.Filled">Upload</MudButton>
</MudItem>
<MudItem>
<MudButton OnClick="#(() => file = null)" Disabled="#(file is null)" Color="Color.Error" Variant="Variant.Filled">Clear</MudButton>
</MudItem>
</MudGrid>
</MudItem>
</MudGrid>
#code {
string _dragEnterStyle;
IBrowserFile file;
void OnInputFileChanged(InputFileChangeEventArgs e)
{
file = e.File;
}
void Upload()
{
//Upload the files here
Snackbar.Configuration.PositionClass = Defaults.Classes.Position.TopCenter;
Snackbar.Add("TODO: Upload " + file.Name, Severity.Normal);
}
}
I think you've left out the code for your drop target, as most of your CSS styles aren't referenced.
However, it appears that your drop target is positioned on top of your files list. Note the z-index in your CSS...
.drag-drop-input {
position: absolute;
width: 100%;
height: 90%;
opacity: 0;
cursor: pointer;
z-index: 2; /* <== here */
}
This would create a glass-pane type effect. You can see your files list and remove buttons, because you have opacity: 0; - however you can't interact with them because your drop target is above them in the z-order.
Think of an HTML page as a bunch of overlapping rectangles. Mouse interactions hit the topmost layer only.
Solution 1: (workable) Remove that z-index, or set the z-index for your files list and your buttons higher than the drop target. This should work however it may create undesirable behaviors on some browsers, if the user tries to drop a file directly on your file list, or your remove buttons.
Solution 2: (recommended) Move your file list and remove buttons outside of the drop target. Design your drop target do its one function- accepting file drops, and mouse clicks that invoke the file upload action.
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'm trying to change the text from black to blue when I click. I have it as an "if" statement and I know it doesn't need to be an "if" statement, but I'm just wondering why it doesn't work. I'm just asking, "If this color is black, change it to blue."
var myvar = document.getElementById('thisdiv');
myvar.onclick = function myfunction() {
if (myvar.style.color == "#000000") {
myvar.style.color = "#0000FF";
}
}
.mydiv {
height: 200px;
width: 200px;
background-color: red;
color: #000000;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
<div class="mydiv" id="thisdiv">Click Me</div>
JSFiddle
I'm not really looking for a solution to make it go from black to blue as I'm sure there's other ways I could figure out to do it, but more just wondering why the way I have it isn't working.
style only holds styles that were added using the style attribute in the HTML. It doesn't contain the computed style from applying <style> tags or stylesheets.
To get the computed style, use window.getComputedStyle(). Note this is read-only.
var myvar = document.getElementById('thisdiv');
myvar.onclick = function myfunction() {
if (window.getComputedStyle(myvar).color === "rgb(0, 0, 0)") {
myvar.style.color = "#0000FF";
}
}
.mydiv {
height: 200px;
width: 200px;
background-color: red;
color: #000000;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
<div class="mydiv" id="thisdiv">Click Me</div>
A better strategy though is to add or remove CSS classes to elements, so that you get separate presentation from functionality.
Okay, so the reason the code is not working is because myvar.style.color is undefined. When you use a stylesheet, it doesn't affect the JavaScript style properties, which should be the same as the styles defined in the style attribute of the element. What you want is something like the following.
// undefined!="#0000ff", success
if(myvar.style.color!="#0000ff") {
myvar.style.color = "#0000ff";
}
If you want to use JQuery, you can try something like the following.
// JQuery always returns opaque colors in rgb(r, g, b) form
// regardless of the original format.
if($(myvar).css("color")=="rgb(0, 0, 0)") {
myvar.style.color = "#0000ff";
}
I strongly suggest using an additional class for this, to account for its state.
var myvar = document.getElementById('thisdiv');
myvar.onclick = function myfunction() {
/* if (myvar.className === ""){
myval.className = "active";
} else {
myvar.className = "";
} */
// Same as below, toggle the class of #thisdiv from active to blank
myvar.className = myvar.className === "" ? "active" : "";
}
.mydiv {
height: 200px;
width: 200px;
background-color: red;
color: #000000;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.mydiv.active{
color: #0000ff;
}
You could also probably make use of the :visited pseudo selector and get rid of the click function entirely. You'd need to make your div one big link though. Something like this;
<a class="mydiv" id="thisdiv">Click Me</a>
.mydiv{
display:block; /* since it's a link and will be inline by default */
}
.mydiv:visited{
color: #0000ff;
}
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>