www.webdeveloper.com
Results 1 to 12 of 12

Thread: Only one of two events occurs

  1. #1
    Join Date
    May 2014
    Posts
    8

    Only one of two events occurs

    Hi, sorry if this is a dumb question, but I have a page in which two events exist, and they should both fire in a particular instance, but only one does. There is a textbox and a hyperlink, like so:

    Code:
    <input id="txtAdjustment" type="text" class="fo-adjustment" value="100%">
    <a href="#" id="lnkApplyToAll" class="button">Apply To All</a>
    When I change the content of my text box and click the button, only the 'change' event fires. When I click the button, the click event fires. How can I make both events fire when I change the textbox and then click the button?

    Here is my JavaScript:

    Code:
    $('#txtAdjustment').on('change', function () {
    	alert("change: Value in text box is: " + $('#txtAdjustment').autoNumericGet());
    	/* Do other stuff */
    });
    
    $('#lnkApplyToAll').on('click', function () {
    	alert("click: Value in text box is: " + $('#txtAdjustment').autoNumericGet());
    	/* Do other stuff */
    });
    Any help is appreciated.

    WW

  2. #2
    Join Date
    May 2014
    Posts
    8
    On another note, based on a thread I found on another site, I changed 'alert' to 'console.log.' This does not change the results; still only the change event fires as far as I can tell.

  3. #3
    Join Date
    May 2014
    Posts
    8
    Okay, I figured out that it's the "other" stuff in the change event function that is causing the issue. There is a function call that contains an AJAX call. Would the existence of this ajax call cause this behavior? Without that function call, the code works as expected.

  4. #4
    Join Date
    May 2014
    Posts
    1,043
    Ajax can greatly impact event driven stuff as they occur "out of sequence". That Ajax could be hanging the second event (meaning it would eventually run if your patient enough), or preventing the entire event bubbling stack.

    Generally it's another of the reasons I dislike jQuery, it abstracts a number of things you NEED to micromanage to do... well... what you are trying to do there.

    Though as a rule, onchange in some browsers is trumped by onsubmit not firing anyways (yes, I'm referring to legacy versions of both IE and FF), which is why usually if I'm trapping both and want both to work, I make a function of the onchange in my program scope (I put everything in anonymous functions when possible) so I can call it from BOTH events, usually with a trigger check of if the .value actually changed since my last check, so that something like your AJAX won't get run twice.

    But... question, if that's an input why are you using the anchor as it's submit... or is that just the debugging snippet in action? I get the feeling whatever form you are building is ignoring the unwritten rule of JavaScript -- "If you can't make the page work without scripting FIRST, you probably have no business adding scripting to it."

  5. #5
    Join Date
    Mar 2005
    Location
    Behind you...
    Posts
    969
    As long as the AJAX call is asynchronous then it should have no effect on anything else loading or executing. By default jQuery's AJAX request are asynchronous so unless you set this to false yourself then it shouldn't be a problem.

    Now, I do feel I'm missing some information that I might need in order to solve your problem. You appear to have two (separate) event handlers. Each triggers on a different event, each on a separate element. You are wanting these to both fire when you change the text field's value and then click on the link below and say this seems to work just fine without the AJAX request. But I would like to point out that technically the events are not firing together. Logically when you walk through it, you edit the value of the text field and the 'change' event will not fire until that element loses focus. This would occur when you click elsewhere on the page, for example on the link. And of course clicking on the link would then trigger that link's 'click' event. But the 'change' event would fire regardless once you click outside of the text field.

    So then the question arises, do you only want the 'change' event to fire if the link is being clicked? Or perhaps a better way of wording that would be, do you want all of this code to only fire when the text field has been changed and then the link is clicked (requiring both conditions to be true in order for the code to execute)?

    It would seem that in order to do this you would likely want to have the 'change' event only log whether a change was made (comparing an original value to the current value and storing the result in a global variable) and the 'click' event would use that information to determine if a change was made whenever the link is clicked. Thus clicking the link by itself does nothing and changing the text field by itself does nothing.
    "Given billions of tries, could a spilled bottle of ink ever fall into the words of Shakespeare?"

  6. #6
    Join Date
    May 2014
    Posts
    8
    Hey, thanks for the response. The change event changes the data on the screen being displayed (it's a tabbed view). The button applies the content of the input box to all of the other tabs in the context.

  7. #7
    Join Date
    May 2014
    Posts
    8
    The AJAX call is completely successfully. There is no onsubmit, just the onchange. The change event changes the data on the "visible" tab and the button changes the data on all tabs.

    I guess I could solve this by removing the change event and having two buttons - one for this tab and one for all tabs, but that seem extraneous - it seems like the code should work as-is, but it doesn't.

  8. #8
    Join Date
    Mar 2005
    Location
    Behind you...
    Posts
    969
    Well now that I have a better understanding of what you are trying to accomplish, I think actually seeing the rest of your code would allow myself and others to see where the real problem is occurring.

    I also assume this information is being stored in a database somewhere, and that each tab on your page is somehow uniquely distinguished in said database. So your 'change' event handler fires an AJAX request that updates one area (likely a single column) of the database and your 'click' event handler should be firing an AJAX request to update this data in multiple places?

    In any case seeing the rest of your code would be useful in providing you with a solution since logically the code you posted would actually work as-is.
    "Given billions of tries, could a spilled bottle of ink ever fall into the words of Shakespeare?"

  9. #9
    Join Date
    May 2014
    Posts
    8
    Ok, here is some more code. I changed some strings to obfuscate my project, but this should give you some more insight. You might note that both events (shown at the bottom here) end up calling applyChanges() with different arguments. If I comment out this line:

    Code:
    Product.App.ForecastDetails.adjustmentFactorOnChange(shiftId, false);
    it works. Inside that function, the code is actually getting in here:

    Code:
    else if (viewName == Product.App.ForecastDetails.detailsByShiftView)
    but
    Code:
    Product.App.ForecastDetails.rowIndex
    is zero, so it doesn't get to the point where element.focus() is being called.

    Hopefully this doesn't create more confusion.

    Code:
    Product.App.ForecastDetails.initialize = function () {
        $('a[id^="lnkSave"]').on('click', function () {
            if (!$(this).attr('disabled')) {
                if ($('input[id^="txtSgcOverride"]').hasClass('validation-errors') || $('input[id^="txtAdjustmentFactor"]').hasClass('validation-errors')) {
                    $('#validation-errors').dialog({
                        resizable: false,
                        width: Isas.DialogWidth.normal,
                        modal: true,
                        buttons: {
                            "OK": function () {
                                $(this).dialog('close');
                            }
                        }
                    });
                } else {
                    var shiftId = Product.App.GetIdFromElement(Product.App.ForecastDetails.GetSelectedTab());
                    $.ajax({
                        url: Product.App.ForecastDetails.saveActionUrl,
                        async: true,
                        cache: false,
                        data: { 'coId': $('#ddlFacility').val(), 'departmentNumber': $('#ddlDepartment').val(), 'scheduleStartDate': $('#ddlSchedulePeriod option:selected').text(), "shiftId": shiftId },
                        success: function (result) {
                            if (result.Successful) {
                                $('#divForecastOutputContainer').html(result.Data);
                                Product.App.ForecastDetails.initialize();
                                Isas.boolHasChanges = false;
                                Isas.showSuccesfullMessage('Changes Saved', 'Forecast details have been saved.');
                            } else {
                                Isas.handleAjaxCallFailure(result);
                            }
                        },
                        error: Isas.handleAjaxSystemError
                    });
                }
            }
            return false;
        });
    
    Product.App.ForecastDetails.applyChanges = function (viewName, shiftId, adjustment, roundingPreferenceFlag, date, override, finalForecastFlag) {
        Product.boolHasChanges = true;
        if (viewName != Product.App.ForecastDetails.detailsView) {
            $("#divForecastVsClinicalActualAnalysisPanel").accordion("option", "active", false);
            $("#divForecastVsClinicalActualAnalysisContainer").empty();
        }
        $.ajax({
            url: Product.App.ForecastDetails.applyChangesActionUrl,
            async: true,
            cache: false,
            data: { 'coId': $('#ddlFacility').val(), 'departmentNumber': $('#ddlDepartment').val(), 'scheduleStartDate': $('#ddlSchedulePeriod option:selected').text(), 'viewName': viewName, "shiftId": shiftId, "adjustment": adjustment, "roundingPreferenceFlag": roundingPreferenceFlag, "date": date, "overrodeForecast": override, "finalForecastFlag": finalForecastFlag },
            success: function (result) {
                if (result.Successful) {
                    if (viewName == Product.App.ForecastDetails.detailsView) {
                        $('#divForecastOutputContainer').html(result.Data);
                        Product.App.ForecastDetails.initialize();
                    } else if (viewName == Product.App.ForecastDetails.detailsByShiftView) {
                        $('#foShiftTab' + shiftId).html(result.Data);
                        Product.App.ForecastDetails.initializeShiftTab(shiftId);
                        if (Product.App.ForecastDetails.rowIndex > 0) {
                            var element = $('#txtOverride' + shiftId + "-" + Product.App.ForecastDetails.rowIndex);
                            Product.App.ForecastDetails.rowIndex = 0;
                            if (element.length) {
                                element.focus();
                                element.select();
                            } else {
                                $('#lnkSave2').focus();
                            }
                        }
                    } else if (viewName == Product.App.ForecastDetails.impactLaborAnalysisByDayView) {
                        Product.App.ForecastDetails.showImpactLaborAnalysisByDay(shiftId, date, result.Data);
                    }
                }
                else {
                    Product.handleAjaxCallFailure(result);
                }
            },
            error: Product.handleAjaxSystemError
        });
    };
    
    Product.App.ForecastDetails.adjustmentFactorOnChange = function (shiftId, isOnload) {
        var value = parseInt($('#txtAdjustmentFactor' + shiftId).autoNumericGet());
    
        if (!isOnload) {
            Product.App.ForecastDetails.applyChanges(Product.App.ForecastDetails.detailsByShiftView, shiftId, value, '', null, null, Product.App.Enums.FinalForecastFlag.Adjusted);
        }
    };
    
    
        $('#txtAdjustmentFactor' + shiftId).on('change', function () {
            console.log('change');
            Product.App.ForecastDetails.adjustmentFactorOnChange(shiftId, false);
        });
    
        $('#lnkApplyToAllShifts' + shiftId).on('click', function () {
            console.log('click');
            if (!$(this).attr('disabled')) {
                Product.App.ForecastDetails.applyChanges(Product.App.ForecastDetails.detailsView, shiftId, $('#txtAdjustmentFactor' + shiftId).autoNumericGet(), $('#ddlPreference' + shiftId).val(), null, null, '');
            }
            return false;
        });

  10. #10
    Join Date
    May 2014
    Posts
    8
    I did notice that applyChanges calls initializeShiftTab, which is the function in which a bunch of this code exists:

    Code:
    Product.App.ForecastDetails.initializeShiftTab = function (shiftId) {
        Product.App.ForecastDetails.hasModifyPermission = $('#hdnHasModifyPermission' + shiftId).val() == 'True';
        Product.App.ForecastDetails.isFinalForecastExceedsSGCapacity = $('#hdnIsFinalForecastExceedsSGCapacity' + shiftId).val() == 'True';
        $('a[id^="lnkSave"]').attr('disabled', !Product.App.ForecastDetails.hasModifyPermission);
        Product.App.ForecastDetails.resizeForecastOutputContainer(shiftId);
    
        $("#divImpactLaborAnalysisPanel" + shiftId).accordion({
            collapsible: true,
            active: 'none',
            autoHeight: false,
            change: function (event, ui) {
                if (ui.options.active === 0 && ui.newContent.children().length == 0) {
                    Product.App.ForecastDetails.renderImpactLaborAnalysisByShift(shiftId);
                }
            }
        });
    
        $("#divForecastAnalysisPanel" + shiftId).accordion({
            collapsible: true,
            active: 'none',
            autoHeight: false,
            change: function (event, ui) {
                if (ui.options.active === 0 && ui.newContent.children().length == 0) {
                    Product.App.ForecastDetails.renderForecastAnalysis(shiftId);
                }
            }
        });
    
        if (Product.App.ForecastDetails.hasModifyPermission) {
            $('input[id^="txtSgcOverride' + shiftId + '"]').autoNumeric({ aSep: '', vMin: '0', vMax: '99999', wEmpty: 'zero' });
        }
    
        $('input[id^="txtSgcOverride' + shiftId + '"]').each(function () {
            var override = parseInt($(this).autoNumericGet());
            if (override > 32767) {
                Isas.bindNotifyEvents($(this), Isas.tooltipDisplay('Invalid entry', 'Override cannot be greater than 32767.', 'error'));
            }
        });
    
        $('#tblStaffingGridCapacity' + shiftId).on('click', 'input[id^="txtSgcType"]', function () {
            if (Product.App.ForecastDetails.hasModifyPermission) {
                Product.App.ForecastDetails.staffingGridCapacityChange(shiftId, null, $(this).val() == 'Override' ? -1 : null);
            }
        });
    
        $('#tblStaffingGridCapacity' + shiftId).on('click', 'input[id^="txtSgcMaxCensusPeak"]', function () {
            if (Product.App.ForecastDetails.hasModifyPermission && !$(this).hasClass('selected') && $(this).val()) {
                var weekdayId = Product.App.GetIdFromElement(this);
                Product.App.ForecastDetails.staffingGridCapacityChange(shiftId, weekdayId, null);
            }
        });
    
        $('#tblStaffingGridCapacity' + shiftId).on('click', 'input[id^="txtSgcOverride"]', function () {
            if (Product.App.ForecastDetails.hasModifyPermission) {
                if (!$(this).hasClass('selected')) {
                    var weekdayId = Product.App.GetIdFromElement(this);
                    Product.App.ForecastDetails.columnIndex = parseInt(weekdayId);
                    Product.App.ForecastDetails.staffingGridCapacityChange(shiftId, weekdayId, $(this).autoNumericGet());
                } else {
                    $(this).select();
                }
            }
        });
    
        $('#tblStaffingGridCapacity' + shiftId).on('change', 'input[id^="txtSgcOverride"]', function () {
            if ($(this).autoNumericGet()) {
                var weekdayId = Product.App.GetIdFromElement(this);
                Product.App.ForecastDetails.columnIndex = parseInt(weekdayId) + 1;
                Product.App.ForecastDetails.staffingGridCapacityChange(shiftId, weekdayId, $(this).autoNumericGet());
            }
        });
    
        if (Product.App.ForecastDetails.hasModifyPermission && $('#hdnIsForecastGenerated' + shiftId).val() == 'True') {
            $('#txtAdjustmentFactor' + shiftId).autoNumeric({ aSign: '%', pSign: 's', vMin: '0', vMax: '999', wEmpty: 'sign' });
        }
    
    //    Product.App.ForecastDetails.adjustmentFactorOnChange(shiftId, true);
    
        $('#txtAdjustmentFactor' + shiftId).on('change', function () {
            Product.App.ForecastDetails.adjustmentFactorOnChange(shiftId, false);
        });
    
        $('#ddlPreference' + shiftId).on('change', function () {
            Product.App.ForecastDetails.applyChanges(Product.App.ForecastDetails.detailsByShiftView, shiftId, null, $(this).val(), null, null, '');
        });
    
        $('#lnkApplyToAllShifts' + shiftId).on('click', function () {
            if (!$(this).attr('disabled')) {
                Product.App.ForecastDetails.applyChanges(Product.App.ForecastDetails.detailsView, shiftId, $('#txtAdjustmentFactor' + shiftId).autoNumericGet(), $('#ddlPreference' + shiftId).val(), null, null, '');
            }
            return false;
        });
    
        if (Product.App.ForecastDetails.hasModifyPermission) {
            $('input[id^="txtOverride' + shiftId + '"]').autoNumeric({ aSep: '', vMin: '0', vMax: '99999', wEmpty: 'zero' });
        }
    
        $('input[id^="txtFinal' + shiftId + '"]').each(function () {
            var warningMsg = $(this).attr('warningMessage');
            if (warningMsg) {
                Isas.bindNotifyEvents($(this), Isas.tooltipDisplay('Needs Verification', warningMsg), 'fo-exceeds-sg-capacity');
            }
        });
    
        $('#tblForecastOutput' + shiftId).on('click', 'input[id^="txtSystem"]', function () {
            if (Product.App.ForecastDetails.hasModifyPermission && $('#hdnIsForecastGenerated' + shiftId).val() == 'True' && !$(this).hasClass('selected')) {
                var date = $('#' + $(this).attr('id').replace("txtSystem", "txtDate")).val();
                Product.App.ForecastDetails.applyChanges(Product.App.ForecastDetails.detailsByShiftView, shiftId, null, '', date, null, Product.App.Enums.FinalForecastFlag.System);
            }
        });
    
        $('#tblForecastOutput' + shiftId).on('click', 'input[id^="txtAdjusted"]', function () {
            if (Product.App.ForecastDetails.hasModifyPermission && $('#hdnIsForecastGenerated' + shiftId).val() == 'True' && !$(this).hasClass('selected')) {
                var date = $('#' + $(this).attr('id').replace("txtAdjusted", "txtDate")).val();
                Product.App.ForecastDetails.applyChanges(Product.App.ForecastDetails.detailsByShiftView, shiftId, null, '', date, null, Product.App.Enums.FinalForecastFlag.Adjusted);
            }
        });
    
        $('#tblForecastOutput' + shiftId).on('click', 'input[id^="txtBreakpoint"]', function () {
            if (Product.App.ForecastDetails.hasModifyPermission && $(this).val() && !$(this).hasClass('selected')) {
                var date = $('#' + $(this).attr('id').replace("txtBreakpoint", "txtDate")).val();
                Product.App.ForecastDetails.applyChanges(Product.App.ForecastDetails.detailsByShiftView, shiftId, null, null, date, null, Product.App.Enums.FinalForecastFlag.Breakpoint);
            }
        });
    
        $('#tblForecastOutput' + shiftId).on('click', 'input[id^="txtOverride"]', function () {
            if (Product.App.ForecastDetails.hasModifyPermission) {
                if (!$(this).hasClass('selected')) {
                    var date = $('#' + $(this).attr('id').replace("txtOverride", "txtDate")).val();
                    Product.App.ForecastDetails.rowIndex = parseInt(Product.App.GetIdFromElement(this));
                    Product.App.ForecastDetails.applyChanges(Product.App.ForecastDetails.detailsByShiftView, shiftId, null, '', date, $(this).val(), Product.App.Enums.FinalForecastFlag.Override);
                } else {
                    $(this).select();
                }
            }
        });
    
        $('#tblForecastOutput' + shiftId).on('change', 'input[id^="txtOverride"]', function () {
            var date = $('#' + $(this).attr('id').replace("txtOverride", "txtDate")).val();
            Product.App.ForecastDetails.rowIndex = parseInt(Product.App.GetIdFromElement(this)) + 1;
            Product.App.ForecastDetails.applyChanges(Product.App.ForecastDetails.detailsByShiftView, shiftId, null, null, date, $(this).val(), Product.App.Enums.FinalForecastFlag.Override);
        });
    
        $('#tblForecastOutput' + shiftId).on('click', 'a[id^="lnkBreakpoint"]', function () {
            var date = $('#' + $(this).attr('id').replace("lnkBreakpoint", "txtDate")).val();
            Product.App.ForecastDetails.renderImpactLaborAnalysisByDay(shiftId, date);
            return false;
        });
    };

  11. #11
    Join Date
    May 2014
    Posts
    8
    Believe it or not, I'm still working on this problem. I went to the length of creating a new web app and trying to re-create the problem. I used the same versions of JQuery and AutoNumeric. However, all of my tests in my test app worked as expected.

    Here's where I am now. Both the textbox and the button end up calling the same function. The function in question has an AJAX call which succeeds. When I remove the AJAX call, the code works as expected - both events fire. So, I tried replacing the code with a different AJAX call - one that just fetches a string from a simple function. The AJAX call works as expected, but the second event does not fire. In other words, ANY (in my sample of 2) AJAX calls in this common function causes the second event to not fire.

    Anyone have any idea why that might be happening? It's as if the second event doesn't even exist. If you click the button any other time, it fires fine, but when changing the text in the textbox and then clicking the button without leaving the textbox, only the textbox's change event fires. The button's click event never fires.

    Help is appreciated; I'm a bit perplexed.

    Woodnote.

  12. #12
    Join Date
    May 2014
    Posts
    1,043
    Yeah, it's what I said about how AJAX requests can cancel or hang event bubbling on the page. It's annoying, but it happens. ALL the time. Usually why I don't try to do what you are trying to do. Sadly, it's a bit of a "doctor, doctor! It hurts when I do this" moment.

    Which if I were to do it, it's why I'd put the onchange event handler as a separate function that checks if there's been an actual change since it was run last. I'd then call it both onchange and onsubmit.
    Java is to JavaScript as Ham is to Hamburger.

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
HTML5 Development Center



Recent Articles