I have this toggle function which has multiple buttons.
var button1 = document.querySelector('#button1');
var button2 = document.querySelector('#button2');
var button3 = document.querySelector('#button3');
var toggleState = function (elem, one, two) {
var elem = document.querySelector(elem);
elem.setAttribute('data-state', elem.getAttribute('data-state') === one ? two : one); //ternary operator
};
button1.onclick = function (e) {
toggleState('#div1', 'open', 'closed');
e.preventDefault();
};
button2.onclick = function (e) {
toggleState('#div2', 'open', 'closed');
e.preventDefault();
};
button3.onclick = function (e) {
toggleState('#div3', 'open', 'closed');
e.preventDefault();
};
I've tried querySelectorAll to combine variables but it doesn't work. I think I know why. But I can't figure out a way to write the script more eloquently. (scratch eloquently. respectable is a better word)
How can I combine variables and onclicks so that the code is not so redundant?
Here is one solution:
You need to get button elements and trigger an onclick event for them.
Instead of trigger onclick event handler per each button, you could use a loop.
Read more about bind function
var buttons=document.getElementsByTagName('button');
var toggleState = function (elem, one, two) {
var elem = document.querySelector(elem);
elem.setAttribute('data-state', elem.getAttribute('data-state') === one ? two : one); //ternary operator
};
for(var i=0;i<buttons.length;i++){
var button=document.querySelector('#button'+(i+1));
button.onclick=(function(index){;
toggleState('#div'+index,'open','closed');
}).bind(this,i+1);
}
Consider a solution similar to the one below. Instead of copying the event handler per element, you could process each of the elements in a loop.
If the element ids are consistent, you could make it even briefer by only specifying the number of toggles and generating the ids on the fly.
var toggles = {
'#button1': '#div1',
'#button2': '#div2',
'#button3': '#div3'
};
Object.keys(toggles).forEach(function(toggle) {
document.querySelector(toggle).onclick = function (e) {
toggleState(toggles[toggle], 'open', 'closed');
e.preventDefault();
};
});
function toggleState(elem, one, two) {
var elem = document.querySelector(elem);
elem.setAttribute('data-state', elem.getAttribute('data-state') === one ? two : one);
};
Related
Im new to Javascript, and I want to build a basic program that displays and increment by one whenever a user clicks on a button or link. For example, when event is fired by a click. The counter should display a 1. Then, when a user clicks wherever i placed the event listener; the counter should display 2. etc.
function addByOne(e) {
var i = 0;
if (e = true) {
elMsg.innerHTML = ++i;
}
}
var elMsg = document.getElementById('count');
var el = document.getElementById('selection');
el.addEventListener('click', function (e) {
addByOne(e);
}, false);
The bottom portion of my code is the event listener i added to my HTML. But i'm mostly worried about the top portion of my code. Whenever i click on a element to make the event fire it just displays a zero. It doesn't increment by one from there.
If you declare i = 0 in the addByOne method, it will be initialized to 0 on each click. It should be declared outside of the function. Also, e = true will overwrite the event object being passed to the event handler.
function addByOne(e) {
elMsg.innerHTML = ++i;
}
var i = 0;
var elMsg = document.getElementById('count');
var el = document.getElementById('selection');
el.addEventListener('click', function (e) {
addByOne(e);
}, false);
i is set to 0 at each event handler call, e is event, if(e = true) assigns true to e : event.
You can use HTMLElement.dataset to store data at the element, Number() to convert string to number, + 1 to add to the number, reset the value at .dataset property of element
function addByOne() {
if (elMsg.dataset.i === undefined) {
elMsg.dataset.i = 1
} else {
elMsg.dataset.i = Number(elMsg.dataset.i) + 1
}
elMsg.innerHTML = elMsg.dataset.i;
}
var elMsg = document.getElementById('count');
var el = document.getElementById('selection');
el.addEventListener('click', function (e) {
addByOne();
}, false);
<div id="count">count</div>
<div id="selection">click selection</div>
I would like to remove all element from my canva except the one on which I click.
I create a set, put all element inside and remove the set :
button.click(function () {
var to_remove = paper.set();
paper.forEach(function (el) {
to_remove.push(el);
});
to_remove.remove();
});
But i don't success to test if my element is my button or not.
Axel
You can simply cache your clicked element and compare it during the loop.
button.click(function() {
var clickedEl = this,
toRemove = paper.set();
paper.forEach(function(el) {
if (el !== clickedEl) {
toRemove.push(el);
}
});
toRemove.remove();
});
Demo: http://jsfiddle.net/yRNNe/
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
I have a form, with a button called add rows. I would like to disable this button after user clicks on it thrice.
You could set a click counter on the button, but seeing as it is called "add rows", I suppose you might be able to just count the number of rows, and determine if it should be disabled that way.
bool disabled = true;
$('#add-rows').prop('disabled', disabled);
Replace true with your favourite means of calculating the number of rows.
From the top answer in google Triple Click Event:
$.event.special.tripleclick = {
setup: function(data, namespaces) {
var elem = this, $elem = jQuery(elem);
$elem.bind('click', jQuery.event.special.tripleclick.handler);
},
teardown: function(namespaces) {
var elem = this, $elem = jQuery(elem);
$elem.unbind('click', jQuery.event.special.tripleclick.handler)
},
handler: function(event) {
var elem = this, $elem = jQuery(elem), clicks = $elem.data('clicks') || 0;
clicks += 1;
if ( clicks === 3 ) {
clicks = 0;
// set event type to "tripleclick"
event.type = "tripleclick";
// let jQuery handle the triggering of "tripleclick" event handlers
jQuery.event.handle.apply(this, arguments)
}
$elem.data('clicks', clicks);
}
};
Used like so:
$("#mybutton").bind("tripleclick", function() {
$(this).attr("disabled", "disabled");
}
Note that you'll probably want to use on instead of bind, see What's the difference between `on` and `live` or `bind`?
Here it is:
//Disable KeyboardNavigation
document.getElementById("author").onfocus = function() {
document.onkeyup = null;
};
document.getElementById("email").onfocus = function() {
document.onkeyup = null;
};
document.getElementById("url").onfocus = function() {
document.onkeyup = null;
};
document.getElementById("comment").onfocus = function() {
document.onkeyup = null;
};
//Enable KeyboardNavigation
document.getElementById("author").onblur = function() {
document.onkeyup = KeyCheck;
};
document.getElementById("email").onblur = function() {
document.onkeyup = KeyCheck;
};
document.getElementById("url").onblur = function() {
document.onkeyup = KeyCheck;
};
document.getElementById("comment").onblur = function() {
document.onkeyup = KeyCheck;
};
I believe it's definitely possible to write a better code with a loop but I really don't know how to make it work. I tried the following:
var formfields= ["author", "email", "url", "comment"];
for (i=1; i<=3; i++){
//Don't really know what to put in here.
}
Thank you in advance for your help!
EDIT : Whole code is below. You should know that I got some help to get to this result:
document.onkeyup = KeyCheck;
var pages = [
"http://",
"http://",
"http://",
"http://",
"http://"];
function leftarrowpressed() {
location.href = pages[ Math.max(0, 0 - 1) ];
//The second '0' here changes from 0 to 4, according to the page.
}
function rightarrowpressed() {
location.href = pages[ Math.min(pages.length - 1, 0 + 1) ];
//The second '0' here changes from 0 to 4, according to the page.
}
function KeyCheck(e)
{
var KeyID = (window.event) ? event.keyCode : e.keyCode;
switch(KeyID)
{
// left arrow key
case 37:
leftarrowpressed();
break;
// right arrow key
case 39:
rightarrowpressed();
break;
}
}
Hope this can help a little more. By the way, thank you everyone. I really don't know which solution to choose.
It looks like what you are doing is trying to prevent keystrokes in an input element from affecting navigation. What you could do instead is check event.target in KeyCheck and only perform the action if it was not triggered by an input element.
function KeyCheck(e) {
var target = e ? e.target : event.srcElement, //standards vs IE
tagname = target.tagName.toLowerCase();
if( tagname !== "input" && tagname !== "textarea" && tagname !== "select") {
//Not from an input, NAVIGATE!
}
}
If using jQuery then you can go a more straight-forward way: inside KeyCheck, check whether any of the elements is focused, and don't do anything in that case. You won't need any of the above.
function KeyCheck(e) {
if($("#author, #email, #url, #comment").is(":focus")) {
return; // ignore if any of these elements has focus
}
// ...
}
Make sure to bind KeyCheck using jQuery too:
$("body").on("keyup", KeyCheck);
var formfields= ["author", "email", "url", "comment"];
for (i=0; i<=3; i++){
var field = document.getElementById(formFields[i]);
field.onfocus = function() {
document.onkeyup = null;
};
field.onblur = function() {
document.onkeyup = KeyCheck;
};
}
or more proper way would be to use something like this
jQuery.each("author email url comment".split(" "), function(i, name) {
$('#' + name).focus(function() {
// do whatever you want to do
}).blur(function() {
// do whatever you wnat to do
));
});
Neat and readable:
var formfields = ["author", "email", "url", "comment"],
i, elem,
blur = function() { document.onkeyup = KeyCheck; },
focus = function() { document.onkeyup = null; };
for (i=0; i<=3; i++) {
elem = document.getElementById(formFields[i]);
elem.onblur = blur;
elem.onfocus = focus;
}
look for the nearest common parent for these elements and add a handler to it. we can use the powers of delegation using the .on() as well as method chaining to bind a hander only to the parent (in this case, 2 handlers for all, not 8 where 2 per element) to take effect on all 4 elements.
var selectors = '#author, #email, #url, #comment';
$('nearest_parent_element').on('focus', selectors, function() {
document.onkeyup = null;
}).on('blur', selectors, function() {
document.onkeyup = KeyCheck;
});
jQuery way:
$("#author, #email, #url, #comment").on({
focus: function() {
$(document).on('keyup', null);
},
blur: function() {
$(document).on('keyup', KeyCheck);
}
});
It all depends on how good you are at JavaScript. I would recommend for you to use event delegation: http://jsfiddle.net/teresko/PkCuZ/3/
It might look a bit complicated , but the add_listener() function would be shared throughout the whole code , so the payload actually looks like this:
var handlers = {
keyout: function(e){
var event = e || window.event,
target = event.target || event.srcElement;
console.log( 'leaving ' + target.name );
},
keyin: function(e){
var event = e || window.event,
target = event.target || event.srcElement;
console.log( 'entering ' + target.name );
}
},
container = document.getElementById('container');
add_listener( container, 'blur' , handlers.keyout );
add_listener( container, 'focus' , handlers.keyin );
This would work with any number of form elements.
As for the add_listener() function , it contains a small fix for blur/focus on IE, and a per-application choice of which method of attaching events to use. It's kinda an universal function which you can just drop in, when you need a common interface for attaching listeners:
var add_listener = (function () {
var fix = {
'focus': 'focusin',
'blur': 'focusout'
};
if ( window.addEventListener ) {
return function ( element, type, callback ) {
element.addEventListener(type, callback, typeof(fix[type]) !== undefined );
};
}else{
return function ( element, type, callback ) {
type = fix[type] || type;
element.attachEvent('on' + type, callback);
};
}
})();