Double buffering divs flicker when refreshing in Ajax - javascript

I have code that refreshes a div running on local server without flickering, but when hosting it on a webserver, it does flicker (disappear to background) when refreshing:
<script>
setTimeout(function() {
$.ajax({
url: "",
context: document.body,
success: function(s,x){
$(this).html(s);
}
});
}, 1000);
</script>
I've had a good look around SO and the web, and it seems I want to double buffer the div table I am refreshing - have one hidden, refresh that, then swap the display style of the two divs
I started with How to avoid blinking when updating page from ajax
moved on to http://www.brightcherry.co.uk/scribbles/jquery-auto-refresh-div-every-x-seconds/
and Trouble with dynamic refreshing div
And got some good ideas from:How to toggle between two divs
The code I am trying to get to work may be too complicated. I feels like it should work, and the tables refresh, but they flicker with a long time between display.
Divs;
<div id="id1" style="display: none";>
<div id="allTable1" class = "tableCenter">
<div id="hiTable" class = "table">
{% for entry in high_entry_list %}
<li>
<a href="/entries/{{ entry.id }}/">
<div class = "high" style = "text-align: left";>
{{ entry.text }}
<div style="float: right">
{{ entry.score }}
</div>
</div>
</a>
</li>
{% endfor %}
</div>
....and two more tables as hiTable above...
</div>
</div>
<div id="id2" style="display: block";>
<div id="allTable2" class = "tableCenter">
<div id="hiTable" class = "table">
{% for entry in high_entry_list %}
<li>
<a href="/entries/{{ entry.id }}/">
<div class = "high" style = "text-align: left";>
{{ entry.text }}
<div style="float: right">
{{ entry.score }}
</div>
</div>
</a>
</li>
{% endfor %}
</div>
....and two more tables as hiTable above...
</div>
</div>
Script:
<script>
var visible_id = 'id2';
setInterval(function() {
if(visible_id == 'id2') {
document.getElementById('id1').style.display = 'block';
document.getElementById('id2').style.display = 'none';
$.ajax({
url: "/index",
context: document.getElementById('allTable2'),
success: function (s) {
$("#allTable2").html(s).load;
}
});
visible_id = 'id1';
} else {
document.getElementById('id1').style.display = 'none';
document.getElementById('id2').style.display = 'block';
$.ajax({
url: "/index",
context: document.getElementById('allTable1'),
success: function (s) {
$("#allTable1").html(s).load;
}
});
visible_id = 'id2';
}
}, 1000);
</script>
So I have div wrappers for the two copies of three tables (one hidden, one shown), the javascript checks the visibility, swaps display style for the two wrappers, and updates the hidden one with an ajax refresh (which works). Is there an obvious logic or coding flaw that may be causing the flicker?

AJAX requests can take significantly longer than a second. You are toggling between the tables whether or not a particular AJAX request has finished, but the AJAX will still execute (perhaps 1.5 seconds later), giving you that undesired flickering behavior.
Rather than setting an interval which will execute whether or not the AJAX request has finished, set a timeout from the AJAX callback. Something like this (you'll probably need to fiddle with it some):
<script>
(function(){
var visible_id = 'id2';
function toggleDisplay(){
// toggle displayed table
document.getElementById('id1').style.display = visible_id === 'id2' ? 'block' : 'none';
document.getElementById('id2').style.display = visible_id === 'id2' ? 'none' : 'block';
var tableId = visible_id === 'id2' ? 'allTable1' : 'allTable2';
$.ajax({
url: "/index",
context: document.getElementById(tableId),
success: function (s) {
$("#" + tableId).html(s).load;
// when the ajax request has finished, initiate the next ajax request
// after a 1 second delay
window.setTimeout( toggleDisplay, 1000 );
}
});
visible_id = visible_id === 'id2' ? 'id1' : 'id2';
}
})();
</script>

Related

Displaying a loading gif every time when a date picker date is changed

I have a laravel application which shows some stats to my users.
On my front end blade, I'm displaying few widgets where each widget contain's a specific stat.
Following widget is to show number of total orders.
<div class="row mt-3" id="shopify_row1">
<div class="col-md-2" id="shopify_widget1">
<div class="jumbotron bg-dark text-white">
<img class="img-fluid pull-left" src="https://cdn0.iconfinder.com/data/icons/social-media-2092/100/social-35-512.png" width="32" height="32">
<h6 class="text-secondary mt-2 px-4">Shopify</h6>
<hr class="border border-white">
<h5 class="text-white">Total Orders</h5>
<span class="tot_o" id="tot_o">{{ $tot_o }}</span>
</div>
</div>
</div>
Like this widget, I have 5 more widgets to display 5 different stats.
In every widget initially I'm displaying stats for the current date, eg: if the total number of orders for the day is 0, it shows 0...
Then, I have added a a date picker as I can get the data only for a particular day.
<td>
<input id="date" class="date form-control" type="date">
</td>
And following is my jQuery...
<script>
$(document).on('change', '#date', function (e) {
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
$.ajax({
type: 'GET',
url : '/shopify_data',
data : {selected_date : $('#date').val()},
success:function(data){
$('#tot_o').empty();
$('#tot_sum').empty();
$('#avg_ov').empty();
$('#cus').empty();
$('#item_sum').empty();
$('#orders').empty();
var total_orders = data.tot_o;
var total_sales = data.sum;
var currency = data.crr;
var avg_ov = data.avg_ov;
var cus = data.cus;
var item_sum = data.item_sum;
var orders = data.orders;
$('#tot_o').append(total_orders);
$('#tot_sum').append(total_sales);
$('#avg_ov').append(avg_ov);
$('#cus').append(cus);
$('#item_sum').append(item_sum);
$('#orders').append(orders);
//console.log(total_orders);
},
timeout:10000
});
});
</script>
This entire code works perfectly, but now I need to add a loading gif till the updated results get displayed on the date change.
What changes should I do to above jQuery in order to add the loading gif...
There are multiple ways how you can create the loading gif. One would be to create an element in your blade template that is hidden or shown by using a class.
HTML:
<div class="loader hidden"></div>
CSS:
.hidden {
display: none;
}
jQuery:
const loader = document.querySelector('.loader');
$(document).on('change', '#date', function (e) {
loader.classList.remove('hidden');
// your other code..
}
And inside your success function you add the hidden class which should hide the loading element again.
success: function(data){
loader.classList.add('hidden');
// your existing code..
},
However, I would instead add a complete block, which ensures that on failure as on success the loading element is hidden.
$.ajax({
// your existing code..
complete: () => {
loader.classList.add('hidden');
}
}
You can place loading gif in any place of DOM with style="display: none".
Next, in your script before ajax you can show gif and after success or fail result hide it again:
<script>
let gif = $('.loading-gif'); // Your loading gif
$(document).on('change', '#date', function (e) {
gif.show(); // Show loading gif
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
$.ajax({
type: 'GET',
url : '/shopify_data',
data : {selected_date : $('#date').val()},
success:function(data){
gif.hide(); // Hide gif
$('#tot_o').empty();
$('#tot_sum').empty();
$('#avg_ov').empty();
$('#cus').empty();
$('#item_sum').empty();
$('#orders').empty();
var total_orders = data.tot_o;
var total_sales = data.sum;
var currency = data.crr;
var avg_ov = data.avg_ov;
var cus = data.cus;
var item_sum = data.item_sum;
var orders = data.orders;
$('#tot_o').append(total_orders);
$('#tot_sum').append(total_sales);
$('#avg_ov').append(avg_ov);
$('#cus').append(cus);
$('#item_sum').append(item_sum);
$('#orders').append(orders);
//console.log(total_orders);
},
timeout:10000
});
});
</script>

Ajax search doesn't work the second time (ASP.NET MVC)

I have a problem changing items after searching.
I looked at similar threads but found no solution there :(
It looks like the first time the page loads well - the first time the entire Index.cshtml page is loaded which contains a collection of books in the selected category.
There is a search engine on the page - after searching for "manual" - ajax correctly replaces elements with those containing "manual" in the name.
Then when I enter something into the search engine a second time (for example "exercises") - the content of the page does not change any more.
I tried to debug and I see that new items are correctly downloaded from the database - the condition "if (Request.IsAjaxRequest ())" is true and the items are passed to partial view - there the "foreach" loop goes through them. Unfortunately, after _Partial, nothing happens.
I can't find a mistake - the strangest thing is that the first ajax call works fine - only the second (and subsequent) bad.
CatalogController.cs
public ActionResult Index(string categoryName = null, string searchQuery = null)
{
if (categoryName == null)
categoryName = (db.Categories.Find(1)).Name;
var category = db.Categories.Include("Books").Where(x => x.Name.ToLower() == categoryName).Single();
var books = category.Books.Where(x => (searchQuery == null || x.Title.ToLower().Contains(searchQuery.ToLower()) || x.SubTitle.ToLower().Contains(searchQuery.ToLower()) || x.Level.ToLower().Contains(searchQuery.ToLower())) && !x.Inaccessible);
if (Request.IsAjaxRequest())
return PartialView("_PartialBooksList", books);
else
return View(books);
}
Index.cshtml
<form class="o-search-form" id="search-form" method="get" data-ajax="true" data-ajax-target="#booksList">
<input class="o-search-input" id="search-filter" type="search" name="searchQuery" data-autocomplete-source="#Url.Action("SearchTips")" placeholder="Search" />
<input class="o-search-submit" type="submit" value="" />
</form>
<div class="row" id="booksList">
#Html.Partial("_PartialBooksList")
</div>
#section Scripts
{
<script src="~/Scripts/jquery-3.5.0.js"></script>
<script src="~/Scripts/jquery-ui-1.12.1.js"></script>
<script>
$(function () {
var setupAutoComplete = function () {
var $input = $(this);
var options =
{
source: $input.attr("data-autocomplete-source"),
select: function (event, ui) {
$input = $(this);
$input.val(ui.item.label);
var $form = $input.parents("form:first");
$form.submit();
}
};
$input.autocomplete(options);
};
var ajaxSubmit = function () {
var $form = $(this);
var settings = {
data: $(this).serialize(),
url: $(this).attr("action"),
type: $(this).attr("method")
};
$.ajax(settings).done(function (result) {
var $targetElement = $($form.data("ajax-target"));
var $newContent = $(result);
$($targetElement).replaceWith($newContent);
$newContent.effect("slide");
});
return false;
};
$("#search-filter").each(setupAutoComplete);
$("#search-form").submit(ajaxSubmit);
});
</script>
}
_PartialBooksList
#model IEnumerable<ImpressDev.Models.Book>
#using ImpressDev.Infrastructure
<div class="row">
#foreach (var book in Model)
{
<div class="col-12 col-xl-4">
<a class="o-shop-link" href="#Url.Action("Details", "Catalog", new { bookId = book.BookId })">
<div class="o-shop-item">
<img class="o-shop-img" src="#Url.BookPhotoSourcePath(book.PhotoSource)" />
<div class="o-shop-text">
<h2>#book.Title</h2>
<h6>#book.SubTitle - #book.Level - <b>#book.Price zł.</b></h6>
+ Add to cart
</div>
</div>
</a>
</div>
}
</div>
Please help
I am not sure if this is the case, but try to change this code:
$($targetElement).replaceWith($newContent);
To this:
$($targetElement).html($newContent);
I think the problem is the div element with id="booksList" is replaced after first search. So you don't have this element in the second search.
I looked through the code step by step and found a solution to my problem.
In the first search, replace id="booksList"
<div class="row" id="booksList">
#Html.Partial("_PartialBooksList")
</div>
partial view in which there was only without id = booksLists.
In the next search there was no ID in this place and there was nothing to replace.

Ajax redirecting when it is not supposed to [duplicate]

This question already has answers here:
jQuery ID selector works only for the first element
(7 answers)
Closed 6 years ago.
I've implemented a program something like a shopping cart where you would add products to the cart. I'm using ajax make the page dynamic so multiple products can be added to the cart without a page reload. For some reason, the first product in the list can be added correctly while the rest of products alway redirects to the controller url when it isn't supposed to.
View Code
<section class="grid grid--loading" id="portfoliolist">
<!-- Loader -->
<img class="grid__loader" src="~/Images/grid.svg" width="60" alt="Loader image" />
<!-- Grid sizer for a fluid Isotope (Masonry) layout -->
<div class="grid__sizer"></div>
<!-- Grid items -->
#foreach (var item in Model.ProductsList)
{
var pricetag = "pricegroup3";
if (item.Price <= 300)
{
pricetag = "pricegroup1";
}
else if (item.Price > 300 && item.Price <= 500)
{
pricetag = "pricegroup2";
}
<div class="grid__item #item.Type #pricetag">
<div class="slider">
#foreach (var image in Model.ProductImagesList.Where(m=>m.ProductID == item.Id))
{
<div class="slider__item"><img src="~/Images/Products/#image.Image" /></div>
}
</div>
<div class="meta">
<h3 class="meta__title">#item.Name</h3>
<span class="meta__brand">#item.Brand</span>
<span class="meta__price">R #item.Price</span>
</div>
<a class="action action--button action--buy" href="#Url.Action("AddToPlatform", "ProductPlatforms", new { ProdID = item.Id })" id="platformAdd" data-value="#item.Id"><i class="fa fa-shopping-cart"></i><span class="text-hidden">Add to Platform</span></a>
</div>
}
</section>
The last tag is what will be clicked on to add the product to the cart.
Script -Ajax
$("#platformAdd").click(function (e) {
e.preventDefault();
$.ajax({
type: "POST",
url: $(this).attr("href"),
success: toastr["success"]("Product added to Platform")
});
});
Controller function
public void AddToPlatform(int ProdID)
{
var currUser = User.Identity.GetUserId();
DateTime now = DateTime.Now;
var exists = ProductExists(ProdID);
if (exists == false)
{
ProductPlatform prodPlatform = new ProductPlatform()
{
ProductID = ProdID,
UserID = currUser,
ViewedStatus = false,
DateAdded = now
};
db.ProductPlatforms.Add(prodPlatform);
db.SaveChanges();
}
}
The ajax script function would call the controller function which will add the product to the cart. Since there are no redirects I can't seem to figure out why the ajax call redirects to the tag "href".
Thanks for any help!
You need to use class instead of id because ids are always unique.
$(".lnkAddProduct").click(function (e) {
e.preventDefault();
$.ajax({
type: "POST",
url: $(this).attr("href"),
success: function(){alert("ADDED");}
});
});

change an element's text with variable id

I am designing a social network that has timeline and there is like button. I use AJAX to apply the like button on the server side. the problem is that I want to change the number of like for each post immediately after they have liked successfully. Because my elements are generated by for-each, I want to change the number of like for the exact element, I really have a problem with it.I am using thymeleaf.
I am looking for an idea that how to do this.
here is my html code:
<div class="col-sm-4">
<div class="row" >
<div class="col-sm-12">
<img th:if="${tweet.isFavorited()}" src="../static/images/like.png" th:src="#{/images/like.png}" th:class="like-img" th:id="${tweet.getId()}" width="35" height="35"/>
<img th:if="${!tweet.isFavorited()}" src="../static/images/dislike.png" th:src="#{/images/dislike.png}" th:class="like-img" th:id="${tweet.getId()}" width="35" height="35"/>
</div>
</div>
<div class="row">
<div class="col-sm-12" >
<h6 th:if="${tweet.isRetweet()}" th:class="like-count" th:id="${tweet.getId()}" th:text="${tweet.getRetweetedStatus().getFavoriteCount()}"></h6>
<h6 th:if="${!tweet.isRetweet()}" th:class="like-count" th:id="${tweet.getId()}" th:text="${tweet.getFavoriteCount()}"></h6>
</div>
</div>
and it is my script code:
$(function () {
$(".like-img").click(function () {
event.preventDefault();
var $post = $(this);
var toSend = {
"tweetId": this.getAttribute("id")
}
$.ajax({
type : "POST",
contentType: "application/json; charset=utf-8",
url : "like",
data : JSON.stringify(toSend),
dataType : 'json'
}).done(function (data) {
if(data.status == "success") {
if ($($post).attr("src") == "/images/dislike.png") {
$($post).attr('src','/images/like.png');
}
else {
$($post).attr('src','/images/dislike.png');
}
return false;
}
});
});
})
Okay so to make this work you will need to assign unique ids to the like-count elements, something like so:
<h6 th:if="${tweet.isRetweet()}" th:class="like-count" th:id="${tweet.getId()}_like_count" th:text="${tweet.getRetweetedStatus().getFavoriteCount()}"></h6>
Then you can retrieve the current count, increment it, and set the text of the count element. Something like so:
var currentCount = parseInt($('#'+toSend.tweetId+'_like_count').innerHtml)
var newCount = currentCount++;
$('#'+toSend.tweetId+'_like_count').text(newCount);

Multiple div refresh on different timings in jquery/ajax?

I want to set up two div entries on a page that are refreshed from a database at different rates.
I'd like to have one div display random entries from the database every second, and below that, every 10 seconds a random entry is selected and has 500 points added to its score.
I can do the second (lottery_Winner):
<div id="lottery_Winner" class = "tableCenter">
{% for entry in lottery_winner %}
<li class = "lot"> <a> {{"WINNER"}}</a> </li>
<li class = "lot"> <span id="entry-text">{{ entry.text }}</span> <span id="score">{{ entry.score }}</span></li>
<script>
$(document).ready(function() {
var currentid = {{entry.id}};
var args = {'lotteryid':currentid};
$.get("/lotteryWinner/", args).done(function(data) {
$("#score").text(data);
});
});
</script>
{% endfor %}
</div>
<script>
setTimeout(function() {
$.ajax({
url: "",
context: document.getElementById("lottery_Winner"),
success: function(data){
$("#lottery_Winner").html(data);
}
});
}, 5000);
</script>
but when I do the first (lottery_Cycle), I get unexpected results - they both refresh the same time (undesired), there is a "ghost" element for one or both of the divs, and the lottery_cycle div entry can get the 500 points, the Winner and Cycle have the same entries and the page refresh cascades until it hangs periodically.
Here's the code I tried:
<div id="lottery_Cycle" class = "tableCenter">
{% for entry in lottery_winner %}
<li class = "lot"> <a> {{"LOTTERY"}}</a> </li>
<li><a href="/entries/{{ entry.id }}/"> <div class = "lot" style = "text-align: left";>{{ entry.text }}
<div style="float: right">{{ entry.score }}</div></a></li>
{% endfor %}
</div>
<div id="lottery_Winner" class = "tableCenter">
{% for entry in lottery_winner %}
<li class = "lot"> <a> {{"*** LOTTERY WINNER ***"}}</a> </li>
<li class = "lot"> <span id="entry-text">{{ entry.text }}</span> <span id="score">{{ entry.score }}</span></li>
<script>
$(document).ready(function() {
var currentid = {{entry.id}};
var args = {'lotteryid':currentid};
$.get("/lotteryWinner/", args).done(function(data) {
$("#score").text(data);
});
});
</script>
{% endfor %}
</div>
<script>
setTimeout(function() {
$.ajax({
url: "",
context: document.getElementById("lottery_Winner"),
success: function(data){
$("#lottery_Winner").html(data);
}
});
}, 5000);
setTimeout(function() {
$.ajax({
url: "",
context: document.getElementById("lottery_Cycle"),
success: function(data){
$("#lottery_Cycle").html(data);
}
});
}, 1000);
</script>
and here is relevant part of views.py;
def lottery(request):
context = {
'lottery_cycle': Entry.objects.random(),
'lottery_winner': Entry.objects.random(),
}
return render(request, 'entries/lottery.html', context);
def lotteryCycle(request):
c = Entry.objects.random()[0]
return HttpResponse("%s,%s,%s" % (c.id, c.text, c.score))
def lotteryWinner(request):
lottery_id = request.GET.get('lotteryid')
if request.method=='GET':
l = Entry.objects.get(pk=lottery_id)
l.score +=500
l.save()
return HttpResponse(l.score)
else:
pass
return HttpResponse('done')
(I tried setInterval as well as setTimeout, same result) Is there any advice on getting the two div refreshes working properly on different timings?

Categories