Separate touch events of parent and child - javascript

I use jquery for my web application. I want it to be correct for desktop browsers and mobile brousers for touchscreen devices.
I have a div, and some elements inside it:
<div class="well listItem element-div alert-error" data-state="removing">
<strong>Item title</strong> <small>Items count</small>
<div class="pull-right" style="margin-top: -5px;">
<a class="btn btn-success approve-button"><i class="icon-ok icon-white"></i></a>
<a class="btn btn-danger cancel-button"><i class="icon-remove icon-white"></i></a>
</div>
</div>
I catch click and touchend event for .listItem class (top-level div) and same events for every a element (for .approve-button and .cancel-button), but when I'm click on desktop browser on 'a' element, it works correct, and when I am pressing on 'a' element in iOS Safari browser, or WindowsPhone InternetExplorer, works only event for parent div, but not for 'a'. If I remove event listener for parent div, events for 'a' elements works correct in mobile browsers. I want parent-div event works when I touch a free space of it, and when I touch 'a' element - I want only 'a' event listener to go on. Can you advise me how to separate them?

Have you tried to check event target?
$(".listItem").on("click", function(event){
if (event.target === this) {
// clicked exactly on this element
}
});

I've had a similar problem, only my content was more nested. You want to exclude the areas (divs, classes or otherwise) where you expect to handle other events, using :not selector, like so:
<article>
<div class="title">
<span class="title"></span>
<div class="buttons">
<div class="minimize">+</div>
<div class="remove">x</div>
</div>
</div>
<div class="post">
...
</div>
</article>
With jQuery:
$("article :not(.buttons, .minimize, .remove)").click(function (event) {
// toggle display value of div.post
});
This triggers a click event anywhere inside article, except for the .buttons, .minimize and .remove divs.

Related

Event delegation does not capture all inner elements

I am using event delegation on my project but for some reason it does not works as expected.
It may seem like a duplicated question but searching for days I have not found the solution so it is not as clear, even in a course I am taking at UDEMY this is not addressed.
The html structure is like this:
<div class="users-list">
<a class="useruid" id='. $row['unique_id'] .'>
<div class="content">
<img src="php/images/'. $row['img'] .'" alt="">
<div class="details">
<span>'. $row['fname']. " " . $row['lname'] .'</span>
<p>'. $you . $msg .'</p> </div>
</div>
<div class="status-dot '. $offline .'"><i class="fas fa-circle"></i>
</div>
</a>
</div>
The vars with the dollar sign are php variables and everything inside "users-list" <div> is added dynamically to the DOM (that part works well).
The problem comes when handling the event listener in javascript as follows:
const ulist = document.querySelector(".users-list");
ulist.addEventListener("click", (e) => {
e.preventDefault();
console.log(e.target);
if (e.target.classList.contains("useruid")) {
console.log(e.target.id);
}
});
I need to get the id number inside the element to use it in another part of the program but it will only be captured if I click on the outer boundaries of the box and most of the time only the <span> and the <p> elements are the ones that will capture the click.
What do I'm missing here, isn't the click suppose to bubble up all the way up passing through the <a> element not matter where I click in that box?
I've searched on other questions here and everywhere online for days but can't find a clear solution. Maybe my approach is incorrect, I don't know really.
Adding additional clarification
using this has the same problem:
if (e.target.matches("a.useruid")) {
console.log(e.target.id);
}
What do I'm missing here, isn't the click suppose to bubble up all the way to the <a> element not matter where I click in that box?
No. It bubbles up to <div class="users-list"> because that is where the event listener is bound.
The target is the element that triggered the event.
You need to change your logic from does the clicked element have the class to does the clicked element or one of its ancestors have the class.
You can do that with closest.
const ulist = document.querySelector(".users-list");
ulist.addEventListener("click", (e) => {
e.preventDefault();
const anchor = e.target.closest(".useruid");
console.log(e.target);
console.log(anchor)
if (anchor) {
console.log(anchor.id);
}
});
<div class="users-list">
<a class="useruid" id='one'>
<div class="content">
<img src="//placekitten.com/100/100" alt="">
<div class="details">
<span>foo</span>
<p>bar</p>
</div>
</div>
</a>
</div>
The target will be the most nested element that has been clicked, So, it maybe a child element to .useruid element.
I think you need to use closest
const ulist = document.querySelector(".users-list");
ulist.addEventListener("click", (e) => {
e.preventDefault();
console.log(e.target);
if (e.target.closest(".useruid")) {
console.log(e.target.id);
}
});
e.target represents the element event was called upon. If you click on span, your e.target is that span.
To solve the problem, you first have to check if the event was called on anchor tag.
const myTarget = e.target.matches("a")? e.target : e.target.closest("a")

onclick and ontouchstart not working on mobile for any element

I have been working on a website and I use onclick to open the navigation but when I tried it on mobile it didn't work, it just did the :hover animation. I then added ontouchstart to the div and it still doesn't work on mobile. When I tap it it acts like I hover over it. I've looked at a few articles on why it might not work but I can't figure it out. Also any element that uses onclick doesn't work on mobile. Also the js is a separate file (not sure if this would for some reason affect it).
<div id="navWrap" onclick="openNav()" ontouchstart="openNav()">
<div class="navLine"></div>
<div class="navLine" id="navMid"></div>
<div class="navLine" id="navBottom"></div>
</div>
function openNav() {
document.getElementById("mySidenav").style.width = "100%";
}
Edit: More code
<div id="headWrap">
<div id="logoWrap"><img src="/static/images/logo.svg" id="logo"></div>
<div id="headText">Cite Chef</div>
<div id="navWrap" onclick="openNav()" ontouchstart="openNav()">
<div class="navLine"></div>
<div class="navLine" id="navMid"></div>
<div class="navLine" id="navBottom"></div>
</div>
</div>
window.addEventListener('load', function(){
document.getElementById('navWrap').addEventListener('mousedown', openNav);
document.getElementsByClassName('closebtn')[0].addEventListener('click', closeNav);
document.getElementById('Create').addEventListener('click', create);
document.getElementById('TInput').addEventListener('click', MYfunctionTwo);
});
This might be due to some overlapping div's or element on the phone view, you need not add a separate event listener for the phone view click.
Try to inspect the code in the mobile view you might some overlapping element.
As you have mentioned on your previous comment, you are testing this on Safari. The inline onclick events are going to cause issues in your Web App. This is not supported in some browsers. I recommend using addEventListener. Simply remove all the inline events and migrate event handling implementations to addEventListener.
<div id="headWrap">
<div id="logoWrap"><img src="/static/images/logo.svg" id="logo"></div>
<div id="headText">Cite Chef</div>
<div id="navWrap">
<div class="navLine"></div>
<div class="navLine" id="navMid"></div>
<div class="navLine" id="navBottom"></div>
</div>
</div>
document.getElementById('navWrap').addEventListener('click', openNav);
In case you are using a lower version of Safari Mobile. This is good information to have from MDN Web Docs in case you plan on doing event delegation in the future:
Safari Mobile 7.0+ (and likely earlier versions too) suffers from a
bug where click events aren't fired on elements that aren't typically
interactive (e.g. <div>) and which also don't have event listeners
directly attached to the elements themselves (i.e. event delegation is
being used).
Reference: https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event

Focus an element within a ShadowDOM

I have a modal dialog created within a Shadow DOM and injected (using violentmonkey) into a page during document load. This dialog contains a simple form. The idea is to prompt the user for input.
When the dialog is created, it has focus. Elements can be tabbed, and I can hook key/mouse events. The problem arises when the window load event is triggered. Focus returns to the main body (verified by issuing document.activeElement in the console and by hooking focusin/out for both body and the shodowroot). I can hook the focusin event, but so far nothing I have tried will return focus to my dialog.
A cut down version of the dialog:
<div id="twifty-translate-dialogue" style="">
#shadow-root (open)
<div class="outer" style="z-index: 2147483647; pointer-events: none;">
<div class="container">
<div id="header" class="">
</div>
<div id="languages" class="">
</div>
<div class="buttons">
<button id="translate" class="button-translate" type="button">Translate</button>
<button id="cancel" class="button-cancel" type="button">Cancel</button>
</div>
</div>
</div>
I would expect the following to work:
window.onload = () => {
document.getElementById("twifty-translate-dialogue").shadowRoot.getElementById("translate").focus()
}
From what I've read, there should be two active elements. The #twifty-translate-dialogue and the shadow DOMs #translate. But:
document.activeElement
> <body>
document.getElementById("twifty-translate-dialogue").shadowRoot.getElementById("translate").focus()
document.activeElement
> #twifty-translate-dialogue
document.getElementById("twifty-translate-dialogue").shadowRoot.activeElement
> null
How can I restore focus?
Update:
A workaround:
window.onload = () => {
window.setTimeout(() => {
this.shadow.getElementById("translate").focus()
}, 1)
}
I'm guessing that the focus changes after the onload event, meaning that trying to set the focus in the handler will have no effect. By using a timeout, I've scheduled the focus call for after the completion of onload.
Is this a bug or standard behaviour? Is there a prefered way of setting the focus?

How to invoke ondragstart and drop event manually javascript

I have used html ondragstart and ondrop events.
<div class="row height_380_div" id="target_div_id" ondragover="drag_over(event)" ondrop="drop(event)">
<div ondragstart="dragStart(event)" class="alert alert-warning col-lg-12 height_30_div node" draggable="true" id="call" name="Call" >
<button class="close top_min_10 hidden call_btn" data-dismiss="alert" onclick="remove_btn(this)">
×</button><p class="top_min_10">Call</p>
</div>
</div>
I want to drag and drop this dynamically on page load. Means for example, the div should be dragged to 100 px left and dropped there, which should be called dynamically when page loads and it should run on its own. How to do that?
posted 6 year ago ((( ... anyways, if anyone needs it Ill leave it here ))
// add listenner to element
someElement.addEventListener('dragstart', () => '...do something');
// fire the custom event (I didn't check it in action, but it worked in my unit
//tests so )))
const dragEvent = new Event('dragstart');
someElement.dispatchEvent(dragEvent);

Get clicked element in Meteor?

I have HTML in the following format:
<div class="panel panel-1 active" data-chart="flight-chart">
<div class="row panel-header">
<div class="col-sm-3"><i class="fa fa-plane fa-4x"></i></div>
<div class="col-sm-9">
<div class="big">123,673</div>
<div class="small">flights added since 3/3/2016</div>
</div>
</div>
<div class="panel-footer">view more information</div>
</div>
I am capturing click events using the code below:
Template.infoPanel.events
'click .panel': (event, instance) ->
console.log 'panel click',event.target
The issue I am having is that depending on where in the .panel div I click, a different element is returned for event.target. So If I click on the .col-sm-9 div, that will be returned as the target even though the event is targeting it's parent .panel. Same goes for clicking on .panel-footer.
How can I get the .panel element 100% of the time from inside of the click event?
Use currentTarget instead:
console.log 'panel click', event.currentTarget
See the Event Maps section of the docs for more details.

Categories