Using jQuery with Flask: How to avoid jumping at the top of the page when submitting a form?

advertisements

I have a single-page site in Flask with a contact form at the bottom. How can I prevent the browser from jumping to the top of the page on submission? I can't use return false because there's not really any explicit JavaScript happening; I am using Flask's WTForms.

<section id="contact">
<h2 class='contact'>Contact</h2>

{% if success %}
<p>Thanks for your message!</p>

{% else %}

{% for message in form.name.errors %}
<div class="flash">{{ message }}</div>

{% endfor %}

{% for message in form.email.errors %}
<div class="flash">{{ message }}</div>
{% endfor %}

{% for message in form.subject.errors %}
<div class="flash">{{ message }}</div>
{% endfor %}

{% for message in form.message.errors %}
<div class="flash">{{ message }}</div>
{% endfor %}

<form id="contactform" action="{{ url_for('contact') }}" method=post>
{{ form.hidden_tag() }}

## "autofocus=true" is to reload at the form on unsuccessful submission. ##

{{ form.name.label }}
{{ form.name(autofocus=true, placeholder="Jimmy Hoffa")}}

{{ form.email.label }}
{{ form.email(placeholder="[email protected]") }}

{{ form.subject.label }}
{{ form.subject }}

{{ form.message.label }}
{{ form.message }}

{{ form.submit }}
</form>
{% endif %}
</section>
</section>

And here's my forms.py and main.py:

forms.py

from flask.ext.wtf import Form, TextField, TextAreaField, SubmitField, validators, ValidationError

class ContactForm(Form):
name = TextField("Name", [validators.Required("Please enter your name!")])
email = TextField("Email", [validators.Required("Please enter your email!"),       validators.Email("Enter a real email!")])
subject = TextField("Subject", [validators.Required("Please enter a subject!")])
message = TextAreaField("Message", [validators.Required("Please enter a message!")])
submit = SubmitField("Send")

main.py

@app.route('/', methods=['GET', 'POST'])
def contact():
form = ContactForm()

if request.method == 'POST':
    if form.validate() == False:
        flash('All fields are required.')
        return render_template('home.html', form=form)
    else:
        msg = Message(form.subject.data, recipients=['[email protected]'])
        msg.body = """
        From %s <%s>
        %s""" % (form.name.data, form.email.data, form.message.data)
        mail.send(msg)

        return render_template('home.html', success=True)

elif request.method == 'GET':
    return render_template('home.html', form=form)

if __name__=='__main__':
app.run(debug=True)

I want to return render_template('home.html#contact'), but I can't.

I know the solution has to be so simple, but I am not exactly a Flask (or jQuery) expert. Also, I would be happy with an alert message on successful submission like with messenger.js (which I also can't figure out), but I would really just like it to automatically reload at the contact section.

I also tried

{% if success %}
<script>$.load('/ #contact')</script>

but that didn't go so well either.


When you submit the form the browser will drop the current page and load a brand new one, which comes from your response to the POST request.

You have a few options to get the new page to scroll:

  1. Instead of responding to the POST event with a new page, you can respond with a redirect. In your example you would redirect to http://<hostname>/home.html#contact. This method may not work with all browsers, as some do not honor hashtags coming in redirects.

  2. Your idea of having jQuery do the scrolling as soon as the page loads is a good one, but your jQuery implementation is incorrect. See the answer to this question to learn how to scroll on load.

  3. The two methods above will cause the window to reload, so even though the new page will scroll to the right position the user will notice that the page reloads. An option to prevent the reload is to have jQuery submit the form in the background as an Ajax request, so that the browser does not reload the page. This question will give you some ideas.