Trying to add event listeners on load to multiple elements using the class name

I have found various scripts and put together to try and achieve this.

The script below should register event listeners on load, but I am trying to do it for when I need multiple items with the same class name, e.g. a tooltip, or to ensure every button on the site had an Ajax loading image applied.

I believe I have everything sorted except ‘the add_event_multiple_classes’ function in my script.

Please help complete this and is this the best way to do it? It seem a lot easy to just use inline JS considering how much code this is taking.



if (typeof getElementsByClassName !== "function") {
  function getElementsByClassName(findClass, parent) {
    parent = parent || document;
    var elements = parent.getElementsByTagName('*');
    var matching = [];
    for(var i = 0, elementsLength = elements.length; i < elementsLength; i++){
      var regex = new RegExp('\\b' + findClass + '\\b')
      if (elements[i].className.match(regex)) {
        matching.push(elements[i]);
      }
    }
    return matching;
  }
}


if (window.addEventListener)
addEvent = function(ob, type, fn ) {
**ob.addEventListener(type, fn, false );
};
else if (document.attachEvent)
addEvent = function(ob, type, fn ) {
**var eProp = type + fn;
**ob['e'+eProp] = fn;
**ob[eProp] = function(){ob['e'+eProp]( window.event );};
**ob.attachEvent( 'on'+type, ob[eProp]);
};


function on_load_scripts() {
	
	//set some event listeners
	
	addEvent(document.getElementById('an-id'), 'click', some_javascript);
	
	add_event_multiple_classes ( 'submit-button', 'click', button_loading_image );
	
}

function add_event_multiple_classes(class, action, fn) {

	// THIS IS THE AREA I AM STRUGGLING ON
	
	//somehow get hold of all the classes with the provided class name and convert so
	//they can all be referenced and have an event listener registered to them.
	
        // What about searching for all matching class names and adding a unique id next to it (how would I do this?)

	/// ??????????????????????????

	for/foreach() { // for every class with the 'class' name

		addEvent(xxxxxxxxxxx, action, fn);

	}

}

function some_javascript() {

	//perform action

}

function button_loading_image() {

	//perform action

}

addEvent(window, 'load', on_load_scripts);


The add_event_multiple_classes function can make good use of the getElementsByClassName function, wich will give you an array of elements that match the class name. YOu can then loop through those elements applying the event function to them.

Thanks for the response,

I’ve sort of moved on with this and now have two problems:

(1) I’m trying to add an event, but specifying the function seems a problem.

My script is slightly different, but it is the same as this:

document.addEventListener(‘load’, your_function, false);

The problem is I want to use a variable instead of your_function.

Somehow I need the value in a variable to be the function name (so I have: var $a = “my_function”; and I want to execute the function named ‘my_function’ from this)

(2) I also understand the mouse event is passed automatically to the function.

So in this: document.addEventListener(‘load’, your_function, false);
…it’s automatically passed to ‘your_function’.

I need to get the div/tag anything that was clicked on so I can then go and do:

x.className = ‘new_class_name’; (with x being the reference to what was clicked on).

Thanks for any help.

No, don’t do that. function names can be assigned as variables too.


function my_function() {

}
var $a = my_function;

Or


var $a = function () {
    ...
};

Here’s the standard technique for doing that:


var eventHandler = function (evt) {
    evt = evt || window.event;
    var targ = evt.target || evt.srcElement;
    ...
}

Once you have the target element that was clicked on, you can then go ahead with targ.className = …

Thanks Paul, you clearly know your stuff.

As for the first one, I don;t really want to do it as a variable it’s just that my script ended up that way.

This is what I have:




var events_list = new Array();
events_list['click'] = "perform_action";
events_list['mouseover'] = "something_else";
add_event_multiple_classes ( 'class-name', 'new-id-name', events_list );
	

function add_event_multiple_classes(classNm, new_id_name, events_list) {

   counter = 1;

   var elements = getElementsByClassName(document, classNm),
       n = elements.length;
   for (var i = 0; i < n; i++) {
     var e = elements[i];
     this_new_id_name = new_id_name + "_" + counter++;
     //add id element to it
	 e.id = this_new_id_name;
     //add events
     for (action in events_list) {
       fn = events_list[action];
   	   addEvent(document.getElementById(this_new_id_name), action, **** THIS NEEDS TO BE THE VALUE IN VARIABLE fn *****);
   	 }
   }
	
}

function perform_action() {

}


Any ideas?

After some simplifications, we end up with this:


function addEventsToElement(el, eventsList) {
    var action;
    for (action in eventsList) {
        if (eventsList.hasOwnProperty(action)) {
            addEvent(el, action, eventsList[action]);
        }
    }
}

function addEventMultipleClasses(className, eventsList) {
    var elements = getElementsByClassName(document, className),
        elementsLength = elements.length,
        i;
    for (i = 0; i < elementsLength; i += 1) {
        addEventsToElement(elements[i], eventsList);
    }
}

function performAction() {

}

function somethingElse() {

}

addEventMultipleClasses('class-name', {
    click: performAction,
    mouseover: somethingElse
});

As an example of one of the changes, you shouldn’t declare variables in the middle of a function. Even when they’re declared in the middle, their declaration is automatically hoisted to the top of the function. So to reduce confusion, always declare variables in one block at the start of the function.

Perfect. Thanks again, and thanks for the extra advice.

I thought it best to continue this thread as my new question partly relates to this.

I simply want to create a standard HTML link but also append JS functionality.

<a href=“?page=2”>Show More</a>

I want to use the code above to append a simple show_more() function, which I can do but it always fires the ?page=2 part after running the JS. I know a simple onclick=“return false” sorts this out but considering I’m trying to do this properly that obviously should;t be used.

If I do this:

function show_more() {
//the show more code

return false;
}

…then the return false in the function does nothing.

Where do I put the return false to prevent the HTML hyperlink from firing if JS is enabled?

That depends on how you are attaching the show_more function to the event.

The most appropriate way is to attach it to the onclick event of the link itself. Not via an inline HTML event attribute, but via scripting instead.


<a id="showmore" href="?page=2">Show More</a>


function show_more() {
    ...
}

document.getElementById('showmore').onclick = show_more;

Notice that it’s show_more without parenthesis that is assigned to the onclick event.
If it were show_more() with the parenthesis, then you would be assigning to the event the returned value from the function, that being undefined or false, depending on if the function returns nothing or false.

So instead, by assigning just a reference to the function itself to the onclick event, the function can return false to prevent the default behaviour (of the web browser following the link) from occurring.

Thanks again Paul.

I am hoping to use the code you posted on Dec 23, 2011 at 20:24 in this topic.

Hopefully, I can just add this to the set of code:



<script>

addEventsToElement ('js-show-more', {click: show_more} ); // note, I definitely use show_more and not show_more()

function show_more() {

 // do the show more stuff

 return false;

}

</script>

<a href="?pg=2" class="js-show-more">SHOW MORE</a>


…If I do this, the JS runs, but it also fires the ?pg=2 part. Is it possible to do this with the set of code you provided rather than using document.getElementById(‘showmore’).onclick = show_more; (although I would guess both should do the same thing, so I’m a little confused there)?

Thanks again for your help!

Can you put up a sample page so that some testing can occur? There is a jsfiddle.net. If you do, in the choose framework part on the left, change onload to no wrap (body) and change mootools to be instead no library.

Hi,

Is this ok as a test?

http://page-test.co.uk/test.html

Oh that’s right, you’re using the advanced event registration technique here, which is hampered by a Microsoft problem.
On Internet Explorer when attachEvent is used, the this keyword becomes broken and points to the window object, instead of the element that triggered the event.

So, we can make use of either the preventDefault() method from the event, or for IE we can set the returnValue property on the event itself.

The following should do the trick:


function run_test(evt) {
    evt = evt || window.event;
    evt.preventDefault();
    window.event.returnValue = false;
    return false;
}

Thanks Paul.

So, would this be ok (for all browsers etc.)?



function run_test(evt) {
 alert("TEST 2");
 return return_false(evt);
}


function return_false(evt) {
 evt = evt || window.event;
 evt.preventDefault();
 window.event.returnValue = false;
 return false;   
}


…I can then use the return_false() function for all other uses.

I’ve updated http://page-test.co.uk/test.html and it seems to work fine.

It would be, yes. Although you may want to change the function name from return_false to prevent_default, or some other name that more appropriately describes the purpose of the function.

That way, you’ll have a better way to understand what the code is doing, without needing to track down and study the function, when you see:


return prevent_default(evt);

Thanks Paul,

I’ve changed it on my actual code, although I thought return_false was a good name for it!! (obviously not, and I understand now).

I have another issue I have just come across using the code Paul helped me with above.

I need to pass a value from PHP using the set of functions at http://page-test.co.uk/test.html but can’t see how I would do it.

So,


<input type="button" onclick="run_function_name('<?php echo $value_from_php; ?>')" />

… would work fine, but how would I fit that into the code at http://page-test.co.uk/test.html? (snippet shown below):


add_event_by_class('test', {click: run_test} );

…especially since I include the JS in external files.

…even if it wasn’t included in an external JS file, I’m not sure if the below would work anyway as I don’t think you can include the brackets:


add_event_by_class('test', {click: run_test('<?php echo %value_from_php; ?>')} );

The only way I can think of is to include values from PHP in a hidden text field and call them from the function that the above snippet runs (so in this case the function is called ‘run_test’).

Thanks in advance.

One it is set as the value of the checkbox, any scripting code can retrieve that value from the checkbox, so you only need to set it the once.

I’ve just realised, this does’t work in IE7 or Firefox 8 (not tested later versions of IE, but guessing if I get IE7 then rest would be ok).

No problem in the latest Chrome / Safari.

I’ve stripped out everything from the test below to isolate as best as possible.

http://page-test.co.uk/test.html

That will be the evt.preventDefault()

You can prevent older web browsers from having troubles with that, by checking if it exists first before running it.


if (evt.preventDefault) {
    evt.preventDefault();
}

Thanks again!!