Modify onClick attribute via Javascript - javascript

I want to add more functionality to an already rendered button code.
The only way to do it is Javascript since it's rendered before from the server and I only have access to the JSP.
Here is what I have now, but it's not working, it just displays the same message twice. And if I click the button, the alert "hello" is not shown.
<script type="text/javascript">
function addEventBefore(element, type, fn) {
var old = element['on' + type] || function() {};
element['on' + type] = function () { fn(); old(); };
}
function sayHello(){
alert('hello');
}
var arr = document.getElementsByTagName("button");
for (i = 0; i < arr.length; i++) {
if (arr[i].getAttribute("name") == "Complete"){
var theclick = arr[i].getAttribute("onClick");
alert(theclick);
addEventBefore(arr[i], 'Click', sayHello);
var theclick2 = arr[i].getAttribute("onClick");
alert(theclick2);
}
}
</script>
This is the rendered source code of the page:
<td valign='top' align='right' >
<button name="Complete" title="The complete Button"
onClick="document.forms.TaskInfoPage.action='http://prodserver:8080/Approval.jsp?windowId=dd4c0&eventTarget=stepApproval&eventName=Complete'
document.forms.InfoPage.submit();return false;">Complete</button>
</td>
So, I want to place the sayHello() before it does the action on the onClick.

It's onclick rather than onClick, so you need this:
addEventBefore(arr[i], 'click', sayHello);
// ^ lower case here
The attribute itself still isn't going to display anything different, but the event themselves will work, you can test it here. Also, to maintain this for your event handlers, I recommend you at least .call() the functions, like this:
function addEventBefore(element, type, fn) {
var old = element['on' + type] || function() {};
element['on' + type] = function () { fn.call(this); old.call(this); };
}

Related

Javascript fires twice Wordpress admin metabox

I added a inline Javascript code to my metabox callback function.
add_action( 'add_meta_boxes', function() {
add_meta_box( 'catalog-item', 'Gegevens', 'catalog_details_callback', 'catalog', 'advanced' );
});
function catalog_details_callback( $post ) {
<input type="text" class="price" name="price" id="price"/>
<script type="text/javascript">
document.getElementById('price').onfocusout = function() {
var regex = /^(\d+[,]+\d{2})$/;
if (regex.test(this.value) == false ) {
this.value = this.value.replace(/([^(\d|,)]|,{2})/g, "");
}
var before = this.value.replace(",", ".");
var roundoff = parseFloat(before).toFixed(2);
var after = roundoff.replace(".", ",");
alert(after);
}
</script>
}
If the function is triggered the function fires the alert twice.
Does anybody know how I fix this?
There could be multiple reason for this:
Please check if you have multiple event listeners. If so, try to check your condition. understand about event listeners here: https://developer.mozilla.org/en-US/docs/Web/API/Element/focusout_event
onfocusout bubbles, means if you have any event written on parent as well as child then both gets called. try to add
document.getElementById('price').onfocusout = function(event) {
event.preventDefault();
event.stopPropagation();
var regex = /^(\d+[,]+\d{2})$/;
if (regex.test(this.value) == false ) {
this.value = this.value.replace(/([^(\d|,)]|,{2})/g, "");
}
var before = this.value.replace(",", ".");
var roundoff = parseFloat(before).toFixed(2);
var after = roundoff.replace(".", ",");
alert(after);
}
If still issue persists then try to add the debugger in the function can check the call trace in google developers console.
I had the same issue with Wordpress.
This works for me
const price_field = document.getElementById('price');
price_field.addEventListener('focusout', (event) => {
var regex = /^(\d+[,]+\d{2})$/;
if (regex.test(price_field.value) == false ) {
this.value = price_field.value.replace(/([^(\d|,)]|,{2})/g, "");
}
var before = price_field.value.replace(",", ".");
var roundoff = parseFloat(before).toFixed(2);
var after = roundoff.replace(".", ",");
price_field.value = after;
alert(after);
});

How to disable all ng-click and ng-submit event

is there any way, how can I globally (in service) disable and enable all ng-click and ng-submit events?
For example when user is offline I want to disable all actions till he gets connection back..
I tried to bind all elements with an onClick event which will call stopImmediatePropagation but it didn't work..
$('*[ng-click]').click(function( event ) {
event.stopImmediatePropagation();
});
Also this question is a little bit different from this one:
Disable ng-click on certain conditions of application for all types of element
I'd like to disable/enable all events in APP globally from service, I'm not able to modify all ng-* calls on all elements in the APP..
Try including a return false too:
$('*[ng-click]').click(function( event ) {
event.stopImmediatePropagation();
return false;
});
Snippet
The below snippet demonstrates that multiple event handlers attached to a single <a> works too.
$(function () {
$("a").click(function () {
alert("Hello!");
return false;
});
$("a").click(function () {
alert("Bye!");
return false;
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
Click Me
So finally I end up with temporarily disabling all events on the page using jquery..
I got inspired from this plugin http://ignitersworld.com/lab/eventPause.html which for some reason did not work (without any error)
So I took main parts and put it to this class which is working now using jquery v2.1.1:
var EventManager = function() {
var self = this;
var nullFun=function(){};
var getIndex = function(array,value){
for(var i=0; i< array.length; i++){
if(array[i]==value){
return i;
}
}
return -1;
};
this.pauseEvent = function(elm,eventAry){
var events = $._data(elm, "events");
if (events) {
$.each(events, function(type, definition) {
if((getIndex(eventAry,type)!=-1)||(eventAry=='')){
$.each(definition, function(index, event) {
if (event.handler.toString() != nullFun.toString()){
if(!$._iwEventPause) $._iwEventPause = {};
$._iwEventPause["iw-event" + event.guid] = event.handler;
event.handler = nullFun;
}
})
}
})
}
};
this.activeEvent = function(elm,eventAry){
var events = $._data(elm, "events");
if (events) {
$.each(events, function(type, definition) {
if((getIndex(eventAry,type)!=-1)||(eventAry=='')){
$.each(definition, function(index, event) {
if (event.handler.toString() == nullFun.toString()){
event.handler = $._iwEventPause["iw-event" + event.guid];
}
})
}
})
}
};
this.disableAll = function(el) {
el = el || $('*');
el.each(function() {
self.pauseEvent($(this)[0], '');
});
self.pauseEvent($(window)[0], '');
};
this.enableAll = function(el) {
el = el || $('*');
el.each(function() {
self.activeEvent($(this)[0], '');
});
self.activeEvent($(window)[0], '');
};
return this;
};
var eManager = new EventManager();
eManager.disableAll();
eManager.enableAll();
This will go through window object and all elements on the page, move their event handlers away to _iwEventPause object and replace handlers with dummy function.. When enabling, it will move handlers back so they get normally called..
This solution does not handle event handlers added after disabling..

Cannot detect if window is loaded

I'm currently working on counting the number of opened tabs on my application. but my problem is it seems that my script won't detect events onload. Here is my code.
I'm using HTML5 web storage and native js. I'm not using jQuery to understand more on native js.
(function(w) {
function Tabz(win, key) {
this.name = '';
this.storageKey = key;
if(win.name != '')
this.name = win.name;
else {
var windowArr = JSON.parse(localStorage.getItem(key)) || [];
this.name = "tabz_"+ windowArr.length;
win.name = this.name;
windowArr.push(this.name);
localStorage.setItem(this.storageKey, JSON.stringify(windowArr) );
}
}
Tabz.prototype.getStorage = function() {
return localStorage.getItem(this.storageKey);
}
Tabz.prototype.removeWindow = function() {
//remove window function here
}
var newWindow = new Tabz(w, 'counter');
window.load = function() {
var count = JSON.parse(newWindow.getStorage()).length;
alert(count!); // this wont execute so that I can check the count.
}
})(window);
Your issue is on this line:
window.load = function() {
This will add a load property to the window, not add an event listener. I think you are looking for onload.
window.onload = function() {
Incidentally, using event properties is considered bad-practice. Using addEventListener would be better.
window.addEventListener("load", function(){
//Do stuff...
});

Uncaught ReferenceError: X is not defined

This code is being used on a Chrome Extension.
When I call the "showOrHideYT()" function, I get a
"Uncaught ReferenceError: showOrHideYT is not defined | (anonymous
function) | onclick"
This code will search for youtube links in a page, and it will add a button (it's really a div with an event) next to the link to show the iframe with the embedded video, pretty much like Reddit Enhancement Suite. Consider the code, per se, incomplete. I just want to know what am i missing when i call the "showOrHideYT(frameZES12345)" function.
if needed, i can provide manifest.json.
Thanks
function showOrHideYT(id)
{
var YTvidWidth = 420;
var YTvidHeight = 315;
frameYT=getElementById(id);
console.log(frameYT.style.visibility);
if (frameYT.style.visibility == "hidden")
{
frameYT.style.width = YTvidWidth+"px";
frameYT.style.height = YTvidHeight+"px";
frameYT.style.visibility = "visible";
}
if (frameYT.style.visibility == "visible")
{
frameYT.style.width = "0px";
frameYT.style.height = "0px";
frameYT.style.visibility = "hidden";
}
};
// DOM utility functions
function insertAfter( referenceNode, newNode ) {
if ((typeof(referenceNode) == 'undefined') || (referenceNode == null)) {
console.log(arguments.callee.caller);
} else if ((typeof(referenceNode.parentNode) != 'undefined') && (typeof(referenceNode.nextSibling) != 'undefined')) {
if (referenceNode.parentNode == null) {
console.log(arguments.callee.caller);
} else {
referenceNode.parentNode.insertBefore( newNode, referenceNode.nextSibling );
}
}
};
function createElementWithID(elementType, id, classname) {
obj = document.createElement(elementType);
if (id != null) {
obj.setAttribute('id', id);
}
if ((typeof(classname) != 'undefined') && (classname != '')) {
obj.setAttribute('class', classname);
}
return obj;
};
///////////////////////////////////////
$(document).ready(function() {
var vidWidth = 420;
var vidHeight = 315;
var linksSemID = document.getElementsByTagName("a") ;
for (var i = 0; i < linksSemID.length; i++){
if (/id=$/.test(linksSemID[i].href)) links[i].href += "1";
}
i=0;
var youTubeRegExp = /(?:v=)([\w\-]+)/g;
var forEach = Array.prototype.forEach;
var linkArray = document.getElementsByTagName('a');
forEach.call(linkArray, function(link){
linkArray.id="zes" + i++;
var linkTarget = link.getAttribute('href');
if (linkTarget!=null)
{
if (linkTarget.search(youTubeRegExp) !=-1)
{
console.log (linkTarget);
idVideo=linkTarget.match(/(?:v=)([\w\-]+)/g);
//idVideo = idVideo.replace("v=", "");
//add buton
botaoMais = document.createElement('DIV');
botaoMais.setAttribute('class','expando-button collapsed video');
botaoMais.setAttribute('onclick','showOrHideYT(frameZES'+ i +')');
insertAfter(link, botaoMais);
//add iframe
ifrm = document.createElement('IFRAME');
ifrm.setAttribute('src', 'http://www.youtube.com/embed/'+ idVideo);
ifrm.style.width = '0px';
ifrm.style.height = '0px';
ifrm.style.frameborder='0px';
ifrm.style.visibility = 'hidden';
ifrm.setAttribute('id', 'frameZES' + i);
insertAfter(link, ifrm);
}
}
});
});
When you use setAttribute with a string, the event will be executed in the context of the page. The functions which are defined in a Content script are executed in a sandboxed scope. So, you have to pass a function reference, instead of a string:
Replace:
botaoMais.setAttribute('onclick','showOrHideYT(frameZES'+ i +')');
With:
botaoMais.addEventListener('click', (function(i) {
return function() {
showOrHideYT("frameZES"+ i);
};
})(i));
Explanation of code:
(function(i) { ..})(i) is used to preserve the value of i for each event.
Inside this self-invoking function, another function is returned, used as an event listener to click.
I see that you are using jQuery in your code. I personally think if we are using a library like jQuery, then we should not mix the native javascript code and jQuery code.
You can use jQuery bind to bind your the functions you need to call on dom ready.
Read below to know more.
suppose you want to call a javascript function on a button click, Here is the HTML for the same.
<div id="clickme">
<input id= "clickmebutton" type="button" value = "clickme" />
</div>
suppose "test" is the function you need to call, here is the code for test function.
function test() {
alert("hello");
}
you now need to bind the test function on the button click.
$(document).ready(function() {
$("#clickmebutton").bind("click", function(){
// do what ever you want to do here
test();
});
});

What is a good generic sibling control Javascript communication strategy?

I'm building a webpage that is composed of several controls, and trying to come up with an effective somewhat generic client side sibling control communication model. One of the controls is the menu control. Whenever an item is clicked in here I wanted to expose a custom client side event that other controls can subscribe to, so that I can achieve a loosely coupled sibling control communication model.
To that end I've created a simple Javascript event collection class (code below) that acts as like a hub for control event registration and event subscription. This code certainly gets the job done, but my question is is there a better more elegant way to do this in terms of best practices or tools, or is this just a fools errand?
/// Event collection object - acts as the hub for control communication.
function ClientEventCollection()
{
this.ClientEvents = {};
this.RegisterEvent = _RegisterEvent;
this.AttachToEvent = _AttachToEvent;
this.FireEvent = _FireEvent;
function _RegisterEvent(eventKey)
{
if (!this.ClientEvents[eventKey])
this.ClientEvents[eventKey] = [];
}
function _AttachToEvent(eventKey, handlerFunc)
{
if (this.ClientEvents[eventKey])
this.ClientEvents[eventKey][this.ClientEvents[eventKey].length] = handlerFunc;
}
function _FireEvent(eventKey, triggerId, contextData )
{
if (this.ClientEvents[eventKey])
{
for (var i = 0; i < this.ClientEvents[eventKey].length; i++)
{
var fn = this.ClientEvents[eventKey][i];
if (fn)
fn(triggerId, contextData);
}
}
}
}
// load new collection instance.
var myClientEvents = new bsdClientEventCollection();
// register events specific to the control that owns it, this will be emitted by each respective control.
myClientEvents.RegisterEvent("menu-item-clicked");
Here is the part where this code above is consumed by source and subscriber controls.
// menu control
$(document).ready(function()
{
$(".menu > a").click( function(event)
{
//event.preventDefault();
myClientEvents.FireEvent("menu-item-clicked", $(this).attr("id"), null);
});
});
<div style="float: left;" class="menu">
<a id="1" href="#">Menu Item1</a><br />
<a id="2" href="#">Menu Item2</a><br />
<a id="3" href="#">Menu Item3</a><br />
<a id="4" href="#">Menu Item4</a><br />
</div>
// event subscriber control
$(document).ready(function()
{
myClientEvents.AttachToEvent("menu-item-clicked", menuItemChanged);
myClientEvents.AttachToEvent("menu-item-clicked", menuItemChanged2);
myClientEvents.AttachToEvent("menu-item-clicked", menuItemChanged3);
});
function menuItemChanged(id, contextData)
{
alert('menuItemChanged ' + id);
}
function menuItemChanged2(id, contextData)
{
alert('menuItemChanged2 ' + id);
}
function menuItemChanged3(id, contextData)
{
alert('menuItemChanged3 ' + id);
}
jQuery's event system can pass additional handler parameters when you trigger events. We also separate the control namespace from jQuery selectors by creating a registry that maps control names to selectors. To deal with handlers binding to a control before the control is registered, we implement a binding delay mechanism.
var controls = {};
(function ControlRegistry(controls) {
controls.items = {};
function bindNow(selector, event, eventData, handler) {
$(selector).bind(event, eventData, handler);
}
function delayBinding(queue, event, eventData, handler) {
queue.push([event, eventData, handler]);
}
function bindAll(queue, selector) {
for (var i=0; i<queue.length; ++i) {
var args = queue[i];
args.unshift(selector);
bindNow.apply(controls, args);
}
}
controls.register = function (name, selector) {
if (typeof this.items[name] == 'object') {
bindAll(this.items[name], selector);
}
this.items[name] = selector;
};
controls.bind = function (control, event, eventData, handler) {
jQuery.isFunction( eventData ) {
handler = eventData;
eventData = null;
}
switch (typeof this.items[control]) {
case 'undefined':
this.items[control] = [];
// FALLTHRU
case 'object':
delayBinding(this.items[control], event, eventData, handler);
break;
case 'string':
bindNow(this.items[control], event, eventData, handler);
break;
}
}
})(controls);
$(document).ready(function()
{
controls.register('menuItem', '.menu > a');
$(".menu > a").click( function(event)
{
$(this).trigger("menu-item-clicked", [$(this).attr("id"), 'cow', 'moo']);
});
});
Elsewhere:
function menuItemChanged(evt, id, animal, speech)
{
alert('menuItemChanged ' + id
+ '\nThe ' + animal + ' says "' + speech + '."');
}
function menuItemChanged2(evt, id, animal, speech))
{
alert('menuItemChanged2 ' + id
+ '\nThe ' + animal + ' says "' + speech + '."');
}
function menuItemChanged3(evt, id, animal, speech))
{
alert('menuItemChanged3 ' + id
+ '\nThe ' + animal + ' says "' + speech + '."');
}
$(document).ready(function()
{
controls.bind('menuItem', "menu-item-clicked", menuItemChanged);
controls.bind('menuItem', "menu-item-clicked", menuItemChanged2);
controls.bind('menuItem', "menu-item-clicked", menuItemChanged3);
});
Update
if you include the restriction that a control be registered before handlers are bound to its events, the control registry can be vastly simplified:
var controls = {
register: function (name, selector) {
if (typeof this[name] != 'function') {
this[name] = selector;
}
};
};
...
controls.register('menuItem', '.menu > a');
$(document).ready(function()
{
$(".menu > a").click( function(event)
{
$(this).trigger("menu-item-clicked", [$(this).attr("id"), 'cow', 'moo']);
});
});
...
$(document).ready(function()
{
$(controls.menuItem).bind("menu-item-clicked", menuItemChanged);
$(controls.menuItem).bind("menu-item-clicked", menuItemChanged2);
$(controls.menuItem).bind("menu-item-clicked", menuItemChanged3);
});
This is a reasonable restriction, as you can register early (within the script for the control) and bind late (in $(document).ready).
My original solution ended up being the right one for me because it achieves the loose coupling I was after in a straight-forward and simple way.

Categories