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?
Related
I'm trying to create a way to export the HTML table to xlsx, but I have a large amount of data in the queries. So I need to use pagination with Paginator so the browser doesn't load the data all at once and end up causing TimeOut. But when applying the Paginator and exporting, it only exports what is on the current page. Any suggestions to improve this code, such as creating a loop so that it can export all pages?
View function:
def export_project_data(request, pk):
if str(request.user) != 'AnonymousUser': # só vai ter acesso se for diferente de AnonymousUser
individuals_list = Individual.objects.filter(Project=pk)
traps = Sample_unit_trap.objects.filter(Project=pk)
page = request.GET.get('page', 1)
paginator = Paginator(individuals_list, 1000)
try:
individuals = paginator.page(page)
except PageNotAnInteger:
individuals = paginator.page(1)
except EmptyPage:
individuals = paginator.page(paginator.num_pages)
path_info = request.META['PATH_INFO']
context = {
'individuals': individuals,
'pk': pk,
'traps': traps,
'header': 'export_project_data',
'path_info': path_info
}
return render(request, 'inv/index.html', context)
HTML paginator code:
<div class="table-div">
{% if individuals.has_other_pages %}
<ul class="pagination pagination-sm">
{% if individuals.has_previous %}
<li>«</li>
{% else %}
<li class="disabled"><span>«</span></li>
{% endif %}
{% for i in individuals.paginator.page_range %}
{% if individuals.number == i %}
<li class="active"><span>{{ i }} <span class="sr-only">(current)</span></span></li>
{% else %}
<li>{{ i }}</li>
{% endif %}
{% endfor %}
{% if individuals.has_next %}
<li>»</li>
{% else %}
<li class="disabled"><span>»</span></li>
{% endif %}
</ul>
{% endif %}
</div>
{% endif %}
Javascript html to xlsx code:
<script>
function html_table_to_excel(type)
{
var data = document.getElementById('data');
var file = XLSX.utils.table_to_book(data, {sheet: "Dados_Ecológicos"});
XLSX.write(file, { bookType: type, bookSST: true, type: 'base64' });
XLSX.writeFile(file, 'file.' + type);
}
const export_button = document.getElementById('export_button');
export_button.addEventListener('click', () => {
html_table_to_excel('xlsx');
});
</script>
In my web site I want to show the user ratings - for that I used the infinite scroll but I am facing one problem.
When it first loads the data before calling the <a class="infinite-more-link" href="?page={{ ratings.next_page_number }}"></a> it is showing the star with the count of vote,but when after calling the <a class="infinite-more-link" href="?page={{ ratings.next_page_number }}"></a> it is not showing the star.
my views.py
#login_required
def ratings_user(request,pk):
ratings = VoteUser.objects.filter(the_user_id=pk).order_by('-pk')
paginator = Paginator(ratings, 1)
page = request.GET.get('page')
try:
posts = paginator.page(page)
except PageNotAnInteger:
posts = paginator.page(1)
except EmptyPage:
posts = paginator.page(paginator.num_pages)
return render(request,request.session['is_mobile']+'profile/ratings.html',{'ratings':posts})
html
{% extends 'mobile/profile/base.html' %}
{% block title %}
Ratings
{% endblock %}
{% block leftcontent %}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css" rel="stylesheet">
{% endblock %}
{% block middlecontent %}
<div class="infinite-container">
{% for i in ratings %}
<div class="infinite-item">
<div class="w3-container w3-card w3-white w3-round w3-margin">
<img src="{{ i.the_user.profile.avatar.url }}" alt="Avatar" class="w3-left w3-circle w3-margin-right" style="width:40px;height:40px;border-radius:50%;">
{% with user=i.the_user.profile %}{{ user.prenom|title|truncatewords:2 }} {{ user.nom|title|truncatewords:1 }}{% endwith %}
<br>
<span class="stars" data-rating="{{ i.vote.vote }}" data-num-stars="5" ></span>
<hr class="w3-clear">
<p>
{{ i.commentaire|linebreaksbr }}
</p>
<span class="glyphicon glyphicon-user"></span> {% with user=i.the_sender.profile %}{{ user.prenom|title|truncatewords:2 }} {{ user.nom|title|truncatewords:1 }}{% endwith %}
</div>
</div>
{% endfor %}
</div>
{% if ratings.has_next %}
<a class="infinite-more-link" href="?page={{ ratings.next_page_number }}"></a>
{% endif %}
{% endblock %}
{% block rightcontent %}
{% endblock %}
{% block js %}
<script>
var infinite = new Waypoint.Infinite({
element: $('.infinite-container')[0]
});
</script>
<script>
//ES5
$.fn.stars = function() {
return $(this).each(function() {
var rating = $(this).data("rating");
var fullStar = new Array(Math.floor(rating + 1)).join('<i class="fas fa-star"></i>');
var halfStar = ((rating%1) !== 0) ? '<i class="fas fa-star-half-alt"></i>': '';
var noStar = new Array(Math.floor($(this).data("numStars") + 1 - rating)).join('<i class="far fa-star"></i>');
$(this).html(fullStar + halfStar + noStar);
});
}
//ES6
$.fn.stars = function() {
return $(this).each(function() {
const rating = $(this).data("rating");
const numStars = $(this).data("numStars");
const fullStar = '<i class="fas fa-star"></i>'.repeat(Math.floor(rating));
const halfStar = (rating%1!== 0) ? '<i class="fas fa-star-half-alt"></i>': '';
const noStar = '<i class="far fa-star"></i>'.repeat(Math.floor(numStars-rating));
$(this).html(`${fullStar}${halfStar}${noStar}`);
});
}
</script>
<script>
$(function(){
$('.stars').stars();
});
</script>
{% endblock %}
I have tried to put the <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css" rel="stylesheet"> inside the class="infinite-item" but it does not help.what might be the reason for that ? Thanks.
Since yesterday I am on this question I tried everything.
This is another user that has tried to help me here https://stackoverflow.com/a/69930878/15042684 but I did not really understand could you please help me to understand it with some code.
this is his answer:
It doesn't look like .stars() will look for new elements being added to the DOM. You should look for a callback function configuration option within Waypoint.Infinite that you can call .stars() on the new elements.
Assuming you are using waypoint's Infinite Scroll, you can use the onAfterPageLoad callback
onAfterPageLoad
Default: $.noop.
Parameters: $items.
This is a callback that will fire at the end of the request cycle, after new items have been appended to the container. It is passed one parameter, which is a jQuery object of all the items that were appended during the page load.
Note that despite using the jquery convention of $name indicates a jquery object and stating is a jquery object, in this case, trial and error shows that $items are the DOM elements, not a jquery object.
No example provided in the docs, so it will probably look something like:
<script>
var infinite = new Waypoint.Infinite({
element: $('.infinite-container')[0],
onAfterPageLoad: function(items) {
$(items).find(".stars").stars();
}
});
</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'm coding an app in Python Flask and I'm building interactive Like buttons with Javascript.
Here's my route:
#app.route('/jquery')
def jquery():
posts = Post.query.all()
return render_template('jquery.html', posts=posts)
And in the jquery.html template I have:
{% for p in posts %}
{% if p.upvotes %}
{% set pupvotes = p.upvotes %}
{% else %}
{% set pupvotes = 0 %}
{% endif %}
<p>{{ p.author.username }} says: <b>{{ p.body }}</b> <button id="
{{ p.id }}" onclick="document.getElementById('{{ p.id }}').innerHTML = {{
pupvotes }} +1 + ' Likes'">{{ pupvotes }} Likes</button></p>
{% endfor %}
Everything actually works this way, but I would like to save the results of +1 likes clicks and transfer them to a Python variable so that I can add it to the database and the updated Like numbers show up on the page after refresh.
I tried to use JavaScript function this way:
<script>
function myF1() {
document.getElementById('{{ p.id }}').innerHTML = {{ pupvotes }} +1 + '
Likes';
}
</script>
and:
onclick="myF1()"
But then only the last Like on the page gets updated with click, no matter which Like button I click.
OK, I've made it work:
Here's our Flask route:
#app.route('/ulk')
def ulk():
ppp = request.args.get('p', 0, type=int)
lpost = Post.query.filter_by(id=ppp).first()
lpost.upvotes += 1
db.session.commit()
return jsonify(result=ppp)
And here's our HTML page with some jQuery:
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
</head>
<body>
<script type=text/javascript>
$(function() {
$('a#like').bind('click', function() {
$.getJSON('/ulk', {
p: pid
}, function(data) {
$("#result2").text(data.result);
});
return false;
});
});
</script>
<br/><br/>
{% for p in posts %}
{% if p.upvotes %}
{% set pupvotes = p.upvotes %}
{% else %}
{% set pupvotes = 0 %}
{% endif %}
<p>{{ p.author.username }} says: <b>{{ p.body }}</b>
<a href="#" id=like>
<button id="{{ p.id }}" onclick="pid={{ p.id }};pvts = {{ pupvotes }} +1;myF1();">{{ pupvotes }} Likes</button>
</a>
</p>
<script type=text/javascript>
function myF1() {
$( "#" + pid ).text(pvts + ' Likes');
$('#' + pid).on('click', function() {
$(this).prop('disabled', true);
});
}
</script>
{%endfor%}
<br/>
<span id=result2>...</span>
</body>
</html>
You are close, however, as #MartijnPieters pointed out, you still need to communicate with the backend to update the number of likes for the post. To do so, slightly change your HTML to include a button to update the likes with a class and id. The id will be the same as the post id, and the class will be generic. Then, utilize jquery with ajax after creating a script.
First, in the Python backend, create a route to handle the updating of the likes for a post:
#app.route('/update_like')
def update_likes():
_id = int(flask.request.args.get('post_id'))
#Do something to update the database
return flask.jsonify({'success':'True'})
I suggest returning a jsonified response so that you can handle errors that may occur, such as a user liking a post twice. If you discover that that is the case, then you could return flask.jsonify({'success':'False'}) and handle that accordingly in the frontend.
Then, in the HTML:
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
</head>
<div class='post_wrapper'>
{%for p in posts%}
<div id='post_{{post.id}}'>
<p>{{p.author.username}} says:</p>
<p>{{p.body}}</p>
{%if p.upvotes%}
<button class='upvote' id='upvote_{{p.id}}'>Like <span id='upvotes_{{p.id}}'>{{p.upvotes}}</span></p>
{%else%}
<button class='upvote' id='upvote_{{p.id}}'>Like <span id='upvotes_{{p.id}}'>0</span></p>
{%endif%}
</div>
{%endfor%}
</div>
<script>
$(document).ready(function(){
$('.post_wrapper').on('click', '.upvote', function(){
var post_id = this.id.match('\\d+');
$.ajax({
url: "/update_like",
type: "get",
data: {post_id: post_id},
success: function(response) {
if (response.success === 'True'){
var like_val = parseInt($('#upvotes_'+post_id).text()) + 1;
$('#upvotes_'+post_id).text(like_val.toString());
}
else{
alert('You already liked that post!');
}
}
});
});
});
</script>
</html>
I'm trying to implement grouping of product image variants in my Shopify theme following this tutorial.
I'm pretty sure I've done everything right up until I reach the Javascript element. I know that I have labelled my images correctly in the Shopify Admin. I've put the JS at the bottom of my product.liquid file and altered slightly to resemble my code however there is a part at the end of the tutorial referencing the JS snippet below which I don't understand or know where it needs to go. I'm presuming it needs to go in my script.js file but I'm not sure.
JS:
var switchImage = function(newImageSrc, newImage, mainImageDomEl) {
jQuery(mainImageDomEl).attr('src', newImageSrc);
$(mainImageDomEl).parents('a').attr('href', newImageSrc);
};
My code is as follows:
product.liquid:
<!-- Begin product photos -->
{% assign featured_image = product.selected_or_first_available_variant.featured_image | default: product.featured_image %}
<!-- Begin featured image -->
<div class="product-single__photos image featured" id="ProductPhoto">
<a href="{{ featured_image | img_url: '1024x1024' }}" class="fancybox" rel="gallery1" id="placeholder">
<img src="{{ featured_image | img_url: '1024x1024' }}" alt="{{ featured_image.alt | escape }}" id="ProductPhotoImg" />
</a>
</div>
<!-- End product image -->
{% comment %}
Create thumbnails if we have more than one product image.
{% endcomment %}
{% if product.images.size > 1 %}
<!-- Begin thumbnails -->
<ul class="grid-uniform">
<div class="thumbs clearfix">
{% assign featured_alt = product.selected_or_first_available_variant.option1 %}
{% for image in product.images %}
{% if image.alt == featured_alt or image == featured_image %}
{% unless forloop.first %}
<li class="image grid_item">
<a href="{{ image | img_url: '1024x1024' }}" class="fancybox" rel="gallery1" data-original-image="{{ image | product_img_url: '1024x1024' }}">
<img src="{{ image | img_url: '1024x1024' }}" alt="{{ image.alt | escape }}" />
</a>
</li>
{% endunless %}
{% endif %}
{% endfor %}
</div>
</ul>
<!-- End thumbnails -->
{% endif %}
<!-- End product photos -->
JS:
jQuery(document).ready(function($){
var images = [];
{% for image in product.images %}
images.push({url: "{{ image | product_img_url: '1024x1024' }}", alt: "{{ image.alt }}"});
{% endfor %}
var thumbnails = $(".thumbs");
$('#product-select-option-0').change(function() {
var selected = $(this).val(), mainImage = jQuery('.featured img').attr('src');
thumbnails.hide().html("");
arr = [];
var addImage = $.each( images, function( i, image ) {
var alt = images[i].alt, url = images[i].url;
if (alt == selected || url == mainImage) {
thumbnails.append('<li class="grid_item"><img src="' + url + '" alt="' + alt + '"></li>');
}
});
arr.push(addImage);
$.when.apply($, arr).done(function () {
thumbnails.fadeIn();
$('#product .thumbs a').on('click', function(e) {
e.preventDefault();
switchImageTwo($(this).attr('href'), null, $('.featured img')[0]);
});
});
});
});
Any help would be most appreciated.
Towards the end of your js you are calling a function called switchImageTwo, which you haven't defined anywhere yet. The js snippet you are unsure about is an example of what the switchImage function could look like (or switchImageTwo in your case). You can put this function with your existing js, above the line $.when.apply... should work.