call function with parameter unobstrusively - javascript

I have a functional code, but would like to improve it for the sake of learning.
I did quite a lot of research and only found a solution using jQuery, but as i'm still a beginer I would like to make in in plain js first.
[How do I get HTML button value to pass as a parameter to my javascript?
Basically, I have a list, and I want each entry of this list to call a function with a given parameter, being a natural number.
Among the classic / obstrusive techniques we have:
<li><a href="#" id="0" class="myClass" onclick="return myFunction(0);" >clickMe</a></li>
<li><a href="#" id="1" class="myClass" onclick="return myFunction(1);" >clickMe</a></li>
...
or
<li>ClickMe</li>
<li>ClickMe</li>
...
and
<li><button class="myClass" onClick="return myFunction(0);">ClickMe</button></li>
...
wich indeed call myFunction() with the parameter set to 0, 1 and so on.
But I've read that it wasn't recomended to do so as
I'm using the <a> tag while I'm not intending to link anywhere.
it mixes HTML and Javascript and that separation of concern is the way to go whenever possible
Then there is the unobstrusive way
<li><button id="0" class="myClass">clickMe</button></li>
completed by
document.getElementById("0").onclick = function (){myFunction()}
This works, but I can't find a way to set my parametter.
If, for instance, I add a value="0" attribute to my button, or myVar="0", I can't use value as a variable as it isn't declared, and it doesn't set the new value of myVar, thus myFunction is called with the wrong parameter being myVar="myVarPreviousValue"
Using GetElementsByClassName : EDIT: updated
Previously we used document.getElementById, however this may be very inconvinient if the list is long as we would have to create an event listner for every button. In such context, the use of document.getElementsByClassName seems appropriate.
As pointed by epascarello there's a nice explaination for the use of this method here Document.getElementsByClassName not working.
However this thread can be completed by precising how to get a variable value that is set in the elements of a given class in order to call a function with a given parameter
Bottom line question
How to, unobstrusivelly, call a function with a given parameter when the user click on some html content that have a given class (could be a <li>, <button>, <span>, <div> as you think is best)
Thank's in advance
J.
Edit : updated the question related to the use of getElementsByClassName

Then there is the unobstrusive way
<li><button id="0" class="myClass">clickMe</a></li>
completed by
document.getElementById("0").onclick = function (){myFunction()}
(Side note: You have <button ...></a> there, probably meant </button> at the end.)
In that specific case, just
document.getElementById("0").onclick = function (){ myFunction(0); };
// ------------------------------------------------------------^
...but I'd always advocate addEventListener instead, so the code plays nicely with others:
document.getElementById("0").addEventListener("click", function (){ myFunction(0); }, false);
(If you have to support obsolete browsers like IE8, this answer has a cross-platform hookEvent you can use instead of addEventListener.)
If you want to use the same handler and put the argument on the element, you can use the id or a data-* attribute:
<li><button id="0" data-value="0" class="myClass">clickMe</button></li>
completed by
document.getElementById("0").addEventListener("click", myFunction, false);
then
function myFunction() {
var parameter = this.getAttribute("data-value");
// or var parameter = this.id;
// ...
}
Here's an example using querySelectorAll to handle all button elements with a data-value attribute, and showing the data-value attribute when clicked:
var buttons = document.querySelectorAll("button[data-value]");
Array.prototype.forEach.call(buttons, function(btn) {
btn.addEventListener("click", handler, false);
});
function handler() {
console.log("Value: " + this.getAttribute("data-value"));
}
<button type="button" data-value="one">I'm 'one'</button>
<button type="button" data-value="two">I'm 'two'</button>
<button type="button" data-value="three">I'm 'three'</button>
<button type="button" data-value="four">I'm 'four'</button>
Side note: While it's valid HTML, I would generally avoid starting an id value with a digit, because although they work with getElementById, they're awkward to use in CSS selectors (#0 is invalid, for instance).

Since it got reopened, I will answer.
There is no getElementByClassName there is a getElementsByClassName. You can not just bind to the collection directly. You would need to loop over the live html collection to add the events.
var anchors = document.getElementsByClassName("myClass");
for (var i = 0; i < anchors.length; i++) {
anchors[i].addEventListener("click", function(e) {
console.log(this.getAttribute("data-id"));
});
}
<ul id="myList">
<li>ClickMe</li>
<li>ClickMe</li>
</ul>
The easy answer is event delegation, one click event on the parent element, look at the event object for what was clicked and you have the element. Read the data attribute or id and you have the number.
var list = document.getElementById("myList");
list.addEventListener("click", function(e) {
console.log(e.target.getAttribute("data-id"));
});
<ul id="myList">
<li>ClickMe</li>
<li>ClickMe</li>
</ul>

If you have a predefined function, you can do
var elements = document.querySelectorAll(".myClass");
for (var i=0;i< elements.length;i++) {
elements[i].onclick = myFunction;
}
and use a data-attribute to pass the data or this.id if that is enough
function myFunction(e) {
e.preventDefault(); // cancel click
e.stopPropagation(); // cancel event bubbling
console.log(this.id, this.getAttribute("data-val")||this.value);
}
window.onload = function() {
var elements = document.querySelectorAll(".myClass");
for (var i = 0; i < elements.length; i++) {
elements[i].onclick = myFunction;
}
}
<ul>
<li>clickMe</li>
<li>clickMe</li>
</ul>
<ul>
<li><button type="button" id="but0" value="value 0" class="myClass">clickMe</button></li>
<li><button type="button" id="but1" value="value 1" class="myClass">clickMe</button></li>
</ul>
<ul>
<li id="link0" data-val="value 0" class="myClass">clickMe</li>
<li id="link1" data-val="value 1" class="myClass">clickMe</li>
</ul>

Related

get index of class element with inline onclick - pure js no jQuery

so i have some class-elements:
<span class="show" onclick="show()">show</span>
<span class="show" onclick="show()">show</span>
<span class="show" onclick="show()">show</span>
when i click one of these elements i need the index for some reason. i know how to use jQuery, but thats not what i am asking for, here.
my js function should look something like this:
function show() {
var index = document.getElementsByClassName('show')[??];
alert(index);
}
How is that possible with pure javascript? NOTE: i have to stick with the onclick="show()" that cannot be changed.
i hope someone can help, thanks in advance :)
Just get the array of all the elements and find the current element who received the click
function show(el) {
var els = Array.prototype.slice.call( document.getElementsByClassName('show'), 0 );
console.log(els.indexOf(event.currentTarget));
}
<span class="show" onclick="show(this)">span 1</span>
<span class="show" onclick="show(this)">span 2</span>
<span class="show" onclick="show(this)">span 3</span>
You can try something like this:
Use addEventListener to bind event handlers. This will allow you to use this.
If you still need index of element, you can either loop over elements or convert NodeList into array and then use Array.indexOf
Sample
Onclick
function show(self){
var spans = document.querySelectorAll('.show');
console.log(Array.from(spans).indexOf(self))
}
<span class="show" onclick="show(this)">show</span>
<span class="show" onclick="show(this)">show</span>
<span class="show" onclick="show(this)">show</span>
addEventListener
function show(){
var spans = document.querySelectorAll('.show');
console.log(Array.from(spans).indexOf(this))
}
function registerEvent(){
var spans = document.querySelectorAll('.show');
for(var i = 0; i< spans.length; i++){
spans[i].addEventListener("click", show)
}
}
registerEvent();
<span class="show" >show</span>
<span class="show" >show</span>
<span class="show" >show</span>
If you absolutely insist on using the onclick attribute (even though you don't really need to):
// get live collection so you only need to call this once
var liveCollection = document.getElementsByClassName('show');
function show(event) {
// convert liveCollection to array for `.indexOf()`
// either ES6
var shows = [...liveCollection];
// or ES5
var shows = Array.prototype.slice.call(liveCollection);
var index = shows.indexOf(event.currentTarget);
alert(index);
}
.show {
cursor: pointer;
}
<span class="show" onclick="show(event)">show</span>
<span class="show" onclick="show(event)">show</span>
<span class="show" onclick="show(event)">show</span>
However, I would rather recommend you use delegated events to handle dynamically added elements, as using the onclick attribute is considered bad practice, but don't just take my word for it.
// see below for how to select the best container
var container = document.body;
// single event listener for all dynamically added elements
container.addEventListener('click', function (event) {
if (event.target.classList.contains('show')) {
show.call(event.target, event);
}
});
// get live collection so you only need to call this once
var liveCollection = container.getElementsByClassName('show');
function show(event) {
// convert liveCollection to array for `.indexOf()`
// either ES6
var shows = [...liveCollection];
// or ES5
var shows = Array.prototype.slice.call(liveCollection);
// this === event.target
var index = shows.indexOf(this);
alert(index);
}
.show {
cursor: pointer;
}
<span class="show">show</span>
<span class="show">show</span>
<span class="show">show</span>
The nice thing about delegated event handling is you don't need an onclick attribute to handle dynamically added elements, so this will work best for your particular usage.
It is recommended, but not necessary, that the event listener for the delegated events is attached to the most immediate container of all the dynamically added elements, so that the click event doesn't have to bubble up all the way to the document.body. This makes the handling more efficient.
If you're JUST looking for the index, you can pass that to the show() method.
<span class="show" onclick="show(0)">show</span>
<span class="show" onclick="show(1)">show</span>
<span class="show" onclick="show(2)">show</span>

Removing a group of event listeners

I have a bunch of event listeners as illustrated below :
<a id="target0" >target0</a>
<div id="container">
<a id="target1" >target1</a>
<a id="target2" >target1</a>
<a id="target3" >target1</a>
.. more targets with event listeners
<script>
document.getElementById("#target1").addEventListener("click", ...);
document.getElementById("#target2").addEventListener("mouseover", ...);
document.getElementById("#target3").addEventListener("mouseout", ...);
...
</script>
</div>
<script>
document.getElementById("#target0").addEventListener("click", ...);
</script>
How do I write a function that will kill ALL DOM elements with event listeners binded to them ( with function reference or anonymous functions ) that are inside the container div ?
function killEvtListenersInsideContainerDiv(){
// loop to all DOM elements inside container that has event listeners and disable them
}
The container div is dynamically loaded by AJAX so the event listeners inside them will vary. The only one "static" is target0.
If you will have more than ont container you should add inside class for each a, something like this:
<div>
<a class="container1" id="#target1"></a>
...
</div>
To remove events there are two ways, I recommend the second because it easier and more modern:
1)Use native javascript, only if you can't use jquery for some technical reasons
var elements = document.getElementsByClassName("container1");
elements.forEach(function(elem, i, arr) {
element.removeEventListener("click");
}, this);
2)Use jquery:
//set
$('.container1').click(function() {
alert("click");
});
//remove
$('.container1').click(function(){}); //just empty function() :) the same as .click(null)
UPDATE: If you can't add class use this
javascript:
var elements = document.querySelector('#idDiv a'); //or '.classId a'
elements.forEach(function(elem, i, arr) {
element.removeEventListener("click");
}, this);
jquery
$('#idDiv a).click(function(){});
There is no way to check if event has been set before, if you really need you should implement this functionality with your hands(use flags for example). If function has already existed, new function will always override previous. In 99.9% cases it is enough.

How to change class name of two IDs at same time using js?

I have two IDs in same name ! if any one clicked among them , i need to change the class name of the both IDs. i know we should use ID for single use. Because of my situation(I have two classes for button ) so i have moved to ID.
Her is my code if i click one id that name only changes another one is remains same
<button class="success" id="1" onClick="reply(this.id)"> Added </button>
<button class="success" id="1" onClick="reply(this.id)"> Added </button>
js function
function reply(clicked_id)
{
document.getElementById(clicked_id).setAttribute('class', 'failed');
var el = document.getElementById(clicked_id);
if (el.firstChild.data == "Added")
{
el.firstChild.data = "Add";
}
}
if i use instead of 'class' to id while renaming class which one will be renamed success class or 'class name 1' ?
You can't. Getelementbyid will only return one element. Probably the first one.
Pure JS Is Second Example
My JS Fiddle Example: http://jsfiddle.net/eunzs7rz/
This example will use the class attribute only to perform the switching that you need, its a extremely basic example as do not want to go beyond what is needed... Also i forgot to remove the id's in the JS Fiddle Example.. so just ignore them
THE CSS:
.success {
background-color:#00f;
}
.failed {
background-color:#f00;
}
THE HTML:
<button class="success"> Added </button>
<button class="success"> Added </button>
THE JAVSCRIPT:
$(function() {
$(".success").click(function(){
Reply(this);
});
});
function Reply(oElm) {
$(oElm).attr('class', 'failed');
}
EDIT - PURE JAVASCRIPT VERSION
Sorry, did not think to check the post tags if this was pure JS. But here you go anyway ;)
<style>
.success {
background-color:#00f;
}
.failed {
background-color:#f00;
}
</style>
<button class="success" onclick="Reply(this)"> Added </button>
<button class="success" onclick="Reply(this)"> Added </button>
<script>
function Reply(oElm) {
oElm.className = 'failed';
}
</script>
THE MAIN THING HERE
Once you have the element either by using 'this' or by using 'getElementBy', you can then simply use ".className" to adjust the class attribute of the selected element.
As already explained by others, id is for single use and is quicker than using class or type. So even if you have a group, if only one is ever used.. use an id.
Then you use the object/reference of 'this' from an event on an element, in this case the onclick... that will send that variable to the function / code called.
So using 'this' is a preferred option as it will always reference the element that it is used/called from.
pass elemenet, not it's Id
<button class="success" id="1" onClick="reply(this)"> Added </button>
<button class="success" id="1" onClick="reply(this)"> Added </button>
function reply(elem)
{
$(elem).setAttribute('class', 'failed');
if (elem.firstChild.data == "Added")
{
elem.firstChild.data = "Add";
}
}
the ID attribute must be unique or else it will get the last defined element with that ID.
See this for reference.
Use a class instead of an id. ids are supposed to be unique in a dom tree.
html:
<button class="success" onClick="reply()"> Added </button>
<button class="success" onClick="reply()"> Added </button>
js:
var ary_success = document.querySelectorAll(".success"); // once and forever. If the set of elements changes, move into function `reply`
function reply () {
var elem;
var s_myclasses;
for (var i=0; i < ary_success.length; i++) {
elem = ary_success[i];
s_myclasses = elem.getAttribute('class');
s_myclasses = s_myclasses.replace ( /(success|failed)/g, '' );
s_myclasses = s_myclasses + ' failed';
elem.setAttribute('class', s_myclasses );
if ( elem.firstChild.data.indexOf("Added") !== -1) {
elem.firstChild.data = "Add";
}
}
}
Live Demo here.
Notes
Make sure that you set ary_successin the onload handler or in an appropriately placed script section - at the timeof execution the buttons must be present in the dom ! If in doubt, move it to the start of reply' body.
If you employ jquery, the code simplifies (well...) to:
$(document).ready( function () {
$(".success").on ( 'click', function ( eve ) {
$(".success").removeClass("success").addClass("failed");
$(".success *:first-child:contains('Added')").text(" Add ");
});
});
Updates
Notes, Live Demo
Iterator method changed, every not supported on test platform

Add Event Listeners via an Array and FOR Loop

Been having a bit of a problem for the last couple of days. I'm trying to streamline my code as much as possible and I have now got to the stage where I am trying to add Event Listeners via JavaScript so my HTML looks tidier.
-HTML Segment-
<input type="button" id="googleSearchButton" />
<input type="button" id="youtubeSearchButton" />
<input type="button" id="wikiSearchButton" />
<input type="button" id="facebookSearchButton" />
<input type="button" id="twitterSearchButton" />
<input type="button" id="tumblrSearchButton" />
<input type="button" id="dropboxSearchButton" />
JavaScript Segment
var contIDArray = ["google", "youtube", "wiki", "facebook", "twitter", "tumblr", "dropbox"];
window.load = initAll();
function initAll(){
applyProperties();
}
function applyProperties(){
for (var i = 0; i < contIDArray.length; i++){
addEventListeners(contIDArray[i] + "SearchButton");
}
}
function addEventListeners(id){
document.getElementById(id).addEventListener("click", testAlert(id), false);
}
function testAlert(id){
alert(id + " clicked")
}
The Theory
As, I hope, you can see, the FOR loop will loop until it runs out of values in the container Array. Each time it will output the place in the Array followed by "SearchButton". For example, the first time it loops it will output "googleSearchButton", the second time "youtubeSearchButton" and so forth.
Now, I know that the FOR loop works for applying properties because I use it to apply Button values and text box placeholder text in other segments of my project.
I have made it add a simple test function ("testAlert()") and set it to pass the id of the element that called it. I have set it up so once the event listeners have been added I can simply click on each button and it will alert its id and tell me that it has been clicked.
The Problem
Now, theoretically, I thought this would work. But it seems that the FOR loops fires the "addEventListeners" function, which, in turn, adds the event listener to fire "testAlert" on click. But it just fires the "testAlert" function as soon as it adds the event listener and does not fire when you click.
I apologise if this seems a bit much to take in, I always overdo the length of my explanation. Hopefully you'll be able to see what I'm trying to accomplish from my code, rather than my explanation.
Help would be much appreciated. :)
You're close here, but there are a few things wrong.
First, you can't just do id.addEventListener. You need to do document.getElementById(id).addEventListener. id is just a string, you need a DOMElement.
Second, when you do testAlert(id), you're running the function, then assigning its return value (undefined) as the event listener. You need to pass a function. Like so:
id.addEventListener("click", function(){
testAlert(this.id); // this is the DOMElement you clicked on
}, false);
Though I suggest adding a class to all your buttons, and then adding the event like that.
<input type="button" id="googleSearchButton" class="searchButton" />
<input type="button" id="youtubeSearchButton" class="searchButton" />
<input type="button" id="wikiSearchButton" class="searchButton" />
<input type="button" id="facebookSearchButton" class="searchButton" />
<input type="button" id="twitterSearchButton" class="searchButton" />
<input type="button" id="tumblrSearchButton" class="searchButton" />
<input type="button" id="dropboxSearchButton" class="searchButton" />
And then:
var buttons = document.getElementsByClassName('searchButton');
for(b in buttons){
if(buttons.hasOwnProperty(b)){
buttons[b].addEventListener("click", function(){
testAlert(this.id); // this is the DOMElement you clicked on
}, false);
}
}
NOTE: addEventListener and getElementsByClassName may not be available in all browsers (by that I mean they might not work in IE). This is why a lot of websites use a JavaScript library, like jQuery. jQuery handles all the cross-browser stuff for you. If you want to use jQuery, you could do this:
$('.searchButton').click(function(){
testAlert(this.id);
});
NOTE 2: In JavaScript, functions are variables, and can be passed as parameters.
document.getElementById(id).addEventListener('click', testAlert, false);
Notice how there are no () after testAlert, we are passing the function itself, when you do testAlert() you're passing its return value. If you do it this way, testAlert will need to be modified a bit:
function testAlert(){
alert(this.id + " clicked")
}
Change:
function addEventListeners(id){
id.addEventListener("click", testAlert(id), false);
}
for:
function addEventListeners(id){
document.getElementById(id).addEventListener("click", testAlert(id), false);
}
Otherwise you're applying addEventListener on a string.
In any case, replace addEventListener with an assignment to the event, like onClick.
id looks like a string to me. So instead do something like this:
function addEventListeners(id){
var obj = document.getElementById(id);
obj.addEventListener("click", testAlert(id), false);
}
Also, here is the working code:
http://jsfiddle.net/ZRZY9/2/
obj.addEventListener("click", function() { testAlert(id); }, true);
As Rocket mentions above "you're calling it and setting the event to the return value undefined".
The bad news is addEventListener() is currently not supported in Internet Explorer 7.
I ran through your code. The initial problem that I came across was that you were trying to find the elements in the document before they were created. window.onLoad fires before the page is complete. I tested this using the body tag's onload attribute and it works that way.
So, it's a combination of the aforementioned issue of your trying to find the element by using the "id" string and the function firing before the page was completely loaded.
Anyway, glad you got it working!
This is the javascript I had at the end:
<script>
var contIDArray = ["google", "youtube", "wiki", "facebook", "twitter", "tumblr", "dropbox"];
function initAll(){
applyProperties();
}
function applyProperties(){
for (var i = 0; i < contIDArray.length; i++){
var newString = contIDArray[i] + "SearchButton"
addEventListeners(newString);
}
}
function addEventListeners(id){
document.getElementById(id).addEventListener("click", testAlert, false);
}
function testAlert(){
alert(this.id + " clicked")
}
</script>

Check if checkboxes are selected on page load and add a class to parent html li

Im looking a way to do it with prototype, this js, needs to loads with the page and interate over all the elements (inputs - checkboxes, in this case) with the given id and assign a class to its parent <li></li>
The JS is:
function changeSelectionStyle(id) {
var inputId = id.substr(0,id.length-2);
if(document.getElementById(inputId).checked){document.getElementById(id).className = 'yes';}
alert(document.getElementById(inputId).checked);
/*
* if(document.getElementById(id).className != 'yes'){
* document.getElementById(id).className = 'yes';
* } else{document.getElementById(id).className = '';}
*/
}
And the HTML (piece of it) that interacts with this JS is:
<li id="selectedAuthorities-4_1li">
<input type="checkbox" id="selectedAuthorities-4_1" name="selectedAuthorities" value="ROLE_ADD_COMMENT_TO_CV" checked="checked" onclick="changeSelectionStyle(this.id + 'li'); checkFatherSelection(this.id);">
<a href="#" onclick="document.getElementById('selectedAuthorities-4_1').click(); return false;">
Agregar comentario
<samp><b></b>Permite agregar comentario en el detalle</samp>
</a>
</li>
After iteration, checking is the checkbox is checked, it has to add the class="yes" to the <li id="selectedAuthorities-4_1li">
To run something on page load (actually, when the DOM is ready), use this:
document.observe("dom:loaded", function() {
init()
});
You can then select all of the inputs and if they're checked, add a class name to its parent:
function init() {
$(document.body).select('input').each(function(element) {
if(element.checked) {
element.up().addClassName('yes')
}
})
}
If you want to get more specific and select the LI (in case there's other markup wrapping the input) you can substitute:
element.up('li').addClassName('yes')
Assuming that you can use prototype, and that you can change the HTML produced, i recommend you to give a class to every checkbox..here's what im thinking.
<li id="selectedAuthorities-4_1li">
<input type="checkbox" class="example_class">
...
</li>
Then using prototype you can iterate through every checkbox using the CSS selector $$
$$('.example_class');
Assuming you want to add a "selected" class to the checkbox parent, you can do:
$$('.example_class').each(function(element) {
if (element.checked) {
element.up().addClassName('selected');
}
});
To run all this code when the dom has finished loading, you can wrap all this code inside of this observer:
document.observe("dom:loaded", function() {
//the above code here
});
If you are willing to use prototype (or any javascript framework), invest some time in learning it, you will get a much better code than the one you produce without it

Categories

Resources