I have 3 divs with class: wpEdit and onClick: alertName()
<div class="wpEdit" onClick="alertName()">Bruce Lee</div>
<div class="wpEdit" onClick="alertName()">Jackie Chan</div>
<div class="wpEdit" onClick="alertName()">Jet li</div>
When clicked i want to know the Index of class wpEdit of the clicked Div:
function alertName(){
//Something like this
var classIndex = this.className.index; // This obviously dosnt work
alert(classIndex);
}
when clicked on Bruce Lee it should alert : 0
when clicked on Jackie Chan it should alert : 1
when clicked on Jet Li it should alert : 2
I need to know which instance of class="wpEdit" is clicked on
Try this
function clickedClassHandler(name,callback) {
// apply click handler to all elements with matching className
var allElements = document.body.getElementsByTagName("*");
for(var x = 0, len = allElements.length; x < len; x++) {
if(allElements[x].className == name) {
allElements[x].onclick = handleClick;
}
}
function handleClick() {
var elmParent = this.parentNode;
var parentChilds = elmParent.childNodes;
var index = 0;
for(var x = 0; x < parentChilds.length; x++) {
if(parentChilds[x] == this) {
break;
}
if(parentChilds[x].className == name) {
index++;
}
}
callback.call(this,index);
}
}
Usage:
clickedClassHandler("wpEdit",function(index){
// do something with the index
alert(index);
// 'this' refers to the element
// so you could do something with the element itself
this.style.backgroundColor = 'orange';
});
The first thing you might want to address in your code is the inline HTML binding.
You could use document.addEventListener on each element, or rely on event delegation.
The widely most used implementation of event delegation comes with jQuery. If you're already using jQuery, this is the way to go!
Alternatively I've also my own little delegate utility.
const delegate = (fn, selector) => {
return function handler(event) {
const matchingEl = matches(event.target, selector, this);
if(matchingEl != null){
fn.call(matchingEl, event);
}
};
};
const matches = (target, selector, boundElement) => {
if (target === boundElement){
return null;
}
if (target.matches(selector)){
return target;
}
if (target.parentNode){
return matches(target.parentNode, selector, boundElement);
}
return null;
};
This is how you would register the event listener.
document.getElementById('#parent')
.addEventListener('click', delegate(handler, '.wpEdit'));
And this is how you could get the index of the element that generated the event.
const handler = (event) => {
console.log(Array.prototype.indexOf.call(event.currentTarget.children, event.target));
}
Live demo:
const delegate = (fn, selector) => {
return function handler(event) {
const matchingEl = matches(event.target, selector, this);
if (matchingEl != null) {
fn.call(matchingEl, event);
}
};
};
const matches = (target, selector, boundElement) => {
if (target === boundElement) {
return null;
}
if (target.matches(selector)) {
return target;
}
if (target.parentNode) {
return matches(target.parentNode, selector, boundElement);
}
return null;
};
const handler = (event) => {
console.log(Array.prototype.indexOf.call(event.currentTarget.children, event.target));
}
document.getElementById('parent')
.addEventListener('click', delegate(handler, '.wpEdit'));
<div id="parent">
<div class="wpEdit">Bruce Lee</div>
<div class="wpEdit">Jackie Chan</div>
<div class="wpEdit">Jet li</div>
</div>
If you want the index of the div's based on your class wpEdit you can do like this:
HTML:
<div class="wpEdit">Bruce Lee</div>
<div class="wpEdit">Jackie Chan</div>
<div class="other">Other</div>
<div class="wpEdit">Jet li</div>
JS:
$(".wpEdit").bind("click", function(){
var divs = $(".wpEdit");
var curIdx = divs.index($(this));
alert(curIdx);
});
Live example : http://jsfiddle.net/pJwzc/
More information on the index function of jQuery : http://api.jquery.com/index/
Using vanilla javascript, this one works for me:
var wpEdits = document.querySelectorAll(".wpEdit");
for (let i = 0; i < wpEdits.length; i++)
wpEdits[i].addEventListener("click", showID);
function showID(evt) {
for (let i = 0; i < wpEdits.length; i++)
if(wpEdits[i] == evt.target)
alert(i);
}
May not be the best solution though as I am still new to js.
Since I am very new to JS, take the following explanation with a grain of salt:
(Line-1)
This is similar to var wpEdits = document.getElementsByClassName("wpEdit");. It will assign all instances of class="wpEdit" from the html file to the wpEdits variable.
(Line-3 and Line-4)
This two lines will cause any click on the class="wpEdit" to call function showID() defined below.
(Line-6 and Line-10)
When a click event happens, the browser will pass the unique properties of the item being clicked to the evt variable. This then is used in the for loop to compare against all available instances incrementally. The evt.target is used to get to the actual target. Once a match is found, it will alert the user.
To avoid wasting CPU time, running a break; is recommended to exit the loop soon after the match is found.
I could not understand, why people add new functions in previous answers, so...
const wpEdit = document.getElementsByClassName('wpEdit');
for(let i = 0; i < wpEdit.length; i++){
wpEdit[i].addEventListener('click',function(){
alert(i);
});
}
I just added 'click' event, using the loop. And [i] already is the current clicked class index...
FIDDLE
Related
I have a list of ids, when one of them is clicked I want to give it the attribute .className="open.
So far what I've done is to put all ids in a list and try to loop through them.
const memberB = document.querySelectorAll('#memberA, #memberAA, #memberAAA ');
for (var i = 0; i < memberB.length; i++) {
memberB[i].onclick = function(){
alert(memberB[i])
if(memberB[i].className=="open"){
memberB[i].className="";
}
else{
memberB[i].className="open";
}
}
What did I do wrong, I try to alert to see if I get the element that i clicked, all i get is 'undefined'.
you can use forEach to loop the NodeList which use querySelectorAll method, and use addEventListener to watch click event happen on all the elements you selected. Finally, use Element.classList.toggle method to toggle the class open or close
there is an example of toggle its background color after click
const members = document.querySelectorAll('.member');
members.forEach(member => {
member.addEventListener('click', e => {
e.target.classList.toggle('hight-light');
});
});
.member {
background-color: gray;
}
.hight-light {
background-color: green;
}
<div class="container">
<div class="member">1</div>
<div class="member hight-light">2</div>
<div class="member">3</div>
<div class="member">4</div>
</div>
I have a code snippet I like to keep around to do these kind of things in a single event listener
window.addEvent = (event_type, target, callback) => {
document.addEventListener(event_type, function (event) {
// If the event doesn't have a target
// Or the target doesn't look like a DOM element (no matches method
// Bail from the listener
if (event.target && typeof (event.target.matches) === 'function') {
if (!event.target.matches(target)) {
// If the element triggering the event is contained in the selector
// Copy the event and trigger it on the right target (keep original in case)
if (event.target.closest(target)) {
const new_event = new CustomEvent(event.type, event);
new_event.data = { originalTarget: event.target };
event.target.closest(target).dispatchEvent(new_event);
}
} else {
callback(event);
}
}
});
};
then in your case I'd do this
window.addEvent('click', '#memberA,#memberAA,#memberAAA', (event) => {
event.target.classList.toggle('open');
});
The script runs befor the DOM elements load.
You can put the script as a function inside an $(document).ready such that it runs after all the elements have been loaded.
$(document).ready(
function () {
const memberB = document.querySelectorAll('#memberA, #memberAA, #memberAAA ');
for (let i = 0; i < memberB.length; i++) {
memberB[i].onclick = function () {
//alert(memberB[i])
if (memberB[i].className === "open") {
memberB[i].className = "";
} else {
memberB[i].className = "open";
}
alert(memberB[i].className)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="memberA">A</button>
<button id="memberAA">AA</button>
<button id="memberAAA">AAA</button>
Let me know if this works!
I write a code to add click event to all tags in page with js.
function modifyText(obj) {
console.log(obj.target.tagName);
}
var el = document.body.getElementsByTagName("*");
console.log(el);
for (var i=0 ;i<el.length; i++){
el[i].addEventListener("click", modifyText, false);
}
In modifyText function I wanna to get any tags that clicked on it.
for example in this html
<div>
<h3>
<b>salaaaaam<i> this is a test</i> </b>for add a click event to all tags in pages.
</h3>
</div>
I wanna to print "I , B, H3 , DIV" when click on
this is a test
but I get something like this "I ,I ,I ,I".
JSFIDDLE
can anybody help please?
Thanks.
That is because event.target will refer to the element that actually triggered the event.
You can use this or event.currentTarget to refer to the element to which the handler is bound.
function modifyText(e) {
snippet.log(this.tagName + ' || ' + e.currentTarget.tagName);
}
var el = document.body.getElementsByTagName("*");
console.log(el);
for (var i = 0; i < el.length; i++) {
el[i].addEventListener("click", modifyText, false);
}
<!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
<div>
<h3>
<b>salaaaaam<i> this is a test</i> </b>for add a click event to all tags in pages.
</h3>
</div>
Adding a listener to all element is quite expensive and wouldn't work for newly added elements
What i would do is add one listener on top and go from there
document.addEventListener('click', evt => {
var elm = evt.target
var els = []
while (elm.tagName) {
els.push(elm.tagName)
elm = elm.parentNode
}
alert(els)
}, false)
function getTextNode(p) {
p.onclick = function modifyText(obj) {
console.log(obj.target.tagName);
}
var childArr = p.childNodes;
for (var i = 0; i < childArr.length; i++) {
textNodeArr = getTextNode(childArr[i]);
}
return;
}
arr = getTextNode(document.body);
Newbie to Javascript here
I have a html/php code, Whats happening right now is when I click the button all elements are shown not just the targeted element. any help most appreciated
HTML/PHP
<div class="te contentDiv">
<div class="myContent" style="display:none">
<?=$text?>
<a id="close_btn"
href="javascript:toggle_visibility('myContent','close_btn','open_btn');"
class="close-more"><?=localised_string('Close')?></a>
</div>
</div>
JavaScript
var toggle_visibility = (function () {
function toggle(cl) {
var els = document.getElementsByClassName(cl);
for (var i = 0; i < els.length; i++) {
var s = els[i].style;
s.display = s.display === 'none' ? 'block' : 'none';
};
}
return function (cl) {
if (cl instanceof Array) {
for (var i = 0; i < cl.length; i++) {
toggle(cl[i]);
}
} else {
toggle(cl);
}
};
})();
Not sure if this is the right way to do it, I have been working from other peoples examples
Your invocation of toggle_visibility() in HTML isn't matching code in JS for
if (cl instanceof Array)
isn't true, thus it's toggling all buttons declared with class myContent. cl in function returned from toggle_visibility() is first argument of invocation, which is 'myContent' in your case. But I think you want either use arguments rather than cl there or wrap the list of names in an array on invocation like this:
toggle_visibility( [ 'myContent', 'close_btn', 'open_btn' ] );
But this won't fix your issue nevertheless.
What about click handler like this:
function toggle_visibility(event) {
event.target.style.display = window.getComputedStyle(event.target, null).style == 'none' ? 'block' : 'none';
return false;
}
Note: see https://developer.mozilla.org/en-US/docs/Web/API/event.target
In your HTML you might use
Link
See that example in addition: https://developer.mozilla.org/en-US/docs/Web/API/event.currentTarget
In jQuery you can achieve it in one line...
$(".close-more").on("click",function(){$(this).toggle()})
Since .foreach and .map won't work on a nodelist, is the only way to work with the elements in a nodelist through a for loop?
What I'm trying to accomplish is adding different event listeners to the different elements within a nodelist. If the element has the class name of "bold", then the iBold() function should be run, and likewise for "italics" and "underline". Having multiple for loops running to handle each individually feels excessive, so that's why I'm trying to work with one loop to handle all rich text. However, if there's a better way to go about this, I'd really like to know since it seems as though I'm just over-thinking all of this.
var QSA = document.querySelectorAll('div > form > div > a.richText');
for (var rtIndex = 0; rtIndex < QSA.length;rtIndex++) { //Rich text event listeners
var rtid = QSA[rtIndex].id;
var targetiFrame = document.getElementById(rtid).getAttribute('data-pstid');
if (document.getElementById(rtid).className == "richText bold") { //Bold text event listener
QSA[rtIndex].addEventListener('click', function() {
if (targetiFrame != 0) {iBold(targetiFrame);}
else {
document.getElementById('richTextField').contentDocument.execCommand('bold', false, null);
document.getElementById('richTextField').contentWindow.focus();
}
}, false);
} else if (document.getElementsByClassName('richText')[rtIndex].className == 'richText underline') { //Underline text event listener
document.getElementsByClassName('richText')[rtIndex].addEventListener('click', function() {
if (targetiFrame == 0) {
document.getElementById('richTextField').contentDocument.execCommand('underline', false, null);
document.getElementById('richTextField').contentWindow.focus();
} else {iUnderline(targetiFrame);}
}, false);
} else if (document.getElementsByClassName('richText')[rtIndex].className == 'richText italic') { //Italic text event listener
document.getElementsByClassName('richText')[rtIndex].addEventListener('click', function() {
if (targetiFrame == 0) {
document.getElementById('richTextField').contentDocument.execCommand('italic', false, null);
document.getElementById('richTextField').contentWindow.focus();
} else {iItalic(targetiFrame);}
}, false);
}
}
for (var sbmtIndex = 0;sbmtIndex < document.getElementsByClassName('sbmtPost').length;sbmtIndex++) { //Event listener for submitting posts or comments
var iSubmt = document.querySelectorAll('form > div')[sbmtIndex];
document.querySelectorAll('form > div > .sbmtPost')[sbmtIndex].addEventListener('click', function() {
var pstData = iSubmt.querySelector('form > div > .sbmtPost').getAttribute('data-cmtid');
var cPrntID = iSubmt.querySelector('form > div > .sbmtPost').getAttribute('data-pstid');
sendData(pstData, cPrntID); //Post Data (data being the id) and Comment Parent Id. Comments are posts. Variables only used for comments
}, false);
}
If you don't mind converting your NodeList to an Array, you can use:
var nodeArray = Array.prototype.slice.call(nodeList);
Then you can use all the functional methods on the resulting Array.
Note: this is not cross-browser compatible, but neither is .forEach, so I don't think it's an issue.
<div class="btn" data='btn1'>btn1</div>
<div class="btn" data='btn2'>btn2</div>
<div class="btn" data='btn3'>btn3</div>
const btns = document.getElementsByClassName('btn');
function getBtns(el, callback) {
Array.prototype.forEach.call(el, function(node) {
// Wrap below in if() condition here.
node.addEventListener('click', callback);
});
}
getBtns(btns, function() {
console.log(this.getAttribute('data'));
});
this points to the button that's clicked.
I am developing an app that stays in the website itself, and I want every link to call a function. I have tried this:
HTML
link<br>
link 2
Javascript
var a = document.getElementsByTagName("a");
for (var i = 0; i < a.length; i++) {
a[i].onclick = function () {
return false
}
}
What is wrong? It doesn't work.
Since it's not jQuery, you should use the preventDefault function.
var a = document.getElementsByTagName("a");
for (var i = 0; i < a.length; i++) {
a[i].onclick = function (e) {
e.preventDefault();
doSomething();
}
}
edit for pure javascript solution
document.addEventListener("click", function(e){
if (e.nodeName==="A"){
e.preventDefault();
return false;
}
}, false);
This will only add one single event to the document and prevent all clicks on anchor elements only.
I removed the old solution because of the comment, that this wasn't a jquery question
Don't use return false, it does more than you really need. Instead try event.preventDefault()
var a = document.getElementsByTagName("a").forEach(function (e) {
e.onclick = function (a) {
doSomething(a);
return false;
}
}
}