I have a simple flask app that uses templates.
Every time I click somewhere on the navigation (in the base.html) it refreshes the entire page, I'd rather it just refresh inside the template because I have collapsable elements in the navbar that go back to being collapsed when the entire page is reloaded.
How do I just reload the new template I want to render and not the nav bar when I click on a link in the nav bar?
for reference here's some code:
base.html
<!DOCTYPE html>
<html lang="en">
<head>
...
</head>
<body>
<script>
// Hide submenus
$('#body-row .collapse').collapse('hide');
// Collapse/Expand icon
$('#collapse-icon').addClass('fa-angle-double-left');
// Collapse click
$('[data-toggle=sidebar-colapse]').click(function() {
SidebarCollapse();
});
function SidebarCollapse () {
$('.menu-collapsed').toggleClass('d-none');
$('.sidebar-submenu').toggleClass('d-none');
$('.submenu-icon').toggleClass('d-none');
$('#sidebar-container').toggleClass('sidebar-expanded sidebar-collapsed');
// Treating d-flex/d-none on separators with title
var SeparatorTitle = $('.sidebar-separator-title');
if ( SeparatorTitle.hasClass('d-flex') ) {
SeparatorTitle.removeClass('d-flex');
} else {
SeparatorTitle.addClass('d-flex');
}
// Collapse/Expand icon
$('#collapse-icon').toggleClass('fa-angle-double-left fa-angle-double-right');
}
</script>
<style>
</style>
{% include 'nav-mini.html' %}
<!-- Bootstrap row -->
<div class="row" id="body-row">
{% include 'nav-side.html' %}
<!-- MAIN -->
<div class="col py-3">
<article class=flashes>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message}}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
</article>
{% block content %}
{% endblock %}
</div>
<!-- Main Col END -->
</div>
<!-- body-row END -->
</body>
</html>
sidenav.html
<!-- Sidebar -->
<div id="sidebar-container" class="sidebar-expanded d-none d-md-block col-2">
<ul class="list-group sticky-top sticky-offset">
{% if sidenavs %}
{% for heading, stuff in sidenavs.items() %}
<li class="list-group-item sidebar-separator-title text-muted d-flex align-items-center menu-collapsed">
<a href="#page{{heading}}" data-toggle="collapse" class="dropdown-toggle">
<br />
{{ heading }}
</a>
</li>
<br />
<ul class="collapse list-unstyled" id="page{{heading}}">
{% for name, address in stuff.items() %}
<a href="{{ address }}" class="bg-dark list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-start align-items-center">
<span class="fa fa-tasks fa-fw mr-3"></span>
<span class="menu-collapsed">{{ name }}</span>
</div>
</a>
{% endfor %}
</ul>
{% endfor %}
{% endif %}
</ul>
<div class="footer">
<h3><center>WCF Insurance</center></h3>
</div>
</div>
<!-- sidebar-container END -->
App.py (flask app)
...
from flask import Flask, url_for, render_template, redirect, jsonify
...
app = Flask(__name__)
CWD = os.path.dirname(os.path.abspath(__file__))
...
#app.route('/bokeh-example')
def page_bokeh_example():
''' iframe for Bokeh Example '''
resp = {
'mininavs': get_mini_nav(),
'sidenavs': get_side_nav(),
'iframe_url': get_iframe_url('Bokeh Example'), }
return render_template('iframe.html', **resp)
....
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5020)
Notice I'm using the render_template() flask function.
I added a JavaScript function call to the onload property of the body tag with the following code in it
function setMenuState(){
if (window.sessionStorage.getItem("MenuState") == null){
window.sessionStorage.setItem("MenuState", "--isHidden");
}
if(window.sessionStorage.getItem("MenuState") != "--isHidden"){
toggleMenu();
window.sessionStorage.setItem("MenuState", "");
}
}
function toggleMenu(){
const textnames = ["","",""]
const sidebarE1 = document.getElementsByClassName("sidebar")[0];
const contentE1 = document.getElementsByClassName("content")[0];
let menuItems = document.getElementsByClassName("fa");
sidebarE1.classList.toggle("sidebar--isHidden");
contentE1.classList.toggle("content--isHidden");
for(item in menuItems){
if(menuItems[item].innerHTML === textnames[item]){
menuItems[item].innerHTML = "";
}else{
menuItems[item].innerHTML = textnames[item];
}
}
if(window.sessionStorage.getItem("MenuState") != "--isHidden"){
window.sessionStorage.setItem("MenuState", "--isHidden");
}else{
window.sessionStorage.setItem("MenuState", "");
}
}
<body onload="setMenuState()">
This still runs the animation of the menu if it's open when you click on a link, but it retains the open/closed state in session storage.
UPDATE:
I have solved(mostly) the animations issue where the menu animation would play on page load.
function setMenuState(){
const sidebarE1 = document.getElementsByClassName("sidebar")[0];
const contentE1 = document.getElementsByClassName("content")[0];
if (window.sessionStorage.getItem("MenuState") == null){
window.sessionStorage.setItem("MenuState", "--isHidden");
}
if(window.sessionStorage.getItem("MenuState") != "--isHidden"){
toggleMenu();
window.sessionStorage.setItem("MenuState", "");
}
setTimeout(()=>{
if(!sidebarE1.classList.contains("sidebar-animated")){
sidebarE1.classList.add("sidebar-animated");
contentE1.classList.add("content-animated");
}}, 250);
}
.content {
background-color: #000000;
padding: 2rem;
height: 100vh;
position: fixed;
width: 100%;
opacity: 1;
margin-left: 4rem;
color: #00ff00;
top: 8rem;
overflow: scroll;
padding-bottom: 32rem;
z-index: 1;
}
.content-animated {
transition: transform 500ms linear;
}
by moving the actual transition property into its own css class, and only adding the class ~250ms after page load, the position and state of the menu is set without animation, and the animation is set before first click.
Related
For my website, i would like to scrolldown automatically to an article when the user click on a button category.
Here is my twig code.
`
{% if category is not null and category|length > 0 %}
{% for categorie in category %}
<li class="nav-item category-link">
<button class="nav-link active" categoryId="{{categorie.id}}">{{categorie.name}}</button>
</li>
{% endfor %}
{% endif %}
</ul>
</div>
</div>
</nav>
<div class="">
{% for content in contents %}
<div contentId="{{content.id}}">
<h2 class="text-center">{{content.title}}</h2>
</div>`
And this is my JS part.
var contentId = document.querySelector('div').getAttribute('contentId');
var categoryId = document.querySelector("button").getAttribute('categoryId');
if(categoryId){
categoryId.addEventListener('click', function() {
if (contentId === categoryId){
window.scrollTo(contentId);
}
});
}```
For information, actually in my database contentId related to categoryId have the same value. But for future they may not have the same value.
Thanks a lot for your help.
How about a non-JS solution?
To do this with JS you would have to do a querySelectorAll on all of the buttons and add an event listener on each, then query the respective contentId div then scroll to it.
A non-JS solution can be like this:
{% if category is not null and category|length > 0 %}
{% for categorie in category %}
<li class="nav-item category-link">
{{categorie.name}}
</li>
{% endfor %}
{% endif %}
</ul>
</div>
</div>
</nav>
<div class="">
{% for content in contents %}
<div id="{{content.id}}" contentId="{{content.id}}">
<h2 class="text-center">{{content.title}}</h2>
</div>
const contents = document.querySelectorAll('div[contentId]')
const categories = document.querySelectorAll("button[categoryId]")
categories.forEach(category => {
category.addEventListener('click', function() {
const categoryId = category.getAttribute('categoryId');
const content = Array.from(contents).find(content => content.getAttribute('contentId') === categoryId)
if (content){
content.scrollIntoView({ behavior: "smooth" });
}
)}
});
results.html
{%if searched_user %}
{{searched_user}}
<button id=likedsongsbutton>View liked songs</button>
<div>
{% for each_searched_user in searched_user %}
<br id="likedsongs"/>{% for liked_songs in each_searched_user.liked_songs.all %}{{liked_songs}}
<br/>
{% endfor %}
{% endfor %}
{% endif %}
{% endblock %}
</div>
<script>
document.querySelector("#likedsongsbutton").addEventListener(onclick, )
</script>
views.py
def results(request):
if request.method == "GET":
search_query = request.GET.get("username")
searched_user = UserProfile.objects.filter(
user__username__contains=search_query
)
return render(
request, "results.html", {
"searched_user":searched_user
})
My question is how do I make the <div> show only when the button is clicked? I do not know what function to pass in the EventListener
If you give your change you div to have an id of myDiv and set the display to be None, you can pass the following lambda in as the second parameter.
() => {document.getElementById("myDiv").style.display = ""}
While the div looks like:
<div id="myDiv" style="display: none"></div>
I also think you should change it from onclick, to "click". Here is a complete example:
<button id=likedsongsbutton>View liked songs</button>
<div id="myDiv" style="display: none; background: blue; height: 100px; width: 100px;"></div>
<script>
document.querySelector("#likedsongsbutton").addEventListener("click", () => {
document.getElementById("myDiv").style.display = ""
});
</script>
I'm trying to implement infinite scroll with django and jquery(Waypoint).
I have a ListView with a pagination of 5, but when waypoint loads second page, AJAX requests are no longer performed so I added the AJAX function on the onAfterPageLoad which helps bring back AJAX function to the newly loaded page.
That's fine, but then it introduces a bug to my code making the page loaded initially (First Page) no longer perform AJAX functions very well. It makes AJAX on the first page run 3 times if I just loaded a third page and makes AJAX on the second page run twice and so on depending on the number of pages loaded already.
I don't know if there are any cool ways to achieve this because I tried using just jquery without waypoint, but still get same errors as I get when using just waypoint making it an error. This is kinda tricky so far.
{% extends "base.html" %}
{% block content %}
{% load static %}
<div class="container" style="max-width:700px">
<div class="px-3 pt-md-5 pb-md-4 mx-auto text-center">
<h1 class="display-4">All Groups</h1>
<p class="lead">List of groups</p>
</div>
<div class="">
<div class="row infinite-container">
{% for group in groups %}
<div class="col-md-12 infinite-item">
<!-- <img class="img-fluid" src="https://picsum.photos/700"> -->
<a class="text-dark" href="{{ group.get_absolute_url }}">
<div class="card mb-4 box-shadow">
<div class="card-body">
<h2 style="font-size:18px;font-weight:bold;min-height:42px;">
{{group.name|truncatechars:50}}</h2>
<div class="d-flex justify-content-between align-items-center">
<small class="text-muted">{{group.owner}}</small>
<small class="text-muted">{{group.date_created}}</small>
</div>
<p><a class='follow-btn' data-href='{{ group.get_group_follow_api_url }}'
data-likes='{{ page.likes.count }}'
href='{{ group.get_group_follow_url }}'>{{ group.follows.count }}
{% if request.user.profile in group.follows.all %}
Unfollow
{% else %}
Follow
{% endif %}
</a></p>
</div>
</div>
</a>
</div>
{% endfor %}
</div>
<!-- Comment for loading spinner -->
{% comment %}
<div class="d-flex d-none position-fixed" style="top:35vh;left:46vw">
<button class="btn btn-primary loading">
<span class="spinner-border spinner-border-sm"></span>
Loading...
</button>
</div>
{% endcomment %}
<!-- End of comment for loading spinner -->
<div class="row">
<div class="col-12">
{% if page_obj.has_next %}
<a class="infinite-more-link" href="?page={{ page_obj.next_page_number }}"></a>
{% endif %}
</span>
</div>
</div>
</div>
</div>
<script src="{% static "js/jquery.waypoints.min.js" %}"></script>
<script src="/static/js/infinite.min.js"></script>
<script>
var infinite = new Waypoint.Infinite({
element: $('.infinite-container')[0],
offset: 'bottom-in-view',
onBeforePageLoad: function () {
$('.loading').show();
},
onAfterPageLoad: function () {
$('.loading').hide();
$(document).ready(function(){
function updateText(btn, newCount, verb){
btn.text(newCount + " " + verb)
}
$(".follow-btn").click(function(e){
e.preventDefault()
var this_ = $(this)
var badge = this_.find('.unlike-update')
var followUrl = this_.attr("data-href")
var followCount = parseInt(this_.attr("data-follows")) | 0
var addFollow = followCount + 1
var removeFollow = followCount - 1
if (followUrl){
$.ajax({
url: followUrl,
method: "GET",
data: {},
success: function(data){
console.log(data)
var newFollows;
if (data.followed){
// updateText(this_, addLike, "Unlike")
// updateText(this_, data.likescount, badge)
updateText(this_, data.followscount, "Unfollow")
} else {
// updateText(this_, removeLike, "Like")
updateText(this_, data.followscount, "Follow")
// remove one like
}
}, error: function(error){
console.log(error)
console.log("error")
}
})
}
})
})
}})
</script>
{% include 'group/snippets/group_follow.html' %}
{% endblock %}
class GroupListView(LoginRequiredMixin, ListView):
model = Group
paginate_by = 5
context_object_name = 'groups'
template_name = 'group/group_list.html'
ordering = ['-date_created']
(I know, bad title, pardon me)
So here's the thing:
I have a navbar with 3 icons and each one opens the same modal, but with different resources inside.
They are only clickable if resources exist for them, otherwise they are grayed out. Therefore 0, 1, 2 or all 3 of them can be visible or grayed out.
It works fine when all 3 are clickable, but if 1 or 2 are grayed out, the other opens an empty modal and the console shows assistance.js:29 Uncaught TypeError: Cannot read property 'style' of null.
This is how the twig looks and how the modal is populated and the modal itself:
<div class="assistance-body">
<nav>
<ul class="nav nav-tabs nav-justified nav-no-padding-top">
{% set allResources = ['videos', 'glossary', 'links'] %}
{% for key in allResources %}
{% if key in resources|keys %}
<li{% if loop.first %} ng-class="active" {% verbatim %}{{class}}{% endverbatim %} {% endif %} class="icon-resource">
<a
data-toggle=""
href="#assistance-{{ key }}"
segment-event="Modules: Tutor: Clicked {{ key|capitalize }} Section"
segment-not-track-if-class="active"
onclick="toggleAssistance('assistance-{{ key }}', ['assistance-videos', 'assistance-glossary', 'assistance-links'] )"
>
<i class="icon-{{ key }} icon-sm"></i>
<span class="sr-only">{{ ('resources.tabs.' ~ key ~ '.title') | trans }}</span>
</a>
</li>
{% else %}
<li{% if loop.first %} {% verbatim %}{{class}}{% endverbatim %} {% endif %} class="icon-resource">
<i class="icon-{{ key }} icon-sm icon-sm-gray"></i>
<span class="sr-only">{{ ('resources.tabs.' ~ key ~ '.title') | trans }}</span>
</li>
{% endif %}
{% endfor %}
</ul>
</nav>
<div id="assistance" class="assistance">
<div>
{% for key, data in resources %}
<div id="assistance-{{ key }}">
<button
type="button"
class="close"
onclick="toggleAssistance('assistance-{{ key }}', ['assistance-videos', 'assistance-glossary', 'assistance-links'] )"
aria-hidden="true">
×
</button>
{% include data.view with { 'category': data.category, 'resources': data.resources } %}
</div>
{% endfor %}
</div>
</div>
</div>
And this is the assistance.js:
function toggleAssistance (target, all) {
var targetAssistanceResource = document.getElementById(target);
var allAssistanceResources = [];
all.forEach(function (el) {
allAssistanceResources.push(document.getElementById(el));
});
// the resource is already opened, let's close it
if (getStyle(targetAssistanceResource, 'display') !== 'none') {
// hiding the main assistance div
document.getElementById('assistance').style.display = 'none';
// hiding all assistance resources
allAssistanceResources.forEach(function (res) {
res.style.display = 'none';
});
} else { // it's not opened, let's open it
// showing the main assistance div
document.getElementById('assistance').style.display = 'block';
// hiding all assistance resources
allAssistanceResources.forEach(function (res) {
res.style.display = 'none';
});
// showing the target assistance resource
targetAssistanceResource.style.display = 'block';
}
}
function getStyle (el, cssprop) {
if (el.currentStyle) { // IE
return el.currentStyle[cssprop];
} else if (window.getComputedStyle) {
return window.getComputedStyle(el)[cssprop];
}
// finally try and get inline style
return el.style[cssprop];
}
I've come across a strange bug in my Django application that has baffled me for the last few days.
Basically one of my dynamic templates stops rendering on the staging server. Everything works fine locally and when push to my staging server all seems fine however, after clicking around a few pages and then navigating back to one of the pages with this template it has suddenly disappeared. If i restart the supervisor processes the template reappears but then disappears again after navigating to other pages.
The weird thing is it's not consistent when it disappears. Sometimes it takes a few times navigating to other pages to disappear.
The template itself has dynamically generated content however even the container divs are not appearing so this leads me to believe that this is not a context error.
Another point about it is that if i stop my supervisor processes and run ./manage.py runserver instead, the problem doesn't seem to happen.
The things i've tried:
I've used django-template-repl to load the url and view the context
the template receives, all the data i expect to be rendered appears to be there.
Any help would be much appreciated, any tips on where to look next.
the view thats rendered:
def list(request):
projects = Project.objects.active()
order = request.GET.get('order', 'popular')
projects = projects.ordered_by(order)
search_q = request.GET.get('search_q')
if search_q:
project_query_terms = ['title', 'short_description', 'who_description']
username_query_terms = [
'user__userprofile__first_name',
'user__userprofile__last_name']
project_query = create_query(search_q, project_query_terms)
username_query = create_query(search_q, username_query_terms) & Q(
name_is_visible__exact=True)
projects = projects.filter(project_query | username_query)
if request.GET.get('sector'):
projects = projects.filter(sector__id=request.GET['sector'])
if request.GET.get('location'):
projects = projects.filter(
location=request.GET['location'],
location_public=True)
form = ProjectFilterForm
form = (form(request.GET, projects=projects)
if request.GET else form(projects=projects))
paginator = Paginator(projects, 9)
page = request.GET.get('page')
try:
projects = paginator.page(page)
except PageNotAnInteger:
projects = paginator.page(1)
except EmptyPage:
projects = paginator.page(paginator.num_pages)
amounts_by_project = Need.objects.amounts_by_project()
amounts_raised_by_project = MoneyDonation.objects.amounts_raised_by_project()
context = {
'body_id': 'project-list',
'projects': projects,
'form': form,
'order': order,
'amounts_by_project': amounts_by_project,
'amounts_raised_by_project': amounts_raised_by_project,
}
template = "_projects-content" if request.is_ajax() else "projects"
template = "projects/" + template + ".html"
return render(request, template, context)
the projects-projects.html template
{% extends 'projects/__base.html' %}
{% load image_tags staticfiles projects %}
{% block content %}
{% include 'projects/_projects-content.html' %}
{% endblock %}
{% block scripts %}
<script>
FCC.drawDonuts('.col-project');
jQuery(function updateWithFilters($){
function queryWithParams() {
var getParams = '';
$('#project-filter-form select').each(function(i) {
getParams += (i === 0) ? '?' : '&';
getParams += this.name + '=' + $(this).children('option:selected')[0].value;
});
$.get({% url 'projects' %} + getParams, function(res){
$("#projects-content").html(res);
updateWithFilters($);
FCC.drawDonuts('.col-project');
});
}
$('#project-filter-form select').change(function(e){
queryWithParams();
});
$('#reset-filters').click(function(e) {
e.preventDefault();
$('#project-filter-form select option:selected').each(function(){
$(this).prop('selected', false);
queryWithParams();
});
});
});
</script>
{% endblock %}
the _projects-content.html
<div id='projects-content'>
{% include 'projects/_projects-filters.html' %}
{% include 'projects/_projects-projects.html' %}
</div>
the _projects-projects.html
<section class="section hp-projects">
<div class="container">
<div class="row">
<div class="col-xs-12 sub-header">
<sub-header>
<h2>Browse all projects</h2>
</sub-header>
</div>
{% if not projects %}
<p>No projects match your search.</p>
{% endif %}
{% for project in projects %}
<div class="col-xs-12 col-sm-6 col-md-4">
{% include '_project-thumbnail.html' %}
</div><!--col-->
{% endfor %}
</div><!--row-->
<div class="row">
<div class="col-xs-12">
{% with projects as collection %}
{% include 'pagination.html' %}
{% endwith %}
</div>
</div>
</div><!--container-->
</section>
_project-thumbnail.html
{% load total staticfiles projects %}
<div class="project-container">
<header>
<a href="{% url 'project_detail' project.id %}" class="project">
<h5 class="project-title">{{ project.title }}</h5>
</a>
</header>
<figure>
<div class="img-wrap">
<a href="{% url 'project_detail' project.id %}" class="project">
<img class="img-responsive" src="{{ MEDIA_URL }}{{ project.profile_image }}" alt="project image" />
</a>
</div>
<figcaption>{{ project.short_description }}</figcaption>
</figure>
<div class="row three-donuts">
<div class="col-xs-6">
{% include "projects/_project-donate-thumbnail.html" %}
</div>
<div class="col-xs-6">
{% include "projects/_project-volunteer-thumbnail.html" %}
</div>
</div>
</div><!--box-container-->
_project-donate-thumbnail.html is the template that is not rendering after navigating to another page
here's the content of the file but as i said nothing inside this renders once you navigate away from this page after a fresh server restart.
however the _project-volunteer-thumbnail stays completely fine. It is just the donate-thumnail that disappears.
_project-donate-thumbnail.html
<div id="project-{{ project.id }}-donate" class="col-project this-project-donate">
<span class="percentage">
{% if project.donate_aim %}
{{ project.donate_percent }}%
{% endif %}
</span>
</div>
<div class="equation">
£{{ project.donate_status }}
<span class="desired-amount">/ £{{ project.donate_aim }}</span>
</div>
<div class="button-link-wrapper">
{% if project.donate_aim %}
donate
{% else %}
<span class="button graph-button donate deactivated">donate</span>
{% endif %}
</div>
again any tips on where to look next would be helpful