Is it possible to capture keydown globally on dynamically added text inputs? - javascript

I'm using the following global jQuery to show and hide a loading div for $.ajax calls:
$(document).ajaxStart(function(){
$("#loading").show();
}
$(document).ajaxStop(function(){
$("#loading").hide();
}
This works fine, but I do not want to show the loading div for autocompletes, so I added this:
$("input[type=text]").keydown(function(){
if($(this).hasClass('ui-autocomplete-input')) {
window.suppressGlobal = true;
}
});
Then, to reset suppressGlobal for "normal" $.ajax calls, this:
var origAjax = $.ajax;
$.ajax = function() {
if (window.suppressGlobal) {
arguments[0].global = false;
}
origAjax.apply(this, arguments);
window.suppressGlobal = false;
};
This all works nicely for text inputs that are constructed with page load. However, I have several situations where text inputs are inserted dynamically on the client-side using jQuery/Javascript, in which case the keydown event does not get bound to the global function. I also tried on:
$("input[type=text]").on('keydown', function(){
if($(this).hasClass('ui-autocomplete-input')) {
window.suppressGlobal = true;
}
});
But that doesn't work either. Is there a way that I can globally capture the keydown event regardless of when the text input was added? Could I somehow globally capture the addition of text inputs to the DOM and attach the event handler then?

you will have to use $(document).on() for dynamically created controls.
jsfiddle: http://jsfiddle.net/G9qJE/
also you can use: $('body').on
explanation:
When an event is assigned, it's only assigned to elements that currently exist on the page. If you later on other elements, there is nothing watching that watches for those elements too allow them to be used as well.
That is why you need something sitting at the document level which is aware of the event and the elements you want to apply it to, so that it can watch for any new elements that match and apply that event to them as well.
$(document).on("keydown", "input[type=text]", function() {
if($(this).hasClass('ui-autocomplete-input')) {
window.suppressGlobal = true;
}
});

Related

Executing an action only once - without disable or one click [duplicate]

I want to check if an element has the class .active, if it does add: margin-top: 4px;
However I only want this to occur once, when the page loads, once the class has been detected I don't want to detect it again and add the CSS style.
This class is applied when certain elements are hovered over.
Is this possible in jQuery?
Check out the one event. Documented example:
$('#foo').one('click', function() {
alert('This will be displayed only once.');
});
This will fire once on page load
$(function(){
if ($("#elementid").hasClass("active"))
{
$("#elementid").css("margin-top", "4px");
}
});
The way I usually do this is:
(function($) {
// my custom function to process elements
function myProcessFunction(context) {
// get context
context = context || document;
// select elements within the context, filter out already processed ones
// loop through remained (unprocessed) elements
// '.my-class' - selector for the elements I want to process
$('.my-class:not(.my-class-processed)', context).each( function(i.e){
// mark the element as processed
$(e).addClass('my-class-processed');
// add process code here
});
}
// run myProcessFunction on document.ready
$(document).ready( function(){
myProcessFunction();
});
})(jQuery);
This way I have:
reusable function
processed elements are marked and won't be processed next time the function is called
elements are processed within the context only, if specified (for instance, HTML code returned via an AJAX request)
Hope this helps )

jQuery Text Editor (jqte), using change event

I am trying to write some code for change() event using jQuery Text Editor (jqte), I have two functions which give jqte functionality to textarea's
One for editors loaded with JavaScript, when clicking some elements in a page:
function onLoadEditor(){
jQuery(".comment-editor").jqte({
// some jqte params, such as fsize: false,indent: false...
change: function(){ observeEditor(); }
});
}
And other, generic function, for pages with one single editor
jQuery(function() {
jQuery(".comment-editor").jqte({
// some jqte params, such as fsize: false,indent: false...
change: function(){ observeEditor(); }
});
});
I want to access the id of the concrete textarea (all textareas in the page have an id) which has fired the change() event
How should I write observeEditor() function to achieve this? Or... how I should define the function in jqte change property?
After reading this jQuery blur event with ID and value I have solved it, with following code (simplified)
function onLoadEditor(){
jQuery(".comment-editor").each(function(idx, elem) {
jQuery(this).jqte({
// some jqte params, such as fsize: false,indent: false...
change: observeEditor(elem.id),
});
}
jQuery(function() {
onLoadEditor();
});
But now I have another problem...
As you can read in the original question, onLoadEditor() is called when clicking some elements in a page. Then another javascript function jsComment() is called, builds a form (with a textarea.comment-editor field included) and it is rendered this way
function jsComment(){
...
var form = '<div class="comments_wrapper ... ';
jQuery(form).insertAfter(some_element).fadeIn('fast');
onLoadEditor();
}
Problem is change() event is being fired only once, when form fades in, while the idea is the opposite, event should fire when user adds some text, not when appearing... Any tips?
UPDATE
After reading Event binding on dynamically created elements? I have solved it this way
function onLoadEditor(){
jQuery('.comment-editor').each(function(idx, elem) {
jQuery(this).jqte({
// some jqte params, such as fsize: false,indent: false...
});
jQuery(document).on('change',
jQuery('.comment-editor'),
function(){
observeEditor(elem.id);
}
);
});
}
jQuery(function() {
onLoadEditor();
});
Although finally I am not using change() event, as it was being fired constantly. Performing better with keyup() & paste(), for instance

preventing effects to be applied to the same object twice when adding objects with ajax

I'm having a little issue with an application I'm making. I have a page where the user edits a document via dragging modules into the page or "canvas" area.
http://thinktankdesign.ca/temp_img.jpg
When the page is loaded, javascript haves the modules collapsible (like above). However after the user drags in a new module the effect is applied again some new modules can collapse as well. here is the problem. each time a module loads the same effect gets applied to the modules that already can collapse. It ends up breaking their animations.
heres the code that gets executed on page load.
//make colapsible
$("h1.handle").click(function() {
var object = $(this);
v$(this).next().toggle("fast", colapsible_class(object));
vreturn false;
}).addClass("open");
and heres the code that gets executed in the creation of a module via ajax
function get_module(id){
var template = $('input[name=template]').val();
$.post(window.location.href, { template: template, module: id, mode: 'create' },
function(data){
$(data).insertBefore(".target_wrapper");
//enable deletion of module
$(".js_no_modules").slideUp("slow");
$(enable_module_deletion());
//show delete button
$("button[name=delete]").show();
//make colapsible
$("h1.handle").click(function() {
var object = $(this);
$(this).next().toggle("fast", colapsible_class(object));
return false;
}).addClass("open");
}
);
}
I need a solid way of preventing the toggle effect to be applied to the same module twice
Use jQuery 1.3 live events instead.
//make colapsible
$("h1.handle").live("click", function() {
var object = $(this);
v$(this).next().toggle("fast", colapsible_class(object));
vreturn false;
}).addClass("open");
and then eliminate the click declaration in the second block of code, changing it to $("h1.handle").addClass("open");
Live events bind all current and future matching elements with an event.
In your Ajax success handler try the following:
//make collapsible
$("h1.handle:not(.open)").click(function() {
var object = $(this);
$(this).next().toggle("fast", colapsible_class(object));
return false;
}).addClass("open");
The best way to solve your problem is, instead of using $("h1.handle") on the AJAX callback, go for $(data).find("h1.handle"). Something like,
var x = $(data);
x.insertBefore(...);
/* your other code */
x.find('h1.handle').click(...).addClass(...);
Like that, only the newly added items will have the event bounded. The already present ones will not be touched.
If we want to answer your question instead of just solving your problem, then we have several alternatives, such as:
store, in your objects, that the onclick event handler has been set so that you don't set it twice
always bind the onclick event, but always unbind it first
use jQuery's live events and the addClass open only on the newly created items.
IMO, the first one is the easiest. You can accomplish it by using jQuery's data(). Then you could do something like:
$("h1.handle").each(function() {
var me = $(this);
// if already has click handler, don't do anything
if (me.data('click_set') != null) { return true; }
// otherwise, store the data and bind the click event
me.data('click_set', true).click(function() {
/* the code you already have on the click handler */
}).addClass('open');
}
The second alternative involves storing the function that you pass inline to the click event binder in a variable, and then using jQuery's unbind to disable it.

Observing events on dynamically inserted objects in the DOM

On load, I add a desired behavior on all textareas on a page.
Event.observe(window, 'load', function() {
$$('textarea').each(function(x) {
x.observe('keydown', dosomethinghere)
});
});
This works because the textareas are already in the DOM, but how should I treat textareas that are dynamically added after the page loads (ex: if I have a button that says "Add More"). I would like these newly created textareas to have the same behavior.
The way I do it is by just observing the new textarea when I add it, like this:
function doSomethingWithTextAreas(){
//do something.
}
document.observe("dom:loaded", function() {
$$('textarea').each(function(s){
s.observe('keydown', doSomethingWithTextareas);
});
$('add_more').observe('click', function(){
textarea = new Element('textarea');
textarea.observe('keydown', doSomethingWithTextareas); //Observes the new textarea.
Element.insert($('textarea_container'), {bottom:textarea});
});
});
Consider using jQuery Live.
$.live() would work as STAii mentions, but there is discussion of implementing a similar function in prototype as well. That would probably be of more benefit so you don't have to add another library.
Well, the answer is a bit tricky. The only way to do this is to maintain a cache of events listeners for your textareas. When adding a new textarea to your page, you would need to call Event.stopObserving on all your cached events. You would then call your $$('textarea').each(...) code again to bind to all the elements.
Thankfully, someone has done this for you already in a very handy lightweight prototype extension called lowpro: http://www.danwebb.net/2006/9/3/low-pro-unobtrusive-scripting-for-prototype
You can do what you wish as simply as:
Event.addBehavior({
'textarea:keydown': function(e) {
dosomethinghere(); // e.g. this.hide();
}
});
Then whenever you add a new textarea dynamically, you simply call Event.addBehavior.reload();
I should point out that "e" is the Event object, and "this" is the element inside the context of the function(e) {} definition.
A nice way of doing this is to have the javascript function which adds the text areas fire an event which any other function can observe and act on. So:
function add_textarea() {
// Code creates a new <textarea> and adds it to the page
var textarea = new Element("textarea");
$("some-form").insert(textarea);
textarea.fire("textarea:add")
}
document.observe("textarea:add", function(event) {
event.target.observe('keydown', dosomethinghere);
});
This allows your 2 functions--one that adds a new textarea and one which attaches observers--to be loosely coupled and not know anything about each other. One simply needs to fire a custom event which the other can observe.

change event doesn't fire on ajax loaded forms

I have an HTML document, with the jquery code
$(function(){
[...]
$("#newNotice").click(function(){
$("#properties").load("testDiagramm.html #NoticeForm");
return false;
});
function showFormValues(){
var s = $("form").serialize();
[... Do things ...]
}
$("input, textarea").change(showFormValues);
});
At the beginning there is no form in the HTML document. But i load diffrent forms into the document. One of those request you can see here
$("#properties").load("testDiagramm.html #NoticeForm");
The problem is. that the codeline
$("input, textarea").change(showFormValues);
only fire, when the form was loaded at the beginning. What must I do, if i want to execute the function showFormValues(), when I changed something in the formular, which i load later?
Thanks a lot for your answers.
Lara
Your form loses its binding to the DOM after it is reloaded via ajax, so event handlers previously bound to the elements that get injected into the page are lost.
I would normally suggest using event delegation with live, but it does not support the change event, so a safe bet would be to rebind using a callback as a second parameter to your $.load function:
$(function(){
[...]
$("#newNotice").click(function(){
$("#properties").load("testDiagramm.html #NoticeForm", function() {
$("input, textarea").change(showFormValues);
});
return false;
});
function showFormValues(){
var s = $("form").serialize();
[... Do things ...]
}
$("input, textarea").change(showFormValues);
});

Categories