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..
Related
I have a local page to help in HTML and JavaScript that helps me with some basic tasks at work. I've been going back over my code and rewriting it to use best practices, since it helps me learn, and recently I've been trying to study namespacing and put it to use by rewriting the common page functions and event listeners.
window.onload = (function() {
var automationPageWrapper = (function() {
var self = {}
self.evntListeners = {
btnTextChange: function() {
// Code that changes button text when clicked
},
btnColorChange: function(formID) {
// Code that iterates through buttons with a certain name
// and makes them all the same default color
}
}
self.listeners = {
btnListeners: function() {
// Add all event listeners having to do with buttons here
}
}
return self;
});
automationPageWrapper.listeners.btnListeners();
});
Why isn't this attaching the event listeners?
Is there a better way to be formatting/calling this?
Is this a professional method for setting up JavaScript code?
I tested the event listeners by taking the functions and posting them into the Chrome console, so I think they work.
The full text, since some people like reading through all of it:
// Global namespace for the Page Functions
window.addEventListener("onload", function() {
var automationPageWrapper = (function() {
var self = {};
// Namespace for event listeners
self.evtListeners = {
// Function to change the color of a selected button
btnColorChange: function(formName) {
var elementsByName = document.getElementsByName(formName);
for (var i = 0; i < elementsByName.length; i++) {
if (elementsByName[i].className == "active") {
elementsByName[i].className = "inactive";
break;
}
}
},
// Add the event listeners
listeners: {
btnListeners: (function () {
document.getElementById('sidebar').addEventListener("click", function(e){
self.evtListeners.btnColorChange('sidebuttons');
e.target.className = "active";
});
})()
}
}
return self;
})();
automationPageWrapper.listeners.btnColorChange();
});
I am trying to change this demo:
http://maxwells.github.io/bootstrap-tags.html
into a responsive version in which I can set it to readOnly and remove it from readOnly as I like. This code:
var alltags = ["new tag", "testtag", "tets", "wawa", "wtf", "wtf2"];
$(document).ready(function() {
var tagbox = $('#my-tag-list').tags({
suggestions: alltags
});
var tagenable = true;
$('#my-tag-list').focusout(function() {
if (tagenable) {
tagbox.readOnly = true;
$('#my-tag-list').empty();
tagbox.init();
tagenable = false;
}
});
$('#my-tag-list').click(function() {
if(!tagenable) {
tagbox.readOnly = false;
$('#my-tag-list').empty();
tagbox.init();
tagenable = true;
}
});
});
seems to work fairly well, it makes everything readonly after focusout and editable when I click it. However, the editing does not work since I cannot insert new tags nor delete them (seems to be like event handling was lost or something like that).
I am guessing that emptying the #my-tag-list div is causing this, but I cannot yet find a way to use for instance "detach" instead that removes everything inside (not the element itself) and putting it back in again.
I tried to make a JS Fiddle, but it isn't really working so well yet:
http://jsfiddle.net/tomzooi/cLxz0L06/
The thing that does work is a save of the entire website, which is here:
https://www.dropbox.com/sh/ldbfqjol3pppu2k/AABhuJA4A6j9XTxUKBEzoH6za?dl=0
this link has the unminimized JS of the bootstrap-tags stuff I am using:
https://github.com/maxwells/bootstrap-tags/blob/master/dist/js/bootstrap-tags.js
So far I managed to do this with some modifications of the bootstrap javascript code. I use two different tagbox which I hide and unhide with some click events.
var tagbox = $('#my-tag-list').tags({
suggestions: alltags,
tagData: tmp_tags,
afterAddingTag: function(tag) { tagboxro.addTag(tag); },
afterDeletingTag: function(tag) {tagboxro.removeTag(tag); }
});
var tagboxro = $('#my-tag-listro').tags({
suggestions: alltags,
tagData: tmp_tags,
readOnly: 'true',
tagSize: 'sm',
tagClass: 'btn-info pull-right'
});
$(document).mouseup(function (e) {
var container = $("#my-tag-list");
if (!container.is(e.target) // if the target of the click isn't the container...
&& container.has(e.target).length === 0) { // ... nor a descendant of the container
if (tagsave) {
$("#my-tag-listro").show();
$("#my-tag-list").hide();
var tags = tagbox.getTags();
$.post("%basedir%/save.php", {
editorID:"new_tags",
tags:tags
}, function(data,status){
//alert("Data: " + data + "\nStatus: " + status);
});
tagsave = false;
}
}
});
$('#my-tag-listro').click(function() {
tagsave = true;
//$(".tag-list").toggle();
$("#my-tag-list").show();
$("#my-tag-listro").hide();
});
I had to modify the code of bootstrap-tags.js to allow for this since it normally deletes all of the usefull functions when it is considered readonly in the init function:
if (this.readOnly) {
this.renderReadOnly();
this.removeTag = function(tag) {
if (_this.tagsArray.indexOf(tag) > -1) {
_this.tagsArray.splice(_this.tagsArray.indexOf(tag), 1);
_this.renderReadOnly();
}
return _this;
};
this.removeTagClicked = function() {};
this.removeLastTag = function() {};
this.addTag = function(tag) {
_this.tagsArray.push(tag);
_this.renderReadOnly();
return _this;
};
this.addTagWithContent = function() {};
this.renameTag = function() {};
return this.setPopover = function() {};
}
would be awesome if this feature was incorporated in a somewhat less hacky way though :)
I'm writing a jQuery plugin where the events which start/stop the plugin are customisable, so potentially the same event could both start and stop the plugin (e.g. click to start and click to stop).
What's an elegant way, ideally not involving timeouts or unbinding and rebinding of listeners (and not too many "isPlaying" "isBeingStarted" flags etc..) to make sure the correct callback is called
(Note: When I posted this answer, the question had a typo in it which made it seem like binding/unbinding would be okay as long as timeouts weren't involved.)
I don't see any need for timeouts, just bind/unbind as appropriate:
this.bind(startEvent, start);
function start() {
$(this).unbind(startEvent).bind(stopEvent, stop);
}
function stop() {
$(this).unbind(stopEvent).bind(startEvent, start);
}
In the above, I assume that startEvent is the configured start event name (and I'd probably add a namespace to it, e.g. the user passes in "click" but you add ".niftyplugin" to it resulting in startEvent containing "click.niftyplugin" so you can bind/unbind at will), and stopEvent is the configured stop event name (with namespace).
Here's a full example, with namespaces and using data to remember the options (you could use a closure if you prefer) - live copy:
// Plugin stuff
(function($) {
$.fn.niftyPlugin = niftyPlugin;
function niftyPlugin(options) {
var data;
data = {
startEvent: (options && options.startEvent || "click") + ".niftyplugin",
stopEvent: (options && options.stopEvent || "click") + ".niftyplugin"
};
this.data("niftyPlugin", data).bind(data.startEvent, start);
return this;
}
function start() {
var $this = $(this),
data = $this.data("niftyPlugin");
$this.unbind(data.startEvent).bind(data.stopEvent, stop);
display("Start");
}
function stop() {
var $this = $(this),
data = $this.data("niftyPlugin");
$this.unbind(data.stopEvent).bind(data.startEvent, start);
display("Stop");
}
function display(msg) {
$("<p>").html(msg).appendTo(document.body);
}
})(jQuery);
// Use
jQuery(function($) {
$("#theButton").click(function() {
$("<p>Non-plugin hook fired</p>").appendTo(document.body);
}).niftyPlugin({
startEvent: "click"
});
});
The only other alternative I see is stopImmediatePropagation - live example:
// Plugin stuff
(function($) {
$.fn.niftyPlugin = niftyPlugin;
function niftyPlugin(options) {
var startEvent, stopEvent, running = false;
startEvent = (options && options.startEvent || "click") + ".niftyplugin";
stopEvent = (options && options.stopEvent || "click") + ".niftyplugin";
this.bind(startEvent, start).bind(stopEvent, stop);
return this;
function start(event) {
if (running) {
return;
}
running = true;
display("Start");
event.stopImmediatePropagation();
}
function stop(event) {
if (!running) {
return;
}
running = false;
display("Stop");
event.stopImmediatePropagation();
}
}
function display(msg) {
$("<p>").html(msg).appendTo(document.body);
}
})(jQuery);
// Use
jQuery(function($) {
$("#theButton").click(function() {
$("<p>Non-plugin hook fired</p>").appendTo(document.body);
}).niftyPlugin({
startEvent: "click"
});
});
I don't like it, though, because it interferes with other handlers for the event. For instance, in the above, if I change the use to this:
// Use
jQuery(function($) {
$("#theButton").niftyPlugin({
startEvent: "click"
}).click(function() {
$("<p>Non-plugin hook fired</p>").appendTo(document.body);
});
});
...so the plug-in grabs the events before the non-plug-in code, boom, the non-plug-in code never sees the event (example).
So despite the overhead, I suspect bind/unbind are your friends here.
It may be overkill, but an elegant way to not have to maintain a bunch of flags (e.g. "isPlaying") is to use a Finite State Machine.
Here's a jQuery implementation: https://github.com/DukeLeNoir/jquery-machine
The eventual solution I've gone for is to do a quick uniqueness test for events used for stopping and starting and if there are any events used for both stopping and starting then a different listener (which does an isPlaying check) is attached to these. There's a small performance hit on loading the plugin, but after that the event handling code is about as efficient as can be.
function processEvents() {
var tempStart = opts.startEvent.split(" ").sort(),
tempStop = opts.stopEvent.split(" ").sort();
startEventLoop: for(var i=0, il = tempStart.length;i<il;i++) {
for(var j=0, jl = tempStop.length;j<jl;j++) {
if(tempStart[i] == tempStop[j]) {
stopStartEvents.push(tempStart[i])
tempStop.splice(j,1);
continue startEventLoop;
}
}
startEvents.push(tempStart[i])
}
startEvents = startEvents.join(" ");
stopEvents = tempStop.join(" ");
stopStartEvents = stopStartEvents.join(" ");
}
$this.on(stopEvents, function() {
$this.trigger("stop.flickBook");
}).on(startEvents, function() {
$this.trigger("start.flickBook");
}).on(stopStartEvents, function() {
playing ? $this.trigger("stop.flickBook") : $this.trigger("start.flickBook");
});
I'm trying to build a Javascript listener for a small page that uses AJAX to load content based on the anchor in the URL. Looking online, I found and modified a script that uses setInterval() to do this and so far it works fine. However, I have other jQuery elements in the $(document).ready() for special effects for the menus and content. If I use setInterval() no other jQuery effects work. I finagled a way to get it work by including the jQuery effects in the loop for setInterval() like so:
$(document).ready(function() {
var pageScripts = function() {
pageEffects();
pageURL();
}
window.setInterval(pageScripts, 500);
});
var currentAnchor = null;
function pageEffects() {
// Popup Menus
$(".bannerMenu").hover(function() {
$(this).find("ul.bannerSubmenu").slideDown(300).show;
}, function() {
$(this).find("ul.bannerSubmenu").slideUp(400);
});
$(".panel").hover(function() {
$(this).find(".panelContent").fadeIn(200);
}, function() {
$(this).find(".panelContent").fadeOut(300);
});
// REL Links Control
$("a[rel='_blank']").click(function() {
this.target = "_blank";
});
$("a[rel='share']").click(function(event) {
var share_url = $(this).attr("href");
window.open(share_url, "Share", "width=768, height=450");
event.preventDefault();
});
}
function pageURL() {
if (currentAnchor != document.location.hash) {
currentAnchor = document.location.hash;
if (!currentAnchor) {
query = "section=home";
} else {
var splits = currentAnchor.substring(1).split("&");
var section = splits[0];
delete splits[0];
var params = splits.join("&");
var query = "section=" + section + params;
}
$.get("loader.php", query, function(data) {
$("#load").fadeIn("fast");
$("#content").fadeOut(100).html(data).fadeIn(500);
$("#load").fadeOut("fast");
});
}
}
This works fine for a while but after a few minutes of the page being loaded, it drags to a near stop in IE and Firefox. I checked the FF Error Console and it comes back with an error "Too many Recursions." Chrome seems to not care and the page continues to run more or less normally despite the amount of time it's been open.
It would seem to me that the pageEffects() call is causing the issue with the recursion, however, any attempts to move it out of the loop breaks them and they cease to work as soon as setInterval makes it first loop.
Any help on this would be greatly appreciated!
I am guessing that the pageEffects need added to the pageURL content.
At the very least this should be more efficient and prevent duplicate handlers
$(document).ready(function() {
pageEffects($('body'));
(function(){
pageURL();
window.setTimeout(arguments.callee, 500);
})();
});
var currentAnchor = null;
function pageEffects(parent) {
// Popup Menus
parent.find(".bannerMenu").each(function() {
$(this).unbind('mouseenter mouseleave');
var proxy = {
subMenu: $(this).find("ul.bannerSubmenu"),
handlerIn: function() {
this.subMenu.slideDown(300).show();
},
handlerOut: function() {
this.subMenu.slideUp(400).hide();
}
};
$(this).hover(proxy.handlerIn, proxy.handlerOut);
});
parent.find(".panel").each(function() {
$(this).unbind('mouseenter mouseleave');
var proxy = {
content: panel.find(".panelContent"),
handlerIn: function() {
this.content.fadeIn(200).show();
},
handlerOut: function() {
this.content.slideUp(400).hide();
}
};
$(this).hover(proxy.handlerIn, proxy.handlerOut);
});
// REL Links Control
parent.find("a[rel='_blank']").each(function() {
$(this).target = "_blank";
});
parent.find("a[rel='share']").click(function(event) {
var share_url = $(this).attr("href");
window.open(share_url, "Share", "width=768, height=450");
event.preventDefault();
});
}
function pageURL() {
if (currentAnchor != document.location.hash) {
currentAnchor = document.location.hash;
if (!currentAnchor) {
query = "section=home";
} else {
var splits = currentAnchor.substring(1).split("&");
var section = splits[0];
delete splits[0];
var params = splits.join("&");
var query = "section=" + section + params;
}
var content = $("#content");
$.get("loader.php", query, function(data) {
$("#load").fadeIn("fast");
content.fadeOut(100).html(data).fadeIn(500);
$("#load").fadeOut("fast");
});
pageEffects(content);
}
}
Thanks for the suggestions. I tried a few of them and they still did not lead to the desirable effects. After some cautious testing, I found out what was happening. With jQuery (and presumably Javascript as a whole), whenever an AJAX callback is made, the elements brought in through the callback are not binded to what was originally binded in the document, they must be rebinded. You can either do this by recalling all the jQuery events on a successful callback or by using the .live() event in jQuery's library. I opted for .live() and it works like a charm now and no more recursive errors :D.
$(document).ready(function() {
// Popup Menus
$(".bannerMenu").live("hover", function(event) {
if (event.type == "mouseover") {
$(this).find("ul.bannerSubmenu").slideDown(300);
} else {
$(this).find("ul.bannerSubmenu").slideUp(400);
}
});
// Rollover Content
$(".panel").live("hover", function(event) {
if (event.type == "mouseover") {
$(this).find(".panelContent").fadeIn(200);
} else {
$(this).find(".panelContent").fadeOut(300);
}
});
// HREF Events
$("a[rel='_blank']").live("click", function(event) {
var target = $(this).attr("href");
window.open(target, "_blank");
event.preventDefault();
});
$("a[rel='share']").live("click", function(event) {
var share_url = $(this).attr("href");
window.open(share_url, "Share", "width=768, height=450");
event.preventDefault();
});
setInterval("checkAnchor()", 500);
});
var currentAnchor = null;
function checkAnchor() {
if (currentAnchor != document.location.hash) {
currentAnchor = document.location.hash;
if (!currentAnchor) {
query = "section=home";
} else {
var splits = currentAnchor.substring(1).split("&");
var section = splits[0];
delete splits[0];
var params = splits.join("&");
var query = "section=" + section + params;
}
$.get("loader.php", query, function(data) {
$("#load").fadeIn(200);
$("#content").fadeOut(200).html(data).fadeIn(200);
$("#load").fadeOut(200);
});
}
}
Anywho, the page works as intended even in IE (which I rarely check for compatibility). Hopefully, some other newb will learn from my mistakes :p.
I have built a dom object Engine that has private/public fields/methods that I have simplified below:
function Engine(args){
this.display = args.display;
this.getDisplay = function(){return this.display;}
this.alertMsg = function(msg){
console.log(this.display);
alert(msg);
}
}
What I would like to do is build a custom event that would be triggered after the alert(msg) such as $(this.display).trigger("afterAlert");
function Engine(args){
this.display = args.display;
this.getDisplay = function(){return this.display;}
this.alertMsg = function(msg){
console.log(this.display);
alert(msg);
// trigger custom event here
$(this.display).trigger("afterAlert");
}
}
Now, this event could be empty or not. How would one or more objects declared later register to the "afterAlert" event? In my case, additional javascript files are loaded by the main file dynamically and could contain a code ressembling :
function new_obj(){
bind("afterAlert", function(){
alert("alert was called");
});
}
See my answer from this question...repeated for clarity
I will tackle the register, triggering and unbinding of custom events.
jQuery has all the tools you need to register, bind and unbind to custom events.
Below is an example of hooking up two divs to a custom event called customAjaxStart. I can then trigger this function and both handlers will get called.
Quick Demo Here - Have the firebug/ie8 console enabled.
e.g
$( function() {
$('#div1').bind('customAjaxStart', function(){
console.log('#div1 ajax start fired');
$(this).fadeTo('slow', 0.3);
});
$('#div2').bind('customAjaxStart', function(){
console.log('#div1 ajax start fired');
$(this).fadeTo('slow', 0.3);
});
//fire the custom event
$.event.trigger('customAjaxStart');
//unbind div1 from custom event
$('#div1').unbind('customAjaxStart');
//again trigger custom event - div1 handler will not fire this time
$.event.trigger('customAjaxStart');
});
Taking the above as an example I would trigger the customAjaxStart from the global ajaxStart. Any listeners would be triggered automatically whenever an xhr call is about to be made (ideal for disabling your widgets or showing a loading gif etc) e.g
$.ajaxStart( function(){
$.event.trigger('customAjaxStart');
});
I think what you are looking for is the Observer pattern. At least that's how I would implement it. The following code snippet uses different names but it does essentially what you want (allows registering for events, and even triggering):
Observable = {
addObserver: function(observer) {
if (!this.__observers) this.__observers = [];
this.__observers.push(observer);
},
addGlobalObserver: function(observer) {
if (!this.__global_observers) this.__global_observers = [];
this.__global_observers.push(observer);
},
removeObserver: function(observer) {
var newObservers = [];
var co;
while (co = this.__observers.pop()) {
if (co != observer) newObservers.push(co)
}
this.__observers = newObservers;
newObservers = [];
while (co = this.__global_observers.pop()) {
if (co != observer) newObservers.push(co)
}
this.__global_observers = newObservers;
},
notify: function(event) {
var allObservers = this.__global_observers.concat(this.__observers);
for (var i=0; i < allObservers.length; i++) {
var o = allObservers[i];
if (o[event]) {
var args = []
for (var j=1; j < arguments.length; j++) {
args.push(arguments[j])
};
o[event].apply(this, args);
}
};
},
__global_observers: [],
__initializer: function() {
this.__observers = [];
}
};
If you include this code into your class, you can register for events using addObserver() (addGlobalObserver() for "class level" events). Inside the object you trigger an event using notify().
Code taken from Coltrane.