I just made a carousel using splide and I just made it in the way I want. There is only one detail left and it doesn't work. I don't want to show the main slider at the beginning and begin to show it only when the user click on any thumbnail. I implemented it this way:
#main-slider{
display:none;
}
and then
thumbnails.on('click', function(){
document.getElementById('main-slider').style.display = "block";
})
But the main slider only get partially visible. That is, only pagination and arrows will display. No way to show everything, especially the image.
Here's the entire code:
{% load static %}
{% comment %} If you want to fully customize the slider appearance, pick the splide-core.min.css file that does not include arrows, pagination and progress bar styles, which reduces unnecessary "override" works. {% endcomment %}
<link href="{% static 'js/splide-3.2.1/dist/css/splide.min.css' %}" rel="stylesheet">
<style>
.splide__slide {
/* opacity of the thumbnails */
opacity: .6;
}
.splide__slide.is-active {
/* opacity of the selected thumbnail */
opacity: 1;
}
#main-slider{
display:none;
}
</style>
<div class="splide">
<div id="main-slider" class="splide">
<div class="splide__track">
<ul class="splide__list">
{% for image in images %}
<li class="splide__slide">
<img src="{{ image.image.url }}" />
</li>
{% endfor %}
</ul>
</div>
</div>
<div id="thumbnail-slider" class="splide">
<div class="splide__track">
<ul class="splide__list">
{% for image in images %}
<li class="splide__slide">
{% comment %} data-splide-lazy must be enabled for lazy load {% endcomment %}
<img src="{{ image.image.url }}" data-splide-lazy="{{ image.image.url }}">
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
<script>
document.addEventListener( 'DOMContentLoaded', function () {
var main = new Splide( '#main-slider', {
type : 'slide',
heightRatio: 0.5,
speed :500,
pagination : true,
arrows : true,
cover : true,
} );
var thumbnails = new Splide( '#thumbnail-slider', {
fixedWidth : 100,
fixedHeight : 60,
gap : 10,
perMove : 1,
perPage : 1,
rewind : true,
pagination : false,
cover : true,
isNavigation: true,
lazyLoad : false,
keyboard : true,
wheel : true,
focus : 'center',
dragMinThreshold: {
mouse: 4,
touch: 10,
},
breakpoints : {
600: {
fixedWidth : 60,
fixedHeight: 44,
},
},
} );
main.sync( thumbnails );
main.mount();
thumbnails.mount();
thumbnails.on('click', function(){
document.getElementById('main-slider').style.display = "block";
})
} );
</script>
<script src="{% static 'js/splide-3.2.1/dist/js/splide.min.js' %}"></script>
Related
I created a project in Django. I need charts in my project.
I use chart.js library and Json.
I use an API for take values. In the API page there are 5 objects.Every object has title, value1 and value2.
When I create a table it works. I can get values but I cannot display values in charts. How can I create several charts with a loop?
views.py
def Jdeneme(request):
response = requests.get('https://api....t')
data = response.json()
return render(request, 'charts.html', {'data': data})
charts.html
{% extends "layouts/base.html" %}
{% load static %}
{% block content %}
<div class="content">
<div class="page-inner">
<h4 class="page-title">Chart.js</h4>
<div class="page-category"></div>
{% for dt in data %}
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<div class="card-title">{{ dt.title }} Doughnut Chart</div>
</div>
<div class="card-body">
<div class="chart-container">
<canvas id="doughnutChart" style="width: 50%; height: 50%"></canvas>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock content %}
{% block javascripts %}
<script>
doughnutChart = document.getElementById('doughnutChart').getContext('2d');
var myDoughnutChart = new Chart(doughnutChart, {
type: 'doughnut',
data: {
datasets: [
{
data: [ {{ dt.value1 }}, {{ dt.value2 }} ],
backgroundColor: ['#e95bda','#4bbffd']
}
],
labels: [
'value 1',
'value 2'
]
},
options: {
responsive: true,
maintainAspectRatio: false,
legend : {
position: 'bottom'
},
layout: {
padding: {
left: 20,
right: 20,
top: 20,
bottom: 20
}
}
}
});
// Chart with HTML Legends
var gradientStroke = htmlLegendsChart.createLinearGradient(500, 0, 100, 0);
gradientStroke.addColorStop(0, '#177dff');
gradientStroke.addColorStop(...
var myHtmlLegendsChart = new Chart(htmlLegendsChart, {
...
var myLegendContainer = document.getElementById("myChartLegend");
// generate HTML legend
myLegendContainer.innerHTML = myHtmlLegendsChart.generateLegend();
...
}
</script>
{% endblock javascripts %}
You are referencing the (template) for-loop variable dt outside of the for loop.
You are also giving all the created canvases the same fixed id.
You are only building one Chart in your javascript code.
Try changing your code like this:
{% extends "layouts/base.html" %}
{% load static %}
{% block content %}
<div class="content">
<div class="page-inner">
<h4 class="page-title">Chart.js</h4>
<div class="page-category"></div>
{% for dt in data %}
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<div class="card-title">{{ dt.title }} Doughnut Chart</div>
</div>
<div class="card-body">
<div class="chart-container">
<canvas id="doughnutChart{{ forloop.counter }}" style="width: 50%; height: 50%"></canvas> {# CHANGE THIS #}
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock content %}
{% block javascripts %}
<script>
{% for dt in data %} {# ADD THIS #}
doughnutChart = document.getElementById('doughnutChart{{ forloop.counter }}').getContext('2d'); {# CHANGE THIS #}
var myDoughnutChart{{ forloop.counter }} = new Chart(doughnutChart, { {# CHANGE THIS #}
type: 'doughnut',
data: {
datasets: [
{
data: [ {{ dt.value1 }}, {{ dt.value2 }} ],
backgroundColor: ['#e95bda','#4bbffd']
}
],
labels: [
'value 1',
'value 2'
]
},
options: {
responsive: true,
maintainAspectRatio: false,
legend : {
position: 'bottom'
},
layout: {
padding: {
left: 20,
right: 20,
top: 20,
bottom: 20
}
}
}
});
{% endfor %} {# ADD THIS #}
// Chart with HTML Legends
var gradientStroke = htmlLegendsChart.createLinearGradient(500, 0, 100, 0);
gradientStroke.addColorStop(0, '#177dff');
gradientStroke.addColorStop(...
var myHtmlLegendsChart = new Chart(htmlLegendsChart, {
...
var myLegendContainer = document.getElementById("myChartLegend");
// generate HTML legend
myLegendContainer.innerHTML = myHtmlLegendsChart.generateLegend();
...
}
</script>
{% endblock javascripts %}
By the way, this is just to make your code work the way you are writing it, but it is debatable whether it's a good idea to dynamically generate Javascript code via Django templates.
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.
I've got a paginated page on shopify that I'm trying to get a infinite scroll working using javascript/ ajax.
The liquid looks something like this:
{% paginate collection.products by 20 %}
<!-- START PRODUCTS -->
{% for product in collection.products %}
<!-- START PRODUCT {{ forloop.index | plus:paginate.current_offset }} -->
<div class="product" id="product-{{ forloop.index | plus:paginate.current_offset }}">
{% include 'product' with product %}
</div>
<!-- END PRODUCT {{ forloop.index | plus:paginate.current_offset }} -->
{% endfor %}
{% if paginate.next %}
<div id="more"><p>↓ More</p></div>
{% endif %}
<div id="product-list-foot"></div>
<!-- END PRODUCTS -->
<!-- the bottom of your collections.liquid -->
{% endpaginate %}
And the JS:
<script>
function ScrollExecute() {
if($(document).height() - 100 < ($(document).scrollTop() + $(window).height())) {
scrollNode = $('#more').last();
scrollURL = $('#more p a').last().attr("href");
if(scrollNode.length > 0 && scrollNode.css('display') != 'none') {
$.ajax({
type: 'GET',
url: scrollURL,
beforeSend: function() {
scrollNode.clone().empty().insertAfter(scrollNode).append('<img class="loading_gif" src=\"{{ "ajax-loader.gif" | asset_url }}\" />');
scrollNode.hide();
},
success: function(data) {
// remove loading feedback
scrollNode.next().remove();
var filteredData = $(data).find(".product");
filteredData.insertBefore( $("#product-list-foot") );
},
dataType: "html"
});
}
}
}
$(document).ready(function () {
$(window).scroll(function(){
$.doTimeout( 'scroll', 100, ScrollExecute);
});
});
</script>
This works great but with one issue. The first paginated page works fine, but if there's 3 pages in total for example it doesn't load the third page. So the load function only works once.
Any ideas why this only works once?
I am working on a Django project where I want to output a text file and apply certain operations like highlighting to the text. My idea was to implement it in such a way that each word can be clicked, for a popup window (or tooltip window) to appear, displaying all the options.
{% for Line in File.body %}
<div>
{% for Token in Line %}
{% if not Token.content == None %}
<span class="inline token" id="token">
<strong style="color: {{ Token.highlight }};">
{{ Token.content }}
</strong>
</span>
<div id="{{ Token.unique_id }}">
POPUP </br>
<button onclick="changeColor()">Highlight</button>
</div>
<script>
tippy('.token', {
content: document.getElementById("{{ Token.unique_id }}"),
delay: 100,
arrow: true,
arrowType: 'round',
size: 'large',
duration: 500,
animation: 'scale',
allowHTML: true,
hideOnClick: true,
trigger: "click"
})
function changeColor() {
var token = document.getElementById("token");
token.style.color="blue;";
}
</script>
{% endif %}
{% if Token.endline %}
</br>
{% endif %}
{% endfor %}
</div>
{% endfor %}
I tried using the tippjs tooltip library (https://atomiks.github.io/tippyjs/), but clicking the highlight button doesn't do anything. If i don't put the javascript part in the loop, nothing happens at all. The code with the javascript part not in the loop would look something like this:
<script>
tippy('.token', {
content: document.getElementById("{{ Token.unique_id }}"),
delay: 100,
arrow: true,
arrowType: 'round',
size: 'large',
duration: 500,
animation: 'scale',
allowHTML: true,
hideOnClick: true,
trigger: "click"
})
function changeColor() {
var token = document.getElementById("token");
token.style.color="blue;";
}
</script>
{% for Line in File.body %}
<div>
{% for Token in Line %}
{% if not Token.content == None %}
<span class="inline token" id="token">
<strong style="color: {{ Token.highlight }};">
{{ Token.content }}
</strong>
</span>
<div id="{{ Token.unique_id }}">
POPUP </br>
<button onclick="changeColor()">Highlight</button>
</div>
{% endif %}
{% if Token.endline %}
</br>
{% endif %}
{% endfor %}
</div>
{% endfor %}
I am new to django and even newer to javascript, so I'm not sure if this is even the right way to go about this.
Can somebody point me in the right direction here?
I am using django 1.10.5, booststrap 4.0 and LeafletJs 1.0.3 with routing machine plugin and geocoder.
Now I have the following problem, the collapse button of the control pannel for routing machine is not loading (its not showing up in the html code) when the map is bigger than 640 x 640px when the page is loaded the first time.
there are no problems when I make the map bigger with chrome dev tools after the page is fully loaded with the map size set in css to 640x640px or smaller.
I have a version of that works the way it should, but this is without django and bootstrap and I need it to work with both.
css code
.map-add-size{
position: relative;
width: 500px;
height:500px;
}
.html
{% extends 'base.html' %}
{% load static %}
{% load crispy_forms_tags %}
{% block css %}
<link href="{% static 'routes/css/add.css' %}" rel="stylesheet">
{{block.super}}
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.0.3/dist/leaflet.css">
<link href="{% static 'routes/css/leaflet-routing-machine.css' %}" rel="stylesheet" >
<link href="{% static 'routes/css/Control.Geocoder.css' %}" rel="stylesheet">
{% endblock %}
{% block content %}
<h3>Add a new route</h3>
<div class="row">
<div class="route-edit">
<div class="col-xs-6">
<p>Route page information</p>
<form id="mainForm" method="post" class="form">
{% csrf_token %}
{{ routeAddForm|crispy }}
<div class="row route-edit">
<button type="submit" name="save_route" class="btn btn-primary">Save and continue</button>
</div>
</form>
</div>
</div>
</div>
<div class ="row">
<div class="cols-xs-8 map-container-div">
<p>Add start and end markers by left clicking on the map. <br>
Add markers by clicking on the lines inbetween the start and end markers<br>
Remove markers by clicking on the cross next to the address of the marker.
</p>
</div>
</div>
<div id="map-add" class="map-add-size"></div>
{% endblock %}
{%block javascript %}
{{block.super}}
<script src="{% static 'routes/js/cookie.js' %}"></script>
<script>
var csrftoken = Cookies.get('csrftoken');
</script>
<script src="https://unpkg.com/leaflet#1.0.3/dist/leaflet.js"></script>
<script src="{% static 'routes/js/leaflet-routing-machine.js' %}"></script>
<script src="{% static 'routes/js/Control.Geocoder.js' %}"></script>
<script src="{% static 'routes/js/map-add.js' %}"></script>
{%endblock%}
javascript
window.lrmConfig = {
// serviceUrl: 'https://api.mapbox.com/directions/v5',
// profile: 'mapbox/driving',
};
var map = L.map('map-add').setView([51.505, -0.09], 3);
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}{r}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
var geocoder = L.Control.Geocoder.mapzen('search-DopSHJw'),
control2 = L.Control.geocoder({
geocoder: geocoder,
defaultMarkGeocode: false
}).on('markgeocode', function(e){
var bbox = e.geocode.bbox;
var poly = L.polygon([
bbox.getSouthEast(),
bbox.getNorthEast(),
bbox.getNorthWest(),
bbox.getSouthWest()
]).addTo(map);
map.fitBounds(poly.getBounds());
}).addTo(map);
control = L.Routing.control(L.extend(window.lrmConfig, {
geocoder: L.Control.Geocoder.nominatim(),
routeWhileDragging: true,
reverseWaypoints: true,
showAlternatives: true,
altLineOptions: {
styles: [
{color: 'black', opacity: 0.15, weight: 9},
{color: 'white', opacity: 0.8, weight: 6},
{color: 'blue', opacity: 0.5, weight: 2}
]
},
})).addTo(map);
L.Routing.errorControl(control).addTo(map);
function createButton(label, container) {
var btn = L.DomUtil.create('button', '', container);
btn.setAttribute('type', 'button');
btn.innerHTML = label;
return btn;
}
map.on('click', function(e) {
var container = L.DomUtil.create('div'),
startBtn = createButton('Start from this location', container),
destBtn = createButton('Go to this location', container);
var removeContainer = L.DomUtil.create('div'),
removeBtn = createButton('Remove waypoint',removeContainer);
L.popup()
.setContent(container)
.setLatLng(e.latlng)
.openOn(map);
L.DomEvent.on(startBtn, 'click', function() {
control.spliceWaypoints(0, 1, e.latlng);
map.closePopup();
});
L.DomEvent.on(destBtn, 'click', function() {
control.spliceWaypoints(control.getWaypoints().length - 1, 1, e.latlng);
map.closePopup();
});
});
// Submit post on submit
$('#mainForm').on('submit', function(event){
event.preventDefault();
console.log("form submitted!"); // sanity check
postData();
});
var successText;
function postData(){
console.log("postData is working!");// sanity check
var formData = $("#mainForm").serializeArray();
var routeArray =Array(),
routeArray = control.getWaypoints();
var json_obj = JSON.stringify(routeArray);
formData.push({name:'json_data',value:json_obj});
console.log("form data that is send")
console.log(formData);
$.post({
type: 'POST',
url: '/routes/add/',
data: formData,
});
}
I found a way to make it work for sizes bigger than 640px. I am unsure if this is the correct way to fix it, but it works for now.
in leaflet-routing-machine.js changed 640 to 1200 in this line and now the button works for maps that are bigger than 640px.
collapsible = collapsible || (collapsible === undefined && map.getSize().x <= 1200);