JQuery event tracking - javascript

in some function I remove element like this $('#'+id+' img.load').remove(); , how can i track this event and run custom code?

(function($){
var remove_orig = $.fn.remove;
$.fn.remove = function(){
console.log('Remove called');
remove_orig.apply(this, arguments);
};
})(jQuery);
You can "hook" in to any jQuery function and place your own handling code within (including logging method(s)) which will execute before the native jQuery code is executed.
demo (another version with the selector shown)
Catching the removal is easy using the above overload. Simply alter the hook to fire a trigger before (or after) jQuery gets to it:
(function($){
var remove_orig = $.fn.remove;
$.fn.remove = function(){
this.trigger('removing');
remove_orig.apply(this, arguments);
};
})(jQuery);
$('#foo').bind('removing',function(e){
alert('#foo is being removed');
});
$('#foo').remove();

One way is to "trigger" a custom event (in this example I am using window):
$('#'+id+' img.load').remove();
$(window).trigger("MyElementRemoved", [id]);
Then in another part of your code "handle" the event:
$(window).bind("MyElementRemoved", function(e, elementId) {
alert("element removed: " + elementId);
}

Related

CKEDITOR : How do set `contentdom` after every SetData

I am creating a CKEditor plugin and I face some issues on this.
Model of My Plugin:
CKEDITOR.plugins.add("myplugin", {
//for lang,require,icon
init:function(a){
editor.on('contentDom', function () {
editor.document.on('key', function (evt) {
console.log("Key Pressed");
});
});
}
});
This is Working Fine.But,my problem is setData.
I am setting data when the user is clicking a file.
After setData the key event is not Working.
Is there any way to attach the listener to document after every setData() within plugin file?
And what are the other type of methods which are used in CKEditor like init ?
(OR)
Is there any way to setData() without affecting contentdom,key events?
Please add the listener to the editor and not to the document. That way it wil not get removed when document is removed:
editor.on( 'instanceReady', function( e ) {
editor.on( 'key', function( e ) {
console.log('test');
});
});
Please see: https://docs.ckeditor.com/ckeditor4/latest/api/CKEDITOR_editor.html#event-key
Finally, I found the Answer.
Refer the below Question
CKEDITOR.setData prevents attaching of events with .on function
And goes to the Document in CKEditor Docs Page.
#contentDomUnload
Finally My Code Like this,
editor.on('contentDom', function () {
var editable = editor.editable();
editable.attachListener(editable, 'keyup', function (evt) {
console.log('for key events');
});
editable.attachListener(editable, 'mousedown', function (evt) {
console.log('for click events');
});
});
And It worked very well.

How to set a jquery on() function only if it doesn't already existis?

I'm calling via ajax additional content where I add a jquery on() function for a click event. Each time I renew the content the event is also set again so at the end it get executed several times. How can I avoid this behavior?
How do I test if the click event is already set on the document?
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Click
<script>
// first ajax load
$(document).on('click', '.open-alert', function () {
alert('hello world!');
});
// second ajax load
$(document).on('click', '.open-alert', function () {
alert('hello world!');
});
</script>
I already try to just the jQuery.isFunction(), but I don't anderstand how to apply it in this case.
You can Unbind the click event , if you getting more than one time exectuated.
$(document).unbind('click').on("click", ".open-alert", function () {
//do stuff here
});
Or you can also use it
$(document).off("click", ".open-alert").on("click", ".open-alert", function () {
});
Using
$(document).on('click', '#element_id', function() {
//your code
});
Will check the DOM for matching elements every time you click (usually used for dynamically created elements with ajax)
But using
$('#element_id').on('click', function() {
//your code
});
Will only bind to existing elements.
If you use the 1st example, you only need to call it once, you can even call it before your ajax call since it will recheck for matching elements on each click.
<script>
$(document).on('click', '.open-alert', function () {
alert('hello world!');
});
// first ajax load
// second ajax load
...
</script>
In case you cannot bind the event to the specific DOM element (which might happen if you use Turbolinks for example) you can use a variable to check whether you set the event or not.
Local scope
var clickIsSet = false;
// any ajax load
$(document).on('click', '.open-alert', function () {
if ( clickIsSet ) {
alert('hello world!');
clickIsSet = true;
}
});
Global scope
I don't recommend to make clickIsSet global, but in case you are importing/exporting modules you can do that:
// main.js
window.clickIsSet = false;
// any-other-module.js
$(document).on('click', '.open-alert', function () {
if ( window.clickIsSet ) {
alert('hello world!');
window.clickIsSet = true;
}
});
jQuery check if event exists on element : $._data( $(yourSelector)[0], 'events' )
this return all of element events such : click , blur ,
focus,....
Important Note: $._data when worked that at least an event bind to element.
so now:
1.in your main script or first ajax script bind click event on element
<script>
$(document).on('click', '.open-alert', function () {
alert('hello world!');
});
</script>
2. in secound ajax:
var _data = $._data( $('.open-alert')[0], 'events' );
if(typeof _data != "undefined"){
var eventClick = $._data( $('.open-alert')[0], 'events' ).click
var hasEventClick = eventClick != null && typeof eventClick != "undefined";
if(!hasEventClick){
$(document).on('click', '.open-alert', function () {
alert('hello world!');
});
}
}
I get Confuse about your question but as far as understand your question I have three suggestions:
Use Id element (as #Mokun write the answer)
Use Common Function for call functionality instead use through the click event.(Make Sure of function does not overwrite your content by calling).
Use of flag variable (or global variable for your tracking event) in jquery and identify your function call for particular execution.

jQuery event collision

I have an object :
var myObject = {
open : function() {
console.log('Object open');
$(this).trigger('open');
}
};
$(myObject).on('open', function() {
console.log('Open event received');
});
myObject.open();
This code throw an "too much recursion".
The problem is that trigger is calling the method, if I rename the open method, this works :
var myObject = {
_open : function() {
console.log('Object open');
$(this).trigger('open');
}
};
$(myObject).on('open', function() {
console.log('Open event received');
});
myObject._open();
Does this make sense for anyone ?
Well you created a recursive loop when you try to trigger 'open' again.
a reccursive loop is a function that calls itself over and over agains until the "stackoverflow" error happen ( no puns intended). it's a more efficient way to make loops.
on the second scenario, you trigger _open and then the function tries to trigger the 'open' event, which trigger the on('open') listener.
simple as that.
In your first example
$(this).trigger('open');
recursively triggers the invocation of the myObject.open function.
In a second one
$(this).trigger('open');
triggers new custom event 'open' and then you handle this event with handler that you defined via on() method.
So it sounds like your trying to create a event listener for when your function is executed?
If so this is how I have handled many of these types of events. I use custom events bound to the document so they are avaiable at a global level.
JS Fiddle: https://jsfiddle.net/7eaoe1hp/
var myObject = {
open : function() {
console.log('Object open');
$(document).triggerHandler('open');
}
};
$(document).on('open', function(){
console.log('Open event received');
});
myObject.open();

Trigger jQuery event on pageload

I have the following jQuery:
// change the number of finished and missing assets
$('.add-requirements .overall-status .status-dropdown li').live('click', function() {
var remaining_titles = $('.item-section.finished').length;
$('.add-requirements .remaining-titles').text(remaining_titles);
});
It fires whenever a status dropdown is changed. How would I also make it fire when the page loads?
From what I can understand you want to update the remaining count on page load and I assume that the finished status is pre setted on the desired elements.
I'll go about this in another way
$('.add-requirements .overall-status .status-dropdown li').live('click', updateRemaining);
updateRemaining()
function updateRemaining(){
var remaining_titles = $('.item-section.finished').length;
$('.add-requirements .remaining-titles').text(remaining_titles);
}
Just chain on a trigger('click') to trigger a click on first pageload :
$('.add-requirements .overall-status .status-dropdown li').live('click', function() {
var remaining_titles = $('.item-section.finished').length;
$('.add-requirements .remaining-titles').text(remaining_titles);
}).trigger('click');
But you should really be using on()
$('closest_non_dynamic_parent').on('click', 'your_freakishly_long_selector', function() {
A bit more verbose, but here is what worked for me:
var remaining_titles_section = $('.add-requirements .remaining-titles');
remaining_titles_section.text($('.item-section.finished').length);
$('.add-requirements .overall-status .status-dropdown li').live('click', function() {
var remaining_titles = $('.item-section.finished').length;
remaining_titles_section.text(remaining_titles);
});

Best way to silently bind window resize event to jQuery plugin without keeping a reference to the targeted element

I'm looking for best-practice advice.
I'm writing a small jQuery plugin to manage horizontal scroll on elements.
I need all the dom elements targeted by that plugin to update on window resize.
Fact is, my website is a full ajax 'app' so when I remove DOM elements, I need them gone so memory doesn't leak.
But I can't find a way to bind the resize event without keeping a reference to the DOM node.
EDIT :
Actually I need the resize handler to get the plugin-targeted elements at 'call' time, coz I don't want to keep any reference to those elements in memory, because I might call .html('') on a parent of theirs...
I did not paste all my code, just an empty shell. I already have a destroy method that unbinds handlers. But I'm generating, removing and appending html nodes dynamically and I the the elements targeted by the plugin to remove silently.
Kevin B stated I could override jQuery .remove method to deal with the handlers, but would have to load jQuery UI for it to work. I don't want that either..
Here is what I tried (attempts commented):
(function($) {
// SOLUTION 2 (see below too)
// Not good either coz elements are not removed until resize is triggered
/*
var hScrolls = $([]);
$(window).bind('resize.hScroll',function(){
if(!hScrolls.length) return;
hScrolls.each(function(){
if($(this).data('hScroll')) $(this).hScroll('updateDimensions');
else hScrolls = hScrolls.not($(this));
});
});
*/
// END SOLUTION 2
// SOLUTION 3 (not implemented but I think I'm on the right path)
$(window).bind('resize.hScroll',function(){
// need to get hScroll'ed elements via selector...
$('[data-hScroll]').hScroll('updateDimensions');
// I don't know how....
});
// END SOLUTION 3
var methods = {
init : function(options) {
var settings = $.extend( {
defaults: true
}, options);
return this.each(function() {
var $this = $(this),
data = $this.data('hScroll');
if (!data) {
$this.data('hScroll', {
target: $this
});
// SOLUTION 1
// This is not good: it keeps a reference to $this when I remove it...
/*
$(window).bind('resize.hScroll', function(){
$this.hScroll('updateDimensions');
});
*/
// END SOLUTION 1
$this.hScroll('updateDimensions');
// SOLUTION 2 (see above too)
hScrolls = hScrolls.add(this);
}
});
},
updateDimensions: function(){
var hScroll = this.data('hScroll');
// do stuff with hScroll.target
}
}
$.fn.hScroll = function(method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if ( typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.hScroll');
}
};
})(jQuery);​
Thanks all in advance!
jQuery calls cleanData any time you do something that removes or replaces elements (yes, even if you use parent.html("") ). You can take advantage of that by extending it and having it trigger an event on the target elements.
// This is taken from https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.widget.js 10/17/2012
if (!$.widget) { // prevent duplicating if jQuery ui widget is already included
var _cleanData = $.cleanData;
$.cleanData = function( elems ) {
for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
try {
$( elem ).triggerHandler( "remove" );
// http://bugs.jquery.com/ticket/8235
} catch( e ) {}
}
_cleanData( elems );
};
}
Now you can bind to the remove event when setting up your plugin and have it run your destroy method.
$(elem).bind("remove",methods.destroy)
You might use a class name and forward the resize event:
$.fn.hScroll = function(method) {
this
.addClass('hScroll')
.data('method', arguments)
};
var methods['alert_text'] = function(config){
alert( config + " " + $(this).text() );
}
$(window).bind('resize.hScroll',function(){
$(".hScroll").each(function(){
var method_config = $(this).data('method');
var method = method_config.shift();
// Forward the resize event with all resize event arguments:
methods[method].apply(this, method_config);
})
})
// Register a resize event for all a.test elements:
$("a.test").hScroll('alert_text', "hey");
// Would alert "hey you" for <a class="test">you</a> on every resize
Update
If you change the dom and want to keep the selector you might try this one:
var elements = [];
$.fn.hScroll = function(method) {
elements.push({'selector' : this.selector, 'arguments' : arguments });
};
var methods['alert_text'] = function(config){
alert( config + " " + $(this).text() );
}
$(window).bind('resize.hScroll',function(){
$.each(elements,function(i, element){
$(element.selector).each(function(){
var method_config = element.arguments;
var method = method_config.shift();
// Forward the resize event with all resize event arguments:
methods[method].apply(this, method_config);
})
})
})
// Register a resize event for all a.test elements:
$("a.test").hScroll('alert_text', "hey");
$(document.body).html("<a class='test'>you</a>");
// Would alert "hey you" for every window resize
You should have the scroll event bound in the extension. Also, you will want to add a "destroy" method to your extension as well. Before you remove the element from the DOM, you will want to call this method. Inside the detroy method is where you will want to unbind the resize event.
One important thing in making this work is that you have a reference to each handler method that is bound to the resize event. Alternatively, you can unbind All resize events upon the removal on an element and then rebind the scroll event to the remaining elements that require it.

Categories