It is a fairly common feature in web applications to have links that open a popover when hovered. Twitter does this, Facebook does it, LinkedIn does it. Popovers are a great way to provide additional information to users.
- User Registration and authentication
- Profile popovers
- Flask framework
- Python for programming
- Flask-Bootstrap
- Flask-WTF
- Flask-SQLAlchemy
- Flask-Login
- Flask-Migrate
- Flask-Moment
- Email validator
- Python-dotenv
- Ajax requests
You can use the following credentials to test the deployed application:
- Username: harry
- Password: 12345678
Alternatively, you can create your own user by clicking the Register link. You will be redirected to the Login page automatically where you can authenticate that user.
-
Clone this repository:
$ git clone git@github.com:GitauHarrison/flask-popovers.git
-
Change into the directory:
$ cd flask-popovers
-
Create and activate a virtual environment:
$ virtualenv venv $ source venv/bin/activate # Alternatively, you can use virtualenvwrapper $ mkvirtualenv venv
- Virtualenvwrapper is a wrapper around virtualenv that makes it easier to use virtualenvs.
mkvirtualenv
not only creates but also activates a virtual enviroment for you. Learn more about virtualenvwrapper here.
- Virtualenvwrapper is a wrapper around virtualenv that makes it easier to use virtualenvs.
-
Install dependencies:
(venv)$ pip install -r requirements.txt
-
Add environment variables as seen in the
.env-template
:(venv)$ cp .env-template .env
- You can get a random value for your
SECRET_KEY
by runningpython -c "import os; print os.urandom(24)"
in your terminal.
- You can get a random value for your
-
Run the application:
(venv)$ flask run
- Open the application in your favourte browser by copying and pasting the link below:
- Feel free to create a new user and see the popovers in action. You can do so by registering a user then logging in.
To create a popover on a link, you first need to identify what link exactly you want to have a popover. You can do this by adding the class
selector on an <a>
element. For example, if you want to add a popover to the link /user/<username>
, you would add the following to the <a>
element:
<span class="user_popup">
<a href="{{ url_for('user', username=post.author.username) }}">
{{ post.author.username }}
</a>
</span>
In the example above, I have modified how I select the link I want to have a popover. This is deliberate. Typically, I would have done:
<a class="user_popup" href="{{ url_for('user', username=post.author.username) }}">
{{ post.author.username }}
</a>
But this has the ugly effect where the popover will acquire the behaviour of the <a>
parent element. This is not desirable. I will end up with something that looks like this:
<a href="" class="user_popup">
<a href="{{ url_for('user', username=post.author.username) }}">
{{ post.author.username }}
<div> <!-- popover element goes here --> </div>
</a>
</a>
Typically, making the popover a child of the hovered element works perfectly for buttons and generally <div>
and <span>
.
Using JQuery, a hover event can be attached to any HTML element by calling the element.hover(handlerIn, handlerOut)
method. JQuery can also conviniently attach the events if the functions are called on a collection of elements.
$('.user_popup').hover(
function(){
// Mouse in event handler
var elem = $(event.currentTarget);
},
function(){
// Mouse out event handler
var elem = $(event.currentTarget);
}
)
When using JQuery, $.ajax()
function is used to send an asynchronous request to the server. An example of a request can be /user/<username>/popup
. This request contains HTML that will be inserted into the popover.
$(function() {
var timer = null;
var xhr = null;
$('.user_popup').hover(
function(event) {
// mouse in event handler
var elem = $(event.currentTarget);
timer = setTimeout(function() {
timer = null;
xhr = $.ajax(
'/user/' + elem.first().text().trim() + '/popup').done(
function(data) {
xhr = null
// create and display popup here
}
);
}, 500);
},
function(event) {
// mouse out event handler
var elem = $(event.currentTarget);
if (timer) {
clearTimeout(timer);
timer = null;
}
else if (xhr) {
xhr.abort();
xhr = null;
}
else {
// destroy popup here
}
}
)
});
The $.ajax()
call returns a promise, which essentially is a special JS object that represents asynchronous operation.
data
argument passed by the $.ajax()
call is the HTML that will be inserted into the popover.
function(data) {
xhr = null;
elem.popover({
trigger: 'manual',
html: true,
animation: false,
container: elem,
content: data
}).popover('show');
flask_moment_render_all();
}
The return of the popover()
call is the newly created popover component. flask_moment_render_all()
function is used to display the last time a user was active.
If the user hovers away from the popover, the popover will be aborted. This is done by calling the .popover('destroy')
method.
function(event) {
// mouse out event handler
var elem = $(event.currentTarget);
if (timer) {
clearTimeout(timer);
timer = null;
}
else if (xhr) {
xhr.abort();
xhr = null;
}
else {
elem.popover('destroy');
}
}
- If you would like to see how the application above has been built from scratch, you can look at this flask popover tutorial.
- If you prefer to only see how this feature can be implemented, look at this flask popover section
If you would like to add private messaging and user notifications to your flask application, you can look at the user notifications in flask repository to learn more.