Passing "this" to a function in jQuery problems - javascript

sorry but couldn't find a solution for my problem so far.
I am writing a kind of an email template editor as a little task for my boss.
$('a, span').click(function(event){
var editableElement = $(this);
if($(this).is('a')){
event.preventDefault();
}
if(editableElement.is('span')){
processType(editableElement, 'simpleText', modalContent)
When I send the 'editableElement' variable first time, everything's fine, sends object to my function, where I open up a modal window, where there is a textarea, which if i enter text and submit it using only jQuery it will put the text from the textarea (using .val()) to my desired element, in this case a "span" element which is the 'editableElement' using .text() on it. No problem, it works for the first time. After I try to edit a second span, it constantly modifies the previous span too, with whatever I enter in to the textarea (which is cleared out completely, the problem is NOT here) I've ran a fast debug with a simple foreach on the editable element, and the problem is that for some reason it keeps adding objects to the editableElement variable everytime I send it to the function. The number of spans I try to edit, the number of objects will be in my variable all with the index of 0.
Any idea what could be the cause of this?
Thanks in advance!
EDIT:
As requested the whole code in one piece which I have problem with, though it was the whole code before too, I'm in an early stage of writing it, I understand that it was hard to read though, perhaps now it is properly formatted as requested.
$(window).load(function()
{
var modalContent = $('#modalContent');
modalOverlay = $('#modalOverlay');
$('a, span').click(function(event)
{
var editableElement = $(this);
if($(this).is('a'))
{
event.preventDefault();
}
if(editableElement.is('span'))
{
processType(editableElement, 'simpleText', modalContent)
}
});
$('#codeGenButton').click(function()
{
var container = $('#codeContainer');
container.empty();
container.text(getPageHTML());
});
$('#modalClose').click(function()
{
$(this).parent().parent().animate({'opacity': '0'}, 200,
function(){
$(this).css({'display': 'none'});
});
});
});
function fillData(targetDomElement, modalObject)
{
$('#modalSubmit').click(function(){
targetDomElement.text($('#simpleTextEdit').val());
closeModalWindow();
});
}
function processType(targetDomElement, type, modalObject)
{
modalObject.empty();
if(type == 'simpleText')
{
modalObject.append("<p id='simpleTextEditTitle'>Text editor</p><textarea id='simpleTextEdit'></textarea>");
getModalWindow();
fillData(targetDomElement, modalObject);
}
}
Step by step of what it should do:
First of all, the html should not be needed for this, it does not matter, and this is the whole code honestly.
When you click on either an element of (span) or an element of (a) it triggers the function.
It will check if it was actually a (span), or an (a) element.
Currently if it is an element (a), it does nothing, not implemented yet, but if it is a (span), it will call in the processType function, which it sends the "handler?" of the element to namely "editableElement" which has been declared right after the click event, the 'simpleText' which gets send too, is just to differentiate between element types I will send to the processType function later on, and for the last, "modalConotent" is only a div container, nothing more.
Once the function gets the data first, it will make sure, that the modal window gets cleared of ALL data that is inside of it, then it will append a bit of html code as you can see, in to the modal window, which pops up right after I have appended data in to it, it is literally just a 'display: block' and 'animate: opacity: 1' nothing special there.
Lastly it will trigger the 'fillData' function, which will put my desired data from '#simpleTextField' which is only a (textarea) where you can write in, to my desired element 'editableElement' which is the element you have clicked at the first place, a (span) element after the submit, which is again, just a css of 'display: none' and 'opacity: 0' closes the modal window.
THE END.

Your problem is here
function fillData(targetDomElement, modalObject)
{
$('#modalSubmit').click(function(){
targetDomElement.text($('#simpleTextEdit').val());
closeModalWindow();
});
}
Each time this function is called it adds a new click handler with the perameters at the time the handler was created. This handler is added in addition to the already created handlers. See a demo here. After successive clicks on the spans notices how fillData is called multiple times for a single click.
To give you the best possible answer I need to know where your modalSubmit button is in relation to modalContent. Also is is modalSubmit dynamic or static on the page?
Here is a fairly hacky fix in the mean time using on and off to bind and remove the handler respectively:
function fillData(targetDomElement, modalObject)
{
$('#modalSubmit').off("click"); /*Clear Hanlders*/
$('#modalSubmit').on("click", function(){
console.log("fill data");
console.log(targetDomElement);
targetDomElement.text($('#simpleTextEdit').val());
/*closeModalWindow(); Don't have anything for this so ignoring it*/
});
}
Demo

I've solved it myself by using .submit() (of course this means adding form, and an input with the type of submit) instead of .click() when I send the request to modify the element I've clicked on originally.
I don't understand though, why it did what it did when I've used the .click() trigger.

Related

jquery change value of input created after page load

I simply need to change the value of the input field which is created on button click. I have tried the below but it gives me undefined error since the element was not there when the page loaded. any ideas?
$('#test').val("test");
I use the below code for click event but I have no idea how to do the same thing for the above one too
$('body').on("click", ".btnx", function() {
//do something
});
$('#test').click(function(){
//code here
});
$('#test').empty();
$('#test').append('test');
There isn't anything like delegated events when you want to set the value of an input that is added out of your control, because there is no event to wait for.
You can wait for the element to come into existance, and set the value when it's there:
var handle = window.setInterval(function(){
var i = $('#test');
if (i.length) {
i.val('test');
window.clearInterval(handle);
}
}, 100);
The interval 100 means that it checks ten times per second, so it may be as long as 1/10th of a second after the element is created that the value is changed. You can adjust the value depending on how fast you need the value to be set, and how much overhead you can allow.

Delegate and AddClass not working

I am having a problem with getting this piece of code to work. Its built to allow for a user to click on one row to "select" it by highlighting via a different class. If another row is clicked, that one is highlighted and the current one is cleared. If one that is already highlighted is clicked again, it will be cleared back to normal.
This is within an ajax call to refresh the page, so everytime the call finishes and inserts the html for the table, this function below is then called. For some reason, every other reload of the table, this works. Ive tracked down the the line that is not functioning to be $(this).addClass(selectedUserClass) at the end of the function. The debug console log I put in there works, with the right class, but the next section does not add the class for some reason.
In firebug, the line changes from .. to , but it doesn't change? I've been looking at this for hours and I can't figure it out. Thanks for any help!
function loadUserListener(style) {
if (style == "normal") {
selectedUserClass = 'selectedUser';
selectedJQueryClass = '.selectedUser';
} else {
selectedUserClass = 'selectedUserInverted';
selectedJQueryClass = '.selectedUserInverted';
}
console.log('setting');
$("#selectuser").delegate("tr", "click", function () {
if ($(this).hasClass(selectedUserClass)) {
$(selectedJQueryClass).removeClass(selectedUserClass);
} else if ($(selectedJQueryClass)[0]) {
console.log('another');
$(selectedJQueryClass).removeClass(selectedUserClass);
$(this).addClass(selectedUserClass);
} else {
console.log(selectedUserClass);
$(this).addClass(selectedUserClass);
}
});
}
adeneo pointed out some things for me, mainly that the listener is still there even though the table is refreshed, so after every ajax call, a listener is being put on another which results in the functionality that only works on 'even' calls.
I used the 'off' function from jquery to clear the event handler every time this listener loading function is called. I also switched delegate to on to be more correct.
I essentially did this:
$('.selectuser').off("click");

Stop propagation for a specific handler

Let's say I have custom dropdown(). When the button is clicked I want to bring up the menu, and when the user clicks outside of the menu I want it to close. So I do something like this:
$(myDropDown).mousedown(dropDownMouseDown);
$("html").mousedown(htmlMouseDown,myDropDown);
function dropDownMouseDown(event) {
event.target.open();
event.stopPropagation();//I need this line or else htmlMouseDown will be called immediately causing the dropDown-menu to close right before its opened
}
function htmlMouseDown() {
this.close();
}
Well, this works. But what if I add two of these? If I click to open the first, then the same on the second then both will be open because dropDownMouseDown stops the propagation so that htmlMouseDown never gets called for the first.
How do I get around this?
If I only had these two then adding some logic for that would of course be easy, but if the quantity is dynamic? Also I might not want to call event.stopPropagation() because it will do strange stuff to other libraries I'm using which listen for that event too?
I also tried putting this line:
$("html").mousedown(htmlMouseDown,myDropDown)
inside the dropDownMouseDown-handler but it will be called immediately anyway once the bubbling reaches the html-element.
Assuming you have a selector for your dropdows, (let's say ".dropdown"), I would try to use '.not()'
$('.dropdown').mousedown(dropDownMouseDown);
$("html").on('mousedown', htmlMouseDown);
function dropDownMouseDown(event) {
event.target.open();
}
function htmlMouseDown(event) {
$('.dropdown').not($(event.target)).close();
}
Here is a fiddle in the same idea with css classes :
http://jsfiddle.net/eFEL6/4/
What about using a variable that contains the last openened one ? There are probably many other ways of doing this, but here is a way I could think of:
var lastOpened = null; // initially nothing is open (unless something is)
Then:
function dropDownMouseDown(event) {
if (lastOpened != null) { // if one is still open
lastOpened.close(); // close it
lastOpened = null; // nothing is open anymore
}
event.target.open();
lastOpened = event.target; // now this one is open
event.stopPropagation();
}
function htmlMouseDown() {
this.close();
lastOpened = null; // nothing is open
}
That should work in a way that the last opened one always close itself before opening a new one.
Thanks for the answers. They're really appreciated. I did figure out a way of doing it that I'm satisfied with. Here's how:
$(myDropDown).mousedown(dropDownMouseDown);
$("html").mousedown(myDropDown,htmlMouseDown);//Pass in the dropDown as the data argument, which can then be accessed by doing event.data in the handler
function dropDownMouseDown(event) {
event.target.open();
}
function htmlMouseDown(event) {
if (event.target!=event.data)//event.target is the element that was clicked, event.data is set to the dropdown that this handler was added for. Unless these two elements are the same then we can...
event.data.close();///close the dropdown this handler was added for
}
Can't believe I didn't think of that. In my case though the element that opens/closes has child-elements so event.target could be one of the child elements instead of the element that the handler was attached to. So I changed my html-element-handler to this:
function htmlMouseDown(event) {
var element=event.target;
while (element) {
if (element==event.data)
return;
element=element.parentElement;
}
event.data.hide();
}

How defined in jQuery was it a regular click on the same element or double-click?

How can I define in jQuery was it a regular click on the same element or double-click?
For example we have element like this:
<div id="here">Click me once or twice</div>
And we need to perform different actions after regular click and double-click.
I tried something like this:
$("#here").dblclick(function(){
alert('Double click');
});
$("#here").click(function(){
alert('Click');
});
But, of course, it doesn't work, everytime works only 'click'.
Then, some people showed me this:
var clickCounter = new Array();
$('#here').click(function () {
clickCounter.push('true');
setTimeout('clickCounter.pop()', 50);
if (clickCounter.length > 2) {
//double click
clickCounter = new Array(); //drop array
} else {
//click
clickCounter = new Array(); //drop array !bug ovethere
}
});
Here we tried to set the interval between clicks, and then keep track of two consecutive events, but this have one problem.. it doesn't work too.
So, someone knows how to do this? or can someone share a link to the material, where I can read about it?
From QuirksMode:
Dblclick
The dblclick event is rarely used. Even when you use it, you should be
sure never to register both an onclick and an ondblclick event handler
on the same HTML element. Finding out what the user has actually done
is nearly impossible if you register both.
After all, when the user double–clicks on an element one click event
takes place before the dblclick. Besides, in Netscape the second click
event is also separately handled before the dblclick. Finally, alerts
are dangerous here, too.
So keep your clicks and dblclicks well separated to avoid
complications.
(emphasis mine)
What you are doing in your question, is exactly how it should be done.
$(".test").click(function() {
$("body").append("you clicked me<br />");
});
$(".test").dblclick(function() {
$("body").append("you doubleclicked me<br />");
});
It works and here is an demo for that.
Since, you want to detect separate single double click. There is a git project for this.
$("button").single_double_click(function () {
alert("Try double-clicking me!")
}, function () {
alert("Double click detected, I'm hiding")
$(this).hide()
})
It adds up events to detect single double clicks.
Hope it helps you now.

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.

Categories