Delegate and AddClass not working - javascript

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");

Related

jQuery.remove() and setTimeout interaction

I have a jQuery function that creates an element when the user hovers on a element. When the mouse goes away from the element I set a timeout to destroy it. While creating and normally destroying the element does work flawlessly, the remove inside the timeout does not work and dies silently. I suspect there is some problem in the interaction between window.setTimeout and jQuery.remove, but couldn't find anything on the web.
My code:
//Element creation
function createElement(){
$(".my_class").remove()
content = compose_content() //It's an <ul>
$('<div></div>', {
class : "my_class",
html: content
}).insertAfter($("#first_row"))
}
//Element destruction
function setDestroyTimeout(){
if(perform_some_checks()){
window.setTimeout(function(){
console.log("Removing...")
$(".my_class").remove()
}, 1000)
}
}
$(".another_class").hover(createElement(), setDestroyTimeout())
The first remove (the one in the createElement function) works smoothly, while the second doesn't do anything. The Timeout works as well, I checked with the console.log instruction.

Passing "this" to a function in jQuery problems

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.

Javascript callback after on change event has partial page refresh

I am using a web framework which uses partial page rendering to refresh a part of a screen using the change event of a input field. I don't have access to the function for the change event, but I can inject jquery to add my own change event for that field. Unfortunately the field is also refreshed in the partial page rendering.
I am trying to inject my own code to add some styling to that part of the page. Unfortunately the $(document).ready only fires once at the beginning, so when the partial page refresh happens my code gets lost.
If I use $(document).change or attach directly to the change event of the input field my code fires before the framework's change and partial page refresh and therefore also gets lost.
So the only thing I could get to work is to wait 3 seconds with a callback and then apply my code again. This is however very ugly and on slow machines it sometimes needs more than 3 seconds.
Is there any way of attaching a callback to the change event that fires after the partial page refresh.
The framework uses very old web standards (table layout) and also tends to return false on a lot of it's change function's. Not sure if this will prevent any further propogation, that is to say prevent my callback.
This is the input element:
<input id="N24:MiscRt1:0" title="Miscellaneous Rate 1" class="xa" onchange="_uixspu('DefaultFormName',1,'DynCalcUpd','MiscRt1',0,1,{'_FORM_SUBMIT_BUTTON':'_fwkActBtnName_MiscRt1_DynCalcUpdYAS-AIio','evtSrcRowId':'AllocationsAM.AllocationsPlanVO321831001-132183-321838243CWBASG8243CWBPERFAPPRAISALOSCgK_CA','DynCalcUpdCol':'Misc1ValUGoCYJgF','evtSrcRowIdx':'06cW78OeE'});return true;" name="N24:MiscRt1:0" size="10" type="text" value="0.00" maxlength="38">
This is the code I tried:
function xxcwb_func() {
$('span#CompTable table.x1n:nth-child(1) tbody tr td input').on('click', function(e) {
e.stopPropagation();
});
$('span#CompTable table.x1n:nth-child(1) tbody tr td select').on('click', function(e) {
e.stopPropagation();
});
$('span#CompTable table.x1n:nth-child(1) tbody tr td').on('click', function() {
if ($(this).hasClass('xxcwb_highlight')) {
$(this).parent().find('td').css('background', '#f2f2f5');
$(this).parent().find('td').removeClass('xxcwb_highlight');
}
else {
$(this).parent().parent().find('td').css('background', '#f2f2f5').removeClass('xxcwb_highlight');
$(this).parent().find('td').css('background', '#97cbf6');
$(this).parent().find('td').addClass("xxcwb_highlight");
}
});
addSeparator('Current Salary');
addSeparator('Proposed Increase Amount');
addSeparator('PDD New Salary');
addSeparator('Proposed Salary');
addSeparator('LM New Salary');
addSeparator('HR New Salary');
addSeparator('CEO New Salary');
addSeparator('Current Car Benefit');
addSeparator('Current Total Cash');
}
$(document).ready(function() {
// Run our code.
xxcwb_func();
});
$(document).change(function() {
xxcwb_func();
});
The callback also has to wait for the other change function and the partial page refresh to finish first.
$(document).change(function() {
window.setTimeout(xxcwb_func,0);
});
This will push it to the browsers TODO list (with a due date specified in the second argument), which it will get around to doing after the partial page refresh occurs.
try
setInterval(function{
/* check if the element is already "evented" */
if (!$("#myelement").data("alreadyEvented")) {
/* set the flag that it is ok now: element is evented */
$("#myelement").data("alreadyEvented","true");
$("#myelement").click(function{ /* or another event */
/* your event */
});
}
},50);
it is simple, dumb, and should be working :)
after partial refresh the attribute will be removed as well -> event will be assigned again

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();
}

Function stops after click()

I'm trying to simulate two click to open a menu. The first one opens the menu and the second the submenu but after the first click() the function stops.
This is my JS code:
function open_menu(id_menu, id_sub_menu) {
$('.sous-domaines a#lien-domaine-'+id_menu).click();
$('a#lien-menu-'+id_sub_menu).click();
}
I call my function in HTML with this code:
<span onClick="open_menu('0', '116')">Open sub-menu</span>
You're doing it too fast maybe.
Wrap the second one in a window.setTimeout()
can you do something like this ? set a bool after first click
var IsClick = false;
function open_menu(id_menu, id_sub_menu) {
$('.sous-domaines a#lien-domaine-'+id_menu).click();
if (IsClick){ fnSecondClick();}
}
function fnSecondClick() {
//open submenu
}
or something like this - on click check if menu is visible:
function open_menu(id_menu, id_sub_menu) {
$('.sous-domaines a#lien-domaine-'+id_menu).click();
if ($(element).is(":visible")){ fnSecondClick();}
}
Well you can give this a try, it is pretty much waht you had, other than the fact i don't have menu open somewhere
http://jsfiddle.net/tRg3e/2/
I think the .click() only works if you have a handler attached, it does not trigger the native click on the element.. I tried it without handler and the link does not go
You should stray away from 'onClick' declared in elements like that, it's bad practice if I do recall. Nonetheless, it still works.
If you could provide some HTML code it would help clarify the idea but you could set a trigger event for example:
http://jsfiddle.net/wrN2u/3/
EDIT: With a toggle - http://jsfiddle.net/wrN2u/18/

Categories