I have this html code:
<div class="container">
<div class="parent">
<!-- P Element Completely On Parent -->
<p style="width: 100%; height: 100%;">Hello</p>
</div>
........
</div>
This is my Javascript:
document.querySelector(".container").addEventListener("click", (e) => {
if ( e.target == document.querySelector(".parent") ) {
alert(" It Worked!!! ")
}
},
true // <--- bubbling
)
I don't want to add library like jQuery and etc...
I don't want to set p elements style="pointer-events: none"
I want to use dynamic code (don't using e.target.parentElement)
Is it possible to detect parent elements clicking without giving event to it ( with event bubbling ) ?
The result code should work after client clicking on p element
If the nesting of .parent is dynamic, you might want to determine the path from the currentTarget to the target. Then search through the path for the specific element you are looking for.
// Determines the node path from baseNode to targetNode, by travelling up from
// the targetNode. The retuned array will include both the baseNode and the
// targetNode. If the targetNode is the baseNode an array with one elment is
// returned. Throws an error if baseNode is not an ancestor of targetNode.
function nodePath(baseNode, targetNode, currentPath = []) {
currentPath.unshift(targetNode);
if (targetNode == baseNode) return currentPath;
return nodePath(baseNode, targetNode.parentNode, currentPath);
}
document
.querySelector(".container")
.addEventListener("click", ({currentTarget, target}) => {
const path = nodePath(currentTarget, target);
const parent = path.find(node => node.matches(".parent"));
if (parent) {
console.log("It Worked!");
}
});
<div class="container">
<div class="parent">
<!-- P Element Completely On Parent -->
<p style="width: 100%; height: 100%;">Hello</p>
</div>
........
</div>
Yes you could try this
document.querySelector(".container").addEventListener("click", (e) => {
var parentel = e.target.parentElement
if (parentel.className == "parent") {
alert(" It Worked!!! ")
}
})
<div class="container">
<div class="parent">
<!-- P Element Completely On Parent -->
<p class- "child" style="width: 100%; height: 100%;">Hello</p>
</div>
</div>
Related
I have an issue and I don't know how I can fix this and I need some help.
The following code I have so far
<div class="parent" data="one">
<div class="child hide" id="one">
test
</div>
</div>
<div class="parent" data="two">
<div class="child hide" id='two'>
test 2
</div>
</div>
var parent = document.getElementsByClassName("parent");
for (var i = 0; i < parent.length; i++) {
parent[i].addEventListener("click", function () {
var child = document.getElementsByClassName("child");
var attribute = this.getAttribute("data");
var the_element = document.getElementById(attribute);
for (var is = 0; is < child.length; is++) {
child[is].classList.add('hide');
child[is].classList.remove('show');
}
the_element.classList.add('show');
});
}
If the user click on the parent the child what is connected get the class show and the hide class is removed. If the user click on another parent all child elements get the class hide en the show class is removed. The code above works for this but what I also want is if the user clicks on the parent and after that the user clicks the same parent the class show remove and add hide at the child.
I think I must use this in JavaScript but how can I combine this all together?
You can toggle element using just adding 'hide' class. Add css classes as below.
<style>
.parent {
...
}
.child {
...
}
.hide {
display: none;
}
</style>
Add html elements as below.
<div class="parent" data="one">
<div class="child hide" id="one">
test
</div>
</div>
<div class="parent" data="two">
<div class="child hide" id='two'>
test 2
</div>
</div>
Then add javascript to toggle elements.
<script>
// Get parent elements and children elements
let parents = document.getElementsByClassName("parent");
let children = document.getElementsByClassName("child");
// Form an Array with parent elements and
// add event listener for each parent
Array.from(parents).forEach(parent => {
parent.addEventListener("click", function () {
// Form an Array with child elements and
// check condition for each children
Array.from(children).forEach(child => {
// Check wheather is it the child of clicked parent
if(child.parentNode == this) {
// If clicked parent's child just toggle hide class
child.classList.toggle("hide");
}else {
// Add hide class for all other children
child.classList.add("hide");
}
});
});
});
</script>
Thats it! Hope it helps.
I'm attempting to track events for all UI elements on a page. The page contains dynamically generated content and various frameworks / libraries. Initially I tracked elements through creating a css class "track" , then adding style "track" to tracked elements. elements are then tracked using :
$('.track').on('click', function() {
console.log('Div clicked' + this.id);
console.log(window.location.href);
console.log(new Date().getTime());
});
As content can be dynamically generated I wanted a method to track these elements also. So tried this using wildcard jQuery operator.
In this fiddle : https://jsfiddle.net/xx68trhg/37/ I'm attempting to track all elements using the jquery '*' selector.
Using jQuery '*' selector appears to fire the event for all elements of given type.
So for this case if is clicked all the click event is fired for all divs. But id is just available for div being clicked.
For the th element the click event is fired twice , what is reason for this ?
Can the source be modified that event is fired for just currently selected event ?
fiddle src :
$(document).ready(function() {
$('*').each(function(i, ele) {
$(this).addClass("tracked");
});
$('.tracked').on('click', function() {
console.log('Div clicked' + this.id);
console.log(window.location.href);
console.log(new Date().getTime());
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<!-- <div id="1" data-track="thisdiv">
Any clicks in here should be tracked
</div>
-->
<div id="1">
Any clicks in here should be tracked 1
</div>
<div id="2">
Any clicks in here should be tracked 2
</div>
<div id="3">
Any clicks in here should be tracked 3
</div>
<th id="th">tester</th>
You can try with:
$(document).ready(function() {
$("body > *").click(function(event) {
console.log(event.target.id);
});
});
$(document).ready(function() {
$("body > *").click(function(event) {
console.log(event.target.id);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="1">
Any clicks in here should be tracked 1
</div>
<div id="2">
Any clicks in here should be tracked 2
</div>
<div id="3">
Any clicks in here should be tracked 3
</div>
<table>
<tr>
<td>Cols 1</td>
<td id="td">Cols 2</td>
</tr>
</table>
<p id="th">tester</p>
You may want to use event delegation to target the elements you need. Advantage is that this also works for dynamically generated elements. See code for an example of this.
// method to add/set data-attribute and value
const nClicksInit = (element, n = "0") => element.setAttribute("data-nclicked", n);
// add data-attribute to all current divs (see css for usage)
// btw: we can't use the method directly (forEach(nClicksInit))
// because that would send the forEach iterator as the value of parameter n
document.querySelectorAll("div").forEach(elem => nClicksInit(elem));
// add a click handler to the document body. You only need one handler method
// (clickHandling) to handle all click events
document.querySelector('body').addEventListener('click', clickHandling);
function clickHandling(evt) {
// evt.target is the element the event is generated
// from. Now, let's detect what was clicked. If none of the
// conditions hereafter are met, this method does nothing.
const from = evt.target;
if (/^div$/i.test(from.nodeName)) {
// aha, it's a div, let's increment the number of detected
// clicks in data-attribute
nClicksInit(from, +from.getAttribute("data-nclicked") + 1);
}
if (from.id === "addDiv") {
// allright, it's button#addDiv, so add a div element
let newElement = document.createElement("div");
newElement.innerHTML = "My clicks are also tracked ;)";
const otherDivs = document.querySelectorAll("div");
otherDivs[otherDivs.length-1].after(newElement);
nClicksInit(newElement);
}
}
body {
font: 12px/15px normal verdana, arial;
margin: 2em;
}
div {
cursor:pointer;
}
div:hover {
color: red;
}
div:hover:before {
content: '['attr(data-nclicked)' click(s) detected] ';
color: green;
}
#addDiv:hover:after {
content: " and see what happens";
}
<div id="1">
Click me and see if clicks are tracked
</div>
<div id="2">
Click me and see if clicks are tracked
</div>
<div id="3">
Click me and see if clicks are tracked
</div>
<p>
<button id="addDiv">Add a div</button>
</p>
<h3 id="th">No events are tracked here, so clicking doesn't do anything</h3>
You can invoke the stopPropagation and the condition this === e.currentTarget to ensure invoke the handler function of the event source DOM.
And you must know the <th> tag must wrapped by <table>, otherwise it will not be rendered.
$(document).ready(function() {
$('*').each(function(i, ele) {
$(this).addClass("tracked");
});
$('.tracked').on('click', function(e) {
if (this === e.currentTarget) {
e.stopPropagation();
console.log('Div clicked' + this.id);
console.log(window.location.href);
console.log(new Date().getTime());
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<!-- <div id="1" data-track="thisdiv">
Any clicks in here should be tracked
</div>
-->
<div id="1">
Any clicks in here should be tracked 1
</div>
<div id="2">
Any clicks in here should be tracked 2
</div>
<div id="3">
Any clicks in here should be tracked 3
</div>
<table>
<th id="th">tester</th>
</table>
My HTML looks like this:
<div class="panel">
<div class="panel-heading"></div>
<div class="panel-body"></div>
</div>
I am currently selecting the parent node of the panel-heading element like so:
e.target.parentNode
This leaves me with the panel class. All is well.
But now I would like to grab the panel-body at that point. Doing something like this unfortunately does not work:
e.target.parentNode.querySelector('.panel-body')
Is there a clean way to do this in vanilla javascript?
If you know the node's class, you can always use document object:
var tgt = document.querySelector('.panel-body');
If you need to get nodes in the context of an event such as click, you can delegate.
Find node that is an ancestor of all of the nodes you wish to access.
ex. .panel
Register the event on that node.
ex. panel.addEventListener('click', callback)
During the bubbling phase, find the event.target by comparing it to the event.currentTarget (the node that is registered to the event)
ex. if(e.target !== e.currentTarget) {...
Click nodes and it's tag and class will be displayed.
Details are commented in snippet
Snippet
// Reference top element
var panel = document.querySelector('.panel');
// Register .panel on click event
panel.addEventListener('click', highlight);
function highlight(e) {
// if the clicked node is not .panel
if (e.target !== e.currentTarget) {
// Get the clicked node's class
var tgtClass = e.target.className;
// Get the clicked node's tag
var tgtTag = e.target.tagName;
}
/* Set the clicked node's tag and class
|| as it's content.
*/
e.target.textContent += ' ' + tgtTag + '.' + tgtClass;
}
[class*=panel] {
border: 1px dashed blue;
color: red;
}
<section class="panel">
<hgroup class='panel-heading-group'>
<h1 class="panel-heading">HEADING</h1>
<h2 class='panel-sub-heading'>SUB-HEADING</h2>
</hgroup>
<main class="panel-body">
<p>CONTENT A</p>
<p>CONTENT B</p>
</main>
<footer class='panel-footer'>FOOTER</footer>
</section>
I have a simple contenteditable markup:
<div class="example" contenteditable="true">
<div class="inside">Some content</div>
</div>
When I delete the "Some content", then class="inside" div also gets deleted. Is there a way to prevent the inside div from being removed when contents are deleted?
For example, this is the look I am trying to make once the contents are deleted.
<div class="example" contenteditable="true">
<div class="inside"></div> <!-- The div is not deleted -->
</div>
I looked around but doesn't seem like there is a clear answer.
Any help will be much appreciated.
Thank you.
This might help someone
function onpaste(e: ClipboardEvent) {
e.preventDefault();
const selection = window.getSelection();
// Don't allow deleting nodes
if (selection.anchorNode.isSameNode(selection.focusNode)) {
// get text representation of clipboard
const text = e.clipboardData.getData("text/plain");
// insert text manually, but without new line characters as can't support <br/>s yet
document.execCommand("insertHTML", false, text.replace(/\n/g, ""));
}
}
function onkeydownInEditable(e: KeyboardEvent) {
if (e.key === "Enter") {
e.preventDefault();
}
if (e.key === "Backspace" || e.key === "Delete" || e.key === "Paste") {
const selection = window.getSelection();
// Don't allow deleting nodes
if (!selection.anchorNode.isSameNode(selection.focusNode))
e.preventDefault();
}
}
elementEditing.addEventListener("keydown", onkeydownInEditable);
elementEditing.addEventListener("paste", onpaste);
const example = document.querySelector(".example")
example.addEventListener("keydown", (e) => {
if (e.keyCode == 8 || e.keyCode == 46) { // delete and del keys
if (example.children.length === 1) { // last inner element
if (example.children[0].innerText < 1) { // last element is empty
e.preventDefault()
}
}
})
You have to add contenteditable="true" to inside elements:
<div class="example">
<div class="inside" contenteditable="true">Some content</div>
<div class="inside">Some content</div> <!-- This one will not be deleted -->
<div class="inside" contenteditable="true">Some content</div>
</div>
.inside, .example {
display: inline;
}
<div class="example" contenteditable="true">
<div class="inside">Some content</div>
</div>
Maybe there's a style that is inline-level element.
Since the width will be 0% when you remove the content. You cannot click it again or add any content.
So, my solution will be
.inside, .example {
display: block;
}
You can specify width if you want. :)
.inside, .example {
display: block;
}
<div class="example" contenteditable="true">
<div class="inside">Some content</div>
</div>
I am facing an issue about this.
<div id="1">
<div id="2">
</div>
<div id="3">
<div id="4">
</div>
</div>
</div>
<div id="others_div">
</div>
I want to add the class "hidden" to "1" when I click on something which is not "1" nor one of its children.
Now I am using this but I have a lack of imagination for solving this issue...
document.onclick = function(e)
{
if(e.target.id!="1")
{
$("#1").addClass("hidden");
}
}
Well, to avoid e.stopPropagation() (maybe you want that event to bubble up to some other ancestor) You can check if it is not clicked on #1 nor on it's children like this:
$('body').on('click', function(e) {
if (!((e.target.id== "1") || $(e.target).closest('#1').length)) {
$("#1").addClass("hidden");
}
});
You could use a jQuery check like the following one to check if the current element is your 1 element or traverse the DOM to see if the current target is contained within an element with an ID of 1 :
<script>
$(function(){
// Trigger this when something is clicked
$(document).click(function(e){
// Toggle the hidden class based on if the current element is 1
// or if it is contained in an element with ID of 1
$("#1").toggleClass('hidden',!((e.target.id== "1") || $(e.target).closest('#1').length))
});
});
</script>
Generally, you should avoid using ID attributes that only consists of numbers as they are not valid (ID attributes must begin with a letter). Ignoring this could result in some issues with regards to CSS or jQuery selection.
JQuery
$('body').on( "click", function(e) {
if(e.target.id !== "1")
{
$("#1").addClass("hidden");
}
});
I think you want this
// taken from http://stackoverflow.com/questions/152975/how-to-detect-a-click-outside-an-element
$('html').click(function() {
//Hide the menus if visible
alert('hide');
});
$('#one').click(function(event){
event.stopPropagation();
});
div#one {
background: yellow;
}
div#others_div {
background: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div id="one">
div one
<div id="2">
div two
</div>
<div id="3">
div three
<div id="4">
div four
</div>
</div>
</div>
<div id="others_div">
other div
</div>