'flask just refresh templated portion of page

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 -->

visual

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.



Solution 1:[1]

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.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1