Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery Modal Confirmation Dialog Not Submitting Form

I am trying to pop up a confirmation modal when the user presses the delete button on the edit form. The modal pops up fine, but when jQuery should be submitting the form, it's not doing anything. I have delete as a type="button", because when it is of type submit the modal function does not hold up the process and it just deletes the user right away.

The HTML ...

-- EDIT --

(I added the <form> tags)

<form action="/admin/edit-user" enctype="application/x-www-form-urlencoded" method="post" name="edit_user_form" id="edit_user_form">
...
<p><input type="submit" value="Save" name="submit" id="submit"/></p>
<p><input type="submit" value="Cancel" name="cancel" id="cancel"/></p>         
<p><input type="button" value="Delete User" name="delete_btn" id="delete_btn" onclick="confirmDeleteUser();"/></p>
...
</form>

...

<div id="dialog-modal" title="Confirm Delete User">
<p><span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 0 0;"></span> Are you sure you wish to delete this user?</p>
<p>To continue editing, click cancel.</p>
</div>

The Javascript:

   function confirmDeleteUser()
    {    
        $('#dialog-modal').dialog({
            autoOpen: false,
            width: 400,
            modal: true,
            resizable: false,

            buttons: {
                "Cancel": function() {
                    $(this).dialog("close");
                    return false;
                },
                "Delete User": function() {
                    var self = $(this);
                    var form = $('#edit_user_form');
                    tmpElm = $('<input type="hidden" />');
                    tmpElm.attr('name', 'delete');
                    tmpElm.attr('id', 'delete');
                    tmpElm.val(true);
                    tmpElm.appendTo(form);
                    form.submit();
                    return true;
                }
            }
        });
        $('#dialog-modal').dialog('open');
    }

When I inspect the source, I'm seeing that the code is properly appending the new hidden element, but the submit just doesn't seem to want to fire. What step am I missing?

like image 623
ashurexm Avatar asked Aug 01 '11 23:08

ashurexm


2 Answers

Try it a different way.


HTML

Your html has the following: onclick="confirmDeleteUser();"

Why? jQuery is supposed to make this easier for you, not harder.

Your HTML should be pure and not calling functions (with the exception of ultra-extreme circumstances you are very unlikely to encounter). Why not use the jQuery library to bind the event to the element, rather than mix javascript function calls into your HTML? You should be doing something like this in the <script> tags, after a document ready statement.

$("#delete_btn").click(function(e){
    /*Code that runs on click of the "delete_btn" ID tag*/
});

If you're unfamiliar with jQuery selectors and events then start reading here.

  • You can find all the events here.
  • You can find all the selectors here

The other reason you should do this is in the event the document isn't correctly/fully loaded in order to prevent it from breaking on your users.


CSS

You've also done this: style="float:left; margin:0 7px 0 0;" in an HTML tag? That's evil, dude. Just evil. How am I going to maintain this code in five months?

Instead, use CSS.

In your tags, or CSS file, you need an entry such as:

.dialogAdjust {
    float: left;
    margin: 0 7px 0 0;
}

Then in your HTML you would say:

<span class="ui-icon ui-icon-alert dialogAdjust"></span>

And now you can tweak the thing to your heart's content. It's better if you can make the class on the dialog div, rather than individual HTML elements, and in this case you absolutely can.


JavaScript

Hokay, so, here's your function:

  function confirmDeleteUser()
    {    
        $('#dialog-modal').dialog({
            autoOpen: false,
            width: 400,
            modal: true,
            resizable: false,

            buttons: {
                "Cancel": function() {
                    $(this).dialog("close");
                    return false;
                },
                "Delete User": function() {
                    var self = $(this);
                    var form = $('#edit_user_form');
                    tmpElm = $('<input type="hidden" />');
                    tmpElm.prop('name', 'delete');
                    tmpElm.prop('id', 'delete');
                    tmpElm.val(true);
                    tmpElm.appendTo(form);
                    form.submit();
                    return true;
                }
            }
        });
        $('#dialog-modal').dialog('open');
    }

What's going on here? First step, you should try and use a tool to measure code quality. Two popular ones are JSHint and JSLint. You don't need to follow things they say like it's the only way to write your code, but it's immensely helpful in finding bugs due to small mistakes. I like JSHint, so we're going to run it through that.

And here's the output:

Errors:

Line 17 tmpElm = $('<input type="hidden" />');
'tmpElm' is not defined.
Line 18 tmpElm.attr('name', 'delete');
'tmpElm' is not defined.
Line 19 tmpElm.attr('id', 'delete');
'tmpElm' is not defined.
Line 20 tmpElm.val(true);
'tmpElm' is not defined.
Line 21 tmpElm.appendTo(form);
'tmpElm' is not defined.

Oops. Looks like you've got an undefined variable in there, meaning it's now global. Global variables are bad, they break scope and can make your code function in strange ways.

We need to fix that. You should always declare local variables in local scope. That means putting a var tempElm; at the top of the "Delete User" function.

Do away with that function wrapper, you won't need it. Your code should create the dialog object and code when the document is done loading, and open the dialog when it's clicked. What is happening in your original code both creating the dialog and opening it every time you click that button. What's the problem with that? You keep creating the object, even though it's created. You're creating the same object again and again and again. In this implementation, you won't notice it, but your design will carry over to places it will unless you take notice of this now.

So, what does that look like? In your <head> tag you should see something like this:

<script>
    
    /*
        This makes all the code inside
        run when the window is done loading, 
        not before, which may cause issues.
    */
    $(window).load(function(){    
    
    
        /*
            This sets up the dialog 
            as you've described before
        */
        $('#dialog-modal').dialog({
            autoOpen: false,
            width: 400,
            modal: true,
            resizable: false,

            buttons: {
                "Cancel": function() {
                    $(this).dialog("close");
                    return false;
                },
                "Delete User": function() {
                    var self = $(this);
                    var form = $('#edit_user_form');
                    //We've added the var infront of tepElem
                    var tmpElm = $('<input type="hidden" />');
                    tmpElm.prop('name', 'delete');
                    tmpElm.prop('id', 'delete');
                    tmpElm.val(true);
                    tmpElm.appendTo(form);
                    form.submit();
                    return true;
                }
            }
        });
        
        /*
            This is the part where I talked 
            about selectors and events in the HTML
        */
        $("#delete_btn").click(function(e){
            $('#dialog-modal').dialog('open');
        });
    });
</script>

When asking for help, use a tool like jsFiddle to post JavaScript in to make it easier for other people to help you.

Here's a jsFiddle of the revisions we've made so far. Spend a bit of time learning how to use it if you're doing a lot of work in JavaScript and want to test something really quickly.

Here's why I wanted you to learn jsFiddle:

You didn't give us enough code to work with successfully, thus leading to me writing this huge post about code quality and how to ask questions, doubly so when you post a bounty.

If you want help, don't make people work really hard for it. You won't get good help unless someone is totally insane.

jsFiddle requires you post actual working code (or non-working code) that lets us see if there's a problem with form.submit(), such as any strange attributes or properties on the form element, or any number of other issues that could be kicking around that you excluded.

So let's look at what's breaking your "Delete User" function.

function() {
    var self = $(this);
    var form = $('#edit_user_form');
    tmpElm = $('<input type="hidden" />');
    tmpElm.prop('name', 'delete');
    tmpElm.prop('id', 'delete');
    tmpElm.val(true);
    tmpElm.appendTo(form);
    form.submit();
    return true;
}
  • Why have you declared self? You never use it once. Lets get rid of that.
  • You could use something called chaining, it's really up to you. Some people hate it.
  • You append a whole new input to the form for something that looks like a choice, I'm not sure this is wise. I'd suggest changing the value of a normal input, or using another pattern, this could lead to a lot of issues, but for the sake of the question I'll imagine it's done for all the right reasons. Be careful however, if someone double clicks submit, that input's in there two times.
  • form is a word reserved by used in the DOM, you should avoid using that one to avoid confusion between your variable and the DOM API.
  • Which form submission button are we clicking here? You have a lot, and jQuery isn't going to guess and hope for the best.

How forms should look:

The w3 explains what forms are and the purpose of things.

Key points:

  • Names are paired with values
  • The submit button clicked is sent, the submit buttons not clicked are not.
  • The DOM is can be navigated based on NAME and ID attributes

Something weird is going on in your form.

Here's a form that javascript understands how to submit: http://jsfiddle.net/YS8uW/2/

Here's javascript attempting to submit your form (stripped down to bare-bones): http://jsfiddle.net/LfDXh/

What's different here?

  • You have IDs all over the place.
  • You use keywords for names

Lets look at something you've done, given an ID of submit to something: http://jsfiddle.net/fdLfC/

And what happens when we don't use that as an ID? http://jsfiddle.net/fdLfC/1/

Ahh. So that's weird.

It's like giving it an id of submit won't let you call submit. A proper explanation is, you've re-written it because the dom did it when you assigned that ID and Name, it's trying to call the element.

You can see this if you open up a debugger or something, it gives you an alert to the effect of:

TypeError: Property 'submit' of object # is not a function

Keep away from using keywords to mean something else and you won't fall into these weird traps.

A big thank you to @MattMcDonald for linking me to this resource, which explains how to deal with, and why, NAME and ID attributes over-write the built-in HTML DOM methods.

Do the other stuff I said too. Disclaimer: Everyone's going to wage war on me saying it's not absolute that you should be doing all those things, I agree, but I think this post is long enough, and we'll all agree that doing these things is a step forward in code quality, not backwards. It's up to you at the end, but try avoiding mixing things together into a huge messy pot. Think about the execution of your code and how it's happening also.

like image 137
Incognito Avatar answered Nov 01 '22 23:11

Incognito


If you are sure it appends the hidden input, then the problem must be in using duplicate ID. The button and the hidden input have the same ID. Make them different and try again.

like image 36
avetarman Avatar answered Nov 01 '22 23:11

avetarman