I am calling some jQuery plugins that attaches themselves to element on DOM ready. These plugins manipulate the DOM when certain events has occurred (click, change etc,)
$("body").find("input[type='checkbox']").checkbox();
Above works fine on DOM ready. However, if I'm loading some HTML from an AJAX call I have to use .on() to guarantee events gets bound consistently.
The question is which event I should bind the plugin to? I have tried below and it doesn't seem to respond.
$("body").on("load", "input[type='checkbox']", function(){
$(this).checkbox();
});
Here's the checkbox() plugin I'm referring to above. If that's any help. :)
'use strict';
define(['jquery'], function($){
return function(){
$.fn.checkbox = function (options) {
options = options || {};
var defaults = {
'className': 'jquery-checkbox',
'checkedClass': 'jquery-checkbox-on'
};
var settings = jQuery.extend(defaults, options);
return this.each(function () {
var self = jQuery(this);
var replacement = jQuery(
'<span class="' + settings.className + '-wrapper">' +
'<a class="' + settings.className + '" href="#" name="' + self.attr('id') + '"></a>' +
'</span>');
var element = jQuery('a', replacement);
if (self.prop('checked')) {
element.addClass(settings.checkedClass);
}
element.on('click', function (event) {
event.preventDefault();
event.stopPropagation();
var input = jQuery('input#' + jQuery(this).attr('name'), replacement.parent());
if (input.prop('checked')) {
input.removeAttr('checked');
} else {
input.prop('checked', true);
}
input.trigger('change');
return false;
});
element.on('focusin', function (event) {
$(this).addClass('checkbox-focus');
});
element.on('focusout', function (event) {
$(this).removeClass('checkbox-focus');
});
element.on("keypress", function(e){
if ( e.which === 32 ){ self.prop('checked', !self.prop('checked')).change(); }
});
self.on('change', function (event) {
var input = jQuery(this);
if (input.prop('checked')) {
jQuery('a[name=' + input.attr('id') + ']', replacement.parent()).addClass(settings.checkedClass);
} else {
jQuery('a[name=' + input.attr('id') + ']', replacement.parent()).removeClass(settings.checkedClass);
}
return true;
});
self.css({
'position': 'absolute',
'top': '-200px',
'left': '-10000px'
}).before(replacement);
});
}
};
});
You appear to want to apply add-ins to elements that have been loaded dynamically. That is not what 'on' is for.
Delegated events listen for specific events (like "click") at a parent/ancestor element then filter the possible recipients, then executes the supplied function against any matching elements that caused the event.
You actually need to apply the add-in code after your Ajax load completes.
Example:
In the success part of your ajax load, apply the addin:
$("input[type='checkbox']").checkbox();
If you loaded a specific part of the screen (likely), then target the selector at that element:
e.g.
$("#myloadcontainer input[type='checkbox']").checkbox();
Related
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..
i have trouble with appended audio.
when page is scrolling i'm dynamicly load an audio, but then, when i'm trying to play it, div, in which located audiofile isn't changing it's property, how to solve that?
How i appending:
$(window).scroll(function(){
if(scrollCount == 0){
if($(window).scrollTop() > 200){
AddMoreContent();
}
} else{
if($(window).scrollTop() > $(window).height() * scrollCount - 100) {
AddMoreContent();
}
}
});
function AddMoreContent(){
$.ajax({url:thisPage + "/" + thisPage + "." + scrollCount +".html",context: document.body, success: function(response){
$("#main-content").append(response);
scrollCount++;
}
});
}
How i listening audio event:
$(document).ready(function () {
$("audio").on("play", function () {
var _this = $(this);
//$("audio").each(function (i, el) {
// if (!$(el).is(_this))
// $(el).get(0).pause();
// $(el).parent().removeClass("intro");
//});
_this.parent().addClass("intro");
});
});
The play event does not bubble, So you can't use event delegation. You need to bind the handler once the element is added to the dom so
function AddMoreContent() {
$.ajax({
url: thisPage + "/" + thisPage + "." + scrollCount + ".html",
context: document.body,
success: function (response) {
var $els = $(response);
$("#main-content").append($els);
//use .filter if the audio elements are in the root level of response
$els.find("audio").on('play', function () {
//do your stuff
});
scrollCount++;
}
});
}
Assuming the audio elements are appended through the $.ajax requests, you need to use a delegated event handler:
$('#main-content').on('play', 'audio', function() {
$(this).parent().addClass('intro');
// your code here...
});
I'm having a problem with my code, lemme first paste my code, most of it isn't important, just the context of it.
$("#navbar > a").click(function(event) {
$('#controls').show();
var currentNum = parseInt($(this).attr('class'), 10);
document.getElementById('pNum').innerHTML = "pg. " + (currentNum + 1);
event.preventDefault();
var t2 = ($(this).attr('id')).split("#");
var $tr = $(zip.file(localStorage.selected + "/" + t2[0]).asText());
document.getElementById('main').innerHTML = "";
$('#main').append($tr);
document.getElementById(t2[1]).scrollIntoView()
current = ($(this).attr('class'));
$(function() {
$("#main img").each(function() {
var imgPath = localStorage.selected + "/" + $(this).attr('src');
var imageData = zip.file(imgPath).asBinary();
$(this).attr('src', 'data:image/jpeg;base64,' + btoa(imageData));
});
});
$("#main a").click(function(event){
event.preventDefault();
var elems = ($(this).attr('href')).split("#");
var $path = $(zip.file(localStorage.selected + "/" + elems[0]).asText());
document.getElementById('main').innerHTML = "";
$('#main').append($path);
});
});
Now the click event at the bottom only works if I place it inside the code that creates the content, which shouldn't be the case and secondly it only works once, after I call it for the first time it refuses to work, any suggestions ?
It sounds like you want to use event delegation instead. For example:
$(document).on('click', '#main a', function(event){
event.preventDefault();
var elems = ($(this).attr('href')).split("#");
var $path = $(zip.file(localStorage.selected + "/" + elems[0]).asText());
document.getElementById('main').innerHTML = "";
$('#main').append($path);
});
The problem is that the $('#main a').click(...) approach requires that the #main a elements already be present on the page at the time that the click handler is bound.
Event delegation allows you to listen for a click event on the document (or any other element that will always be present), and see if that event originated from a #main a element. This allows you to add/remove elements on the fly without worrying about which ones have or haven't already had click handlers bound.
I've had this problem before, have you tried using this?:
$("<element id>").on( 'click', this, function ()
{
// Your code here
}
reference: http://api.jquery.com/on/
edit* Sorry did not see an answer before ( better explanation in answer above ) - but I'll keep mine for reference.
I'm using the following placeholder plugin
(function($){
var ph = "PLACEHOLDER-INPUT";
var phl = "PLACEHOLDER-LABEL";
var boundEvents = false;
var default_options = {
labelClass: 'placeholder'
};
//check for native support for placeholder attribute, if so stub methods and return
var input = document.createElement("input");
if ('placeholder' in input) {
$.fn.placeholder = $.fn.unplaceholder = function(){}; //empty function
delete input; //cleanup IE memory
return;
};
delete input;
//bind to resize to fix placeholders when the page resizes (fields are hidden/displayed, which can change positioning).
$(window).resize(checkResize);
$.fn.placeholder = function(options) {
bindEvents();
var opts = $.extend(default_options, options)
this.each(function(){
var rnd=Math.random().toString(32).replace(/\./,'')
,input=$(this)
,label=$('<label style="position:absolute;display:none;top:0;left:0;"></label>');
if (!input.attr('placeholder') || input.data(ph) === ph) return; //already watermarked
//make sure the input tag has an ID assigned, if not, assign one.
if (!input.attr('id')) input.attr('id', 'input_' + rnd);
label .attr('id',input.attr('id') + "_placeholder")
.data(ph, '#' + input.attr('id')) //reference to the input tag
.attr('for',input.attr('id'))
.addClass(opts.labelClass)
.addClass(opts.labelClass + '-for-' + this.tagName.toLowerCase()) //ex: watermark-for-textarea
.addClass(phl)
.text(input.attr('placeholder'));
input
.data(phl, '#' + label.attr('id')) //set a reference to the label
.data(ph,ph) //set that the field is watermarked
.addClass(ph) //add the watermark class
.after(label) //add the label field to the page
//setup overlay
itemFocus.call(this);
itemBlur.call(this);
});
};
$.fn.unplaceholder = function(){
this.each(function(){
var input=$(this),
label=$(input.data(phl));
if (input.data(ph) !== ph) return;
label.remove();
input.removeData(ph).removeData(phl).removeClass(ph).unbind('change',itemChange);
});
};
function bindEvents() {
if (boundEvents) return;
//prepare live bindings if not already done.
$("form").live('reset', function(){
$(this).find('.' + ph).each(itemBlur);
});
$('.' + ph)
.live('keydown',itemFocus)
.live('mousedown',itemFocus)
.live('mouseup',itemFocus)
.live('mouseclick',itemFocus)
.live('focus',itemFocus)
.live('focusin',itemFocus)
.live('blur',itemBlur)
.live('focusout',itemBlur)
.live('change',itemChange);
;
$('.' + phl)
.live('click', function() { $($(this).data(ph)).focus(); })
.live('mouseup', function() { $($(this).data(ph)).focus(); });
bound = true;
boundEvents = true;
};
function itemChange() {
var input = $(this);
if (!!input.val()) {
$(input.data(phl)).hide();
return;
}
if (input.data(ph+'FOCUSED') != 1) {
showPHL(input);
}
}
function itemFocus() {
$($(this).data(ph+'FOCUSED',1).data(phl)).hide();
};
function itemBlur() {
var that = this;
showPHL($(this).removeData(ph+'FOCUSED'));
//use timeout to let other validators/formatters directly bound to blur/focusout work
setTimeout(function(){
var input = $(that);
//if the item wasn't refocused, test the item
if (input.data(ph+'FOCUSED') != 1) {
showPHL(input);
}
}, 200);
};
function showPHL(input, forced) {
var label = $(input.data(phl));
//if not already shown, and needs to be, show it.
if ((forced || label.css('display') == 'none') && !input.val())
label
.text(input.attr('placeholder'))
.css('top', input.position().top + 'px')
.css('left', input.position().left + 'px')
.css('display', 'block');
//console.dir({ 'input': { 'id':input.attr('id'), 'pos': input.position() }});
}
var cr;
function checkResize() {
if (cr) window.clearTimeout(cr);
cr = window.setTimeout(checkResize2, 50);
}
function checkResize2() {
$('.' + ph).each(function(){
var input = $(this);
var focused = $(this).data(ph+'FOCUSED');
if (!focused) showPHL(input, true);
});
}
}(jQuery));
It applies the placeholder attribute to form fields in browsers that do not natively support the placeholder attribute (ex. IE9). It works for statically loaded text fields, however for text fields that are loaded via ajax, the placeholder does not appear.
Is it possible to achieve this 'watermark' effect on text fields that are loaded via ajax?
What happens if you trigger the window resize function after adding in new inputs?
$(window).trigger('resize')
You could apply the plugin to newly created controls after the AJAX call completes. Forgive the pseudo-code as I'm not really sure about how your AJAX calls are working:
$.ajax({
url: "test.html",
cache: false
}).done(function( result ) {
field = $('<input>').html(result);
$("#results").append(field);
field.placeholder();
});
Another option is that you could use jQuery's .on() method to bind dynamically created controls to the function--but it wants an event (like click). I'm not sure how you would do that. Maybe something like this:
$( 'body' ).on('click','input.addField', function(e){
$(this).placeholder();
});
I know this won't work, but maybe it helps get you brainstorm solutions.
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.