Reputation: 3204
Now first off before everyone gives the answer "don't use inline events," let me first say that I am not using inline events, I'm simply wanting to clean up what others have coded without being able to go in and just fix all of the code.
So, I wrote a little function to transfer the inline events to jQuery events instead, but it uses new Function() in part. It works, but I know it can be cleaned up in general to be nicer.
var transferInlineEvents = function(element){
var attributes = $(element).get(0).attributes;
$(attributes).each(function(i){
if (/^on/.test(this.name)) {
var handler = this.name.replace(/^on/, '');
var evt = this.value;
evt = evt.replace('$(this)', "$('#"+$(element).attr('id')+"')");
$(element).on(handler, function(){
var newEvent = (jQuery.support.boxModel) ? new Function([evt]) : evt;
newEvent();
});
$(element).removeAttr(this.name);
}
});
};
This would, and does essentially make this:
<input type="text" name="myinput" id="myinput" onclick="$(this).val('');" value="Enter your name..." />
Into this:
<input type="text" name="myinput" id="myinput" value="Enter your name..." />
$('#myinput').on('focus', function(){
$('#myinput').val('');
});
I don't know how to not use new Function so it works in all of the browsers and I don't know how to make it so I don't need to replace this.
Any help is appreciated.
Upvotes: 0
Views: 112
Reputation: 3204
After a lot of reading and searching it seems that there is no way to do this without either using eval() or new Function(). And since the latter is slightly better I'm going with that. Also, keep in mind that eval() and new Function() are only insecure when they are part of a input that can be manipulated by a user. This is not the case in my situation, so I believe this to be safe.
This will convert all inline events with the 'on' prefix to be jQuery bound events and remove the inline events.
;(function ($) {
$.fn.transferInlineEvents = function () {
var el = this;
var attributes = el.get(0).attributes;
$(attributes).each(function(i){
var oEvent = this.name;
var oFunc = $.trim(this.value);
if (/^on/.test(oEvent)) {
var nEvent = oEvent.replace(/^on/, '');
var nFunc = new Function(oFunc);
el.removeAttr(oEvent);
el.bind(nEvent, nFunc);
}
});
};
})(jQuery);
Test it here: http://jsfiddle.net/9vXtm/2/
Upvotes: 0
Reputation: 19581
Following your quote : Any help is appreciated.
Be careful the code below, uses eval
, which is not very light part of JavaScript programming and is known to be unsecure.
I think this would be cross-browser enough :
var transferInlineEvents = function(element){
var attributes = $(element).get(0).attributes;
$(attributes).each(function(i){
if (/^on/.test(this.name)) {
var handler = this.name.replace(/^on/, '');
var evt = this.value;
evt = evt.replace('$(this)', "$('#"+$(element).attr('id')+"')");
$(element).on(handler, function() {
console.log('event activated', evt);
var newEvent = eval('(function(){'+evt+'});');
newEvent();
});
$(element).removeAttr(this.name);
}
});
};
$("input").each(function(i, element) {
transferInlineEvents(element);
});
You can find a demo here.
Another approach, if you know your inline code is simple and not so complicated is to regexp check every argument of your inline function and create a separate function for each.
This is generally safe, but it won't work on complicated code.
var transferInlineEvents = function(element){
var attributes = $(element).get(0).attributes;
$(attributes).each(function(i){
if (/^on/.test(this.name)) {
var handler = this.name.replace(/^on/, '');
var evt = this.value;
var actions = evt.split(';');
var functions = [];
for ( var action = 0; action < actions.length; action++ ) {
var methods = actions[action].split('.');
for ( var method = 1; method < methods.length; method++ ) {
/* If it is val attribute */
if ( /^val\(/.test(methods[method]) ) {
var value = methods[method].substr(4).replace(/^('|")/, '').replace(/\)$/, '').replace(/('|")$/, '');
functions.push( function() { $('#'+element.id).val(value) } );
}
}
}
if ( functions.length > 0 ) {
$(element).on(handler, function() {
for ( fn = 0; fn < functions.length; fn++ ) {
console.log('fireing up function');
functions[fn]();
}
});
$(element).removeAttr(this.name);
}
}
});
};
Demo : http://jsbin.com/osevop/1/edit
Upvotes: 0
Reputation: 15404
This will cost you a good bit of effort, but will buy you nothing.
One point of not applying event handlers inline is to keep semantics (markup) separate from page functionality (scripts). The other is to defer script execution until after all other page-load activities have occurred.
Once you start manipulating event handlers after page-load, neither of these objectives are achievable. If you go ahead with this approach, you still have the mess in your source code (I gurantee if you view source you will still see the inline handlers) and the browser will still be attaching those handlers to the elements in the DOM before it removes and re-attaches them.
The only way to truly clean things up is to do it in your source code, on the back end.
Upvotes: 3