James
James

Reputation: 89

Scope for javascript variables

I am new to JavaScript and I am having trouble understanding the scope of variables in some script that I have pieced together from examples I have found. The code below is part of a tutorial that Matt Berseth put out on his website. The application has an ajaxToolKit:ModalPopupExtender with JavaScript functions to execute when the Yes or No buttons are clicked. The two variables that are supposed to hold the delete button location and a div do not seem to get populated and thus the code exceptions. When I click on the Yes or No button both the _Source and _popup variables are undefined.

I would really appreciate the time taken to provide an explanation as to what I have setup wrong in my code.

The button that is fires the OnClientClick event calling the SubmitPayment function    
<asp:Button ID="btnSubmit" runat="server" Text="Submit" OnClientClick="return SubmitPayment(this); return false;" UseSubmitBehavior="False" AccessKey="S" ValidationGroup="Manual" />

A hidden field to save a value in (tested later in the javascript)
    <div id="divHiddenFields">
        <asp:HiddenField ID="hfTotalAmtDue" runat="server" />
    </div>

The Dialog Panel
    <div id="divConfirmPaymentDialog">
        //panel that makes up confirmation dialog
        <asp:Panel ID="pnlConfirmPaymentDialog" runat="server" style="display:none" CssClass="modalPopup" Height="200" Width="450">
            <div align="center">
                <p class="info">You are attempting to make a payment when your account(s) has/have no balance!</p>
                <p class="info">If you do this you will have a credit applied to your account in the amount of your payment.</p>
                <p class="info">Are you sure that you want to do this?</p>
                <asp:Button ID="btnConfirmPaymentYes" runat="server" Text="Yes" Width="75" />
                <asp:Button ID="btnConfirmPaymentNo" runat="server" Text="No" Width="75" />
            </div>
        </asp:Panel>
        //modal dialog extender that implements showing the modal dialog with panel
        <cc1:ModalPopupExtender ID="mpeConfirmPayment" runat="server" BehaviorID="mpeConfirmPaymentBehaviorID" BackgroundCssClass="modalBackground" 
        CancelControlID="btnConfirmPaymentNo"  OnCancelScript="btnConfirmPaymentNo_Click();" OkControlID="btnConfirmPaymentYes" OnOkScript="btnConfirmPaymentYes_Click();" 
        PopupControlID="pnlConfirmPaymentDialog" TargetControlID="pnlConfirmPaymentDialog" />
    </div>   

The Javascript
    <script type="text/javascript">       
        //this system function sets the App_init handler function as the initialization handler
        Sys.Application.add_init(App_Init);
        //this function handles the hookup of the beginrequest and endrequest ="divhandlers.  The two functions are called
        //at the begin and end of the webpage lifecycle
        function App_Init()
        {
          Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(BeginRequest);
          Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequest);
        }
        //this function handles the begining of the webpage lifecylce
        function BeginRequest(sender, args){
            $(document).ready(function(){
                //if btnYes was clicked then block the UI
                $('#<%= btnYes.ClientID %>').live('click', function(e){
                    //e.preventDefault();
                    $.blockUI();
                });
            });
        }
        //this function handles the end of the webpage lifecylce
        function EndRequest(sender, args) {
            //call unblockUI 
            $(document).ready(function() {
                $('#<%= btnYes.ClientID %>').live('click', function(e) {
                    $.unblockUI();
                });
            });

            //check for errors that occurred during page execution
            if (args.get_error() != undefined) {
                var errorMessage;
                if (args.get_response().get_statusCode() == '200') {
                    errorMessage = args.get_error().message;
                }
                else {
                    // Error occurred somewhere other than the server page.
                    errorMessage = 'An unspecified error occurred. ';
                }
                args.set_errorHandled(true);
                if (errorMessage.indexOf('ValidationError:') > 0) {
                    alert(errorMessage.replace('Sys.WebForms.PageRequestManager', '').replace('ServerErrorException:', ''));
                }
            }
        }
        //this funcion will raise the viewccreceipt dialog when called
        function OpenReceipt() {
            window.open('ViewCCReceipt.aspx', 'Name', 'height=600,width=800');
        }

        //  keeps track of the delete button for the row
        //  that is going to be removed
        var _source;
        // keep track of the popup div
        var _popup;

        //This function will be called when the submit button on the creditcard entry screen is pressed.
        //The function will check to see if the balance is already zero and message the customer that they will have a
        //credit balance if they continue and allow the to confirm the payment.
        function SubmitPayment(source) {
            $(document).ready(function() {
                //Get the Total Amount Due from hidden field hfTotalAmountDue
                var TotalAmtDue = $('#<%= hfTotalAmtDue.ClientID %>').val();
                if (TotalAmtDue <= 0) {
                    this._source = source;
                    this._popup = $find('mpeConfirmPaymentBehaviorID');
                    //  find the confirm ModalPopup and show it    
                    this._popup.show();
                }
            });
        }
        //event handler for the btnConfirmPaymentYes button
        //when this handler is executed the modal popup is hidden and a postback is done
        function btnConfirmPaymentYes_Click(){
            //  find the confirm ModalPopup and hide it    
            this._popup.hide();
            //  use the cached button as the postback source
            __doPostBack(this._source.name, '');
        }
        //event handler for the btnConfirmPaymentNo button
        //when this handler is executed the modal popup is hidden and the postback permanently shut down
        function btnConfirmPaymentNo_Click(){
            //  find the confirm ModalPopup and hide it 
            this._popup.hide();
            //  clear the event source
            this._source = null;
            this._popup = null;
        }
    </script>

Upvotes: 1

Views: 394

Answers (2)

beatgammit
beatgammit

Reputation: 20215

Scope is a tricky thing, and here are the basics.

The only scope is function scope. Variables declared in a function stay in the function. Variables declared outside of a function are in global scope. Global variables are to be avoided though, as they're bad style.

Functions are objects that don't have a home. You can only depend on the value of this in two circumstances:

  • You called the function like this: functionname.call(context, params), where context is the value of this
  • Your function is part of another object. this will be the object it's a part of
    • Note: if the function is detached from the function, all bets are off

If a function is called in this way: functionname(params), then this will be whatever this was in the calling scope. It's kind of weird though, so if you're going to use this, be sure you know what it is.

You should assume that this will never refer to the global scope, and you should never use this in the global scope.

I would use some kind of packager to avoid using the global scope. I use ender.js, and it's nice because the default packages are pretty similar to jQuery, so there's a small learning curve.

Douglas Crockford is the man at explaining JavaScript:

http://javascript.crockford.com/

Upvotes: 1

Mike Samuel
Mike Samuel

Reputation: 120496

this._source is different from var _source in JavaScript.

Instead of doing

this._source = source;
this._popup = $find('mpeConfirmPaymentBehaviorID');

maybe you should do

_source = source;
_popup = $find('mpeConfirmPaymentBehaviorID');

which will assign it (on document load) to variables declared in a scope that contains the event handler function definitions: btnConfirmPayment{No,Yes}_Click.

A lot of the time the two are equivalent since this refers to the global scope by default, and your var declaration is in the global scope, but on load event handlers are probably run with this being some DOM node.

Upvotes: 2

Related Questions