I've got a page of RadListView - I scroll down, interact etc - when I navigate to another page and then do topframe.back() - the original page's ListView loses it's last state - the entire page is rebuilt.
Anyway for it to not to re-init the page...I know in native Android the default behavior is to have a nested pages of Activities - so can always go back to the last state.
Indeed, the RadListView does not remember its state out-of-the-box at this very moment. What I have done to resolve this issue is to "cache" the scrollIndex and pass it on the loaded event for my RadListView.
e.g.
list-page.xml
<lv:RadListView id="rad-list" items="{{ dataItems }}"
loaded="onListLoaded" itemTap="onItemTap">
list-page.ts
export function onListLoaded(args: RadListwModule.ListViewEventData) {
list = <RadListwModule.RadListView>args.object;
if (list.items) {
list.scrollToIndex(roversViewModel.get("cachedIndex"));
}
}
// onItemTap is the event where I update the cached index
export function onItemTap(args:RadListwModule.ListViewEventData) {
var tappedItemIndex = args.itemIndex;
roversViewModel.set("cachedIndex", tappedItemIndex);
}
Related
Got an issue with safari loading old youtube videos when back button is clicked. I have tried adding onunload="" (mentioned here Preventing cache on back-button in Safari 5) to the body tag but it doesn't work in this case.
Is there any way to prevent safari loading from cache on a certain page?
Your problem is caused by back-forward cache. It is supposed to save complete state of page when user navigates away. When user navigates back with back button page can be loaded from cache very quickly. This is different from normal cache which only caches HTML code.
When page is loaded for bfcache onload event wont be triggered. Instead you can check the persisted property of the onpageshow event. It is set to false on initial page load. When page is loaded from bfcache it is set to true.
Kludgish solution is to force a reload when page is loaded from bfcache.
window.onpageshow = function(event) {
if (event.persisted) {
window.location.reload()
}
};
If you are using jQuery then do:
$(window).bind("pageshow", function(event) {
if (event.originalEvent.persisted) {
window.location.reload()
}
});
All of those answer are a bit of the hack. In modern browsers (safari) only on onpageshow solution work,
window.onpageshow = function (event) {
if (event.persisted) {
window.location.reload();
}
};
but on slow devices sometimes you will see for a split second previous cached view before it will be reloaded. Proper way to deal with this problem is to set properly Cache-Control on the server response to one bellow
'Cache-Control', 'no-cache, max-age=0, must-revalidate, no-store'
Yes the Safari browser does not handle back/foreward button cache the same like Firefox and Chrome does. Specially iframes like vimeo or youtube videos are cached hardly although there is a new iframe.src.
I found three ways to handle this. Choose the best for your case.
Solutions tested on Firefox 53 and Safari 10.1
1. Detect if user is using the back/foreward button, then reload whole page or reload only the cached iframes by replacing the src
if (!!window.performance && window.performance.navigation.type === 2) {
// value 2 means "The page was accessed by navigating into the history"
console.log('Reloading');
//window.location.reload(); // reload whole page
$('iframe').attr('src', function (i, val) { return val; }); // reload only iframes
}
2. reload whole page if page is cached
window.onpageshow = function (event) {
if (event.persisted) {
window.location.reload();
}
};
3. remove the page from history so users can't visit the page again by back/forward buttons
$(function () {
//replace() does not keep the originating page in the session history,
document.location.replace("/Exercises#nocache"); // clear the last entry in the history and redirect to new url
});
You can use an anchor, and watch the value of the document's location href;
Start off with http://acme.co/, append something to the location, like '#b';
So, now your URL is http://acme.co/#b, when a person hits the back button, it goes back to http://acme.co, and the interval check function sees the lack of the hash tag we set, clears the interval, and loads the referring URL with a time-stamp appended to it.
There are some side-effects, but I'll leave you to figure those out ;)
<script>
document.location.hash = "#b";
var referrer = document.referrer;
// setup an interval to watch for the removal of the hash tag
var hashcheck = setInterval(function(){
if(document.location.hash!="#b") {
// clear the interval
clearInterval(hashCheck);
var ticks = new Date().getTime();
// load the referring page with a timestamp at the end to avoid caching
document.location.href.replace(referrer+'?'+ticks);
}
},100);
</script>
This is untested but it should work with minimal tweaking.
The behavior is related to Safari's Back/Forward cache. You can learn about it on the relevant Apple documentation: http://web.archive.org/web/20070612072521/http://developer.apple.com/internet/safari/faq.html#anchor5
Apple's own fix suggestion is to add an empty iframe on your page:
<iframe style="height:0px;width:0px;visibility:hidden" src="about:blank">
this frame prevents back forward cache
</iframe>
(The previous accepted answer seems valid too, just wanted to chip in documentation and another potential fix)
I had the same issue with using 3 different anchor links to the next page. When coming back from the next page and choosing a different anchor the link did not change.
so I had
House 1
View House 2
View House 3
Changed to
House 1
View House 2
View House 3
Also used for safety:
// Javascript
window.onpageshow = function(event) {
if (event.persisted) {
window.location.reload()
}
};
// JQuery
$(window).bind("pageshow", function(event) {
if (event.originalEvent.persisted) {
window.location.reload()
}
});
None of the solutions found online to unload, reload and reload(true) singularily didn't work. Hope this helps someone with the same situation.
First of all insert field in your code:
<input id="reloadValue" type="hidden" name="reloadValue" value="" />
then run jQuery:
jQuery(document).ready(function()
{
var d = new Date();
d = d.getTime();
if (jQuery('#reloadValue').val().length == 0)
{
jQuery('#reloadValue').val(d);
jQuery('body').show();
}
else
{
jQuery('#reloadValue').val('');
location.reload();
}
});
There are many ways to disable the bfcache. The easiest one is to set an 'unload' handler. I think it was a huge mistake to make 'unload' and 'beforeunload' handlers disable the bfcache, but that's what they did (if you want to have one of those handlers and still make the bfcache work, you can remove the beforeunload handler inside the beforeunload handler).
window.addEventListener('unload', function() {})
Read more here:
https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/1.5/Using_Firefox_1.5_caching
I'm using a Kendo ScrollView to display person images on a form.
Separate from the ScrollView, users can change the display order of the images. After they save their changes to the display order, the ScrollView is reloaded, scrolls to the first item, and should display the images in their new order.
However, I've noticed that when the ScrollView is currently on the first page, that page does not get refreshed/redrawn.
My ScrollView looks something like this:
#(Html.Kendo().ScrollView()
.Name("personImage")
.TemplateId("personImageTemplate")
.DataSource(d => d
.Custom()
.Type("aspnetmvc-ajax")
.Transport(t => t
.Read(r => r.Action("PersonImages_Read", "Person", new { personID = Model.ID } ))
)
.Schema(s => s.Data("Data").Total("Total"))
.ServerPaging(false)
.PageSize(1)
)
)
The template looks like this:
<script type="text/x-kendo-tmpl" id="personImageTemplate">
<img class="personImage"
src="#(Url.Action("ImageRender", "Person"))?imageID=#= data.ID #"
title="#= data.Filename #" />
</script>
And here is my refresh function:
function refreshPersonImageScrollView() {
var scrollView = $("#personImage").data("kendoScrollView");
if (scrollView) {
scrollView.dataSource.read();
// https://docs.telerik.com/kendo-ui/api/javascript/ui/scrollview/methods/refresh
// redraws, doesn't re-read from datasource
scrollView.refresh();
// scroll to first image
scrollView.scrollTo(0);
}
}
When I watch the requests being made when I call this function, I see this:
A. When a page other than the first page is selected:
PersonImages_Read (the ScrollView's dataSource read)
The ScrollView scrolls to the first image
3x ImageRender, as it renders the first 3 items in the ScrollView
B. When the first page is selected:
PersonImages_Read (the ScrollView's dataSource read)
Nothing else
I tried switching the order of scrollView.refresh() and scrollView.scrollTo(0), but the result does not change.
Is there any way to get Kendo to refresh/redraw the current page? I thought refresh would do it, based on the documentation, but it does not.
Edit
I've been able to replicate this issue in REPL. To see the behavior in action:
Note the "Rendered" time under the first image.
Scroll to the second image in the ScrollView.
Wait several seconds, then click the "Refresh" button.
The ScrollView should scroll back to the first image.
Observe that the "Rendered" time under the first image matches the "Last clicked" time reported below the "Refresh" button, and is no longer what it was in step #1. (This is the correct behavior.)
Remain on the first image for several seconds. Note the "Rendered" time listed before continuing.
Click the "Refresh" button.
Note that the "Last clicked" time has updated, and in the "Log" section, there is an entry that reads "dataSource read complete" at approx. the same time. However, the "Rendered" time under the image has not changed, and there is no log entry that says "image for product #X loaded".
I am using Kendo version 2021.3.1109 in my project. The Kendo version in the REPL above is 2022.3.913 and it still occurs in that version.
I have found a way to resolve the issue, but this may be worth opening a possible bug ticket with Telerik, because you would think that scrollView.refresh call would work.
What I changed in your refreshPersonImageScrollview function was to call setDataSource on the scrollview rather than calling the refresh method. Like so:
function refreshPersonImageScrollView() {
$("#refresh-last-clicked").text("Last clicked: " + getCurrentTime());
addToLog("refresh button clicked");
var scrollView = $("#personImage").data("kendoScrollView");
if (scrollView) {
scrollView.dataSource.read();
scrollView.setDataSource(scrollView.dataSource);
// scroll to first image
scrollView.scrollTo(0);
}
}
This appears to force the scrollView to re-evaluate its life choices and properly refresh :) However, it does seem to trigger additional dataSource reads, so it's not ideal.
One other thing I tried that didn't resolve the problem, but may be a good thing to change to anyway, would be to utilize the promise returned by the dataSource.read call. Meaning, do your scrollView setDataSource and scrollTo calls after the dataSource read promise is settled, like so:
scrollView.dataSource.read().then(function() {
scrollView.setDataSource(scrollView.dataSource);
// scroll to first image
scrollView.scrollTo(0);
});
REPL link here
I am using Shopify "Streamline Theme" with quick product view and I recently added infinite scroll to products on each collection using Ajaxinate.js.
When I open a collection page it loads with some products which is supposed to do, The products already there work fine with quick view and quick add to cart and also.
The Infinite scroll works fine and it loads new product fine but the problem is raised when the new products loaded through AJAX call doesn't have work with the quick view function.
I have tried to create a callback function to activate the quick view with no success, using the theme initialisation code with no success.
function callBack(){
theme.init();
theme.initQuickShop();
};
document.addEventListener("DOMContentLoaded", function() {
var endlessClick = new Ajaxinate({
method: "scroll",
loadingText: 'Loading...',
callback: callBack
});
});
Edit -------
My problem, is that when the page is loaded only the initial loaded products quickview elements are loaded in the DOM. When the scroll more button is clicked, the newly loaded products are loaded without their respective quickview elements. Hence why the quickview does't work for them. The theme.js file comes with this initialisation code:
theme.reinitProductGridItem = function($scope) {
if (AOS) {
AOS.refreshHard();
}
if (theme.settings.currenciesEnabled) {
theme.currencySwitcher.ajaxrefresh();
}
// Reload quick shop buttons
theme.initQuickShop(true);
// Refresh reviews app
if (window.SPR) {
SPR.initDomEls();SPR.loadBadges();
}
// Re-register product templates in quick view modals.
// Will not double-register.
sections.register('product-template', theme.Product, $scope);
// Re-hook up collapsible box triggers
theme.collapsibles.init();
};
I have tried to integrate this into a callback but no success, the quickview modal doesn't seem to load for the newly loaded products:
function callBack(){
ReloadSmartWishlist();
var $container = $('#CollectionSection');
theme.reinitProductGridItem($container);
// I have tried the following init qith no success:
// theme.init();
// theme.initQuickShop(true);
// theme.initQuickShop();
// sections.register('product-template', theme.Product, $container);
// AOS.refreshHard();
};
document.addEventListener("DOMContentLoaded", function() {
var endlessClick = new Ajaxinate({
method: "click",
loadingText: 'Loading...',
offset: 0,
callback: callBack
});
});
I am missing something but what? :/
Note for other things like loading products images with the callback and the wishlist app, it works as intended...
When you load elements via AJAX and if the events are not attached to a parent element that is not removed from the DOM, those elements will not have an attached event to them.
The term used here is event delegation.
Here is an example of non-delegated event:
document.querySelectorAll('a').addEventListener('click', function(){
// Do something
})
Since you are attaching the event to the existing "a" elements if you add new 'a' via AJAX those elements will not have the event since Javascript already attached all the events and it will not reattach them if you don't specifically recall them again.
Here is an example of a delegated event:
document.querySelector('body').addEventListener('click', function(target){
let target = event.target;
if (target.tagName === 'A'){
// Do something here
}
})
Where we attach the event to the body tag ( where it's a better idea to attach it to a closer none-modified parent element of the ajax items ) and once we click we check if our target tag is an "a" and do something then.
So long story short, you will need to delegate the quick cart link so that it works after you load the items via AJAX.
Drip is correct you need to delegate your event, but for people like me it's hard to completely understand how to do that.
I'm not sure how your quickview is structured, but if you open it with a .click function and can use jquery use the [.on() function][1].
For example: I use a quickview that opens on a button click. My button is attached to my product-grid-item.liquid with this bit of code:
<div class="quick-view-button">
<a class="quick-view" data-handle="{{ product.handle }}" href="javascript:void(0);">Quick View</a>
</div>
My quickview function originally looked like this:
function quickView() {
$(".quick-view").click(function () {
//all of the quickview code
What happens is exactly like you described. The event listeners only loaded on the first product load but nothing after an AJAX load.
Using jquery's .on() binds the event listener to the element meaning when it's loaded in later it'll still have the event. Here's an example of what my code looks like after using .on()
function quickView() {
$('body').on('click','.quick-view',function(){
I really hope this helps you or someone else with this problem.
[1]: http://api.jquery.com/on/
How can I get the current screen I'm working on? For example, I have a slidedrawer containing buttons to navigate to another page. When I'm on a certain page(About Page) and when I tap the button to navigate to the About Page, I want to just close the slidedrawer if it is on the same page.
My idea is that to get the current page and just compare it but I dont know how.
Note: The slidedrawer content menu is a custom component.
There are several ways to solve this problem. The easiest is to install the nativescript-dom plugin and then you can do this really simply:
// This will return an array of elements that are SlideDrawer's.
var slideDrawers = getElementsByTagName('SlideDrawer');
or even better is if you have assigned your SlideDrawer an id, you can do
<!-- Declarative XML -->
<SlideDrawer id="myId">...</SlideDrawer>
// JS: Will return a single element that matching the id.
var mySlideDrawer = getElementById('myId');
However, if you just want to not use any helpers and you want to get direct access to the currentPage the method is to do:
var frame = require('ui/frame');
var myPage = frame.topmost().currentPage;
Please note; the currentPage will reflect the old page while navigation is taking effect until the navigatedTo event is fired, at that point the currentPage is actually updated to be the currentPage. However, if you are looking for the current page during any of the navigation events (NavigatingTo, NavigatedTo, Loaded, Unloaded) each of those events are transferred a parameter with the current page as part of the object.
exports.onNavigatedTo = function(args) {
var page = args.object;
// do what you want with the current page variable
}
I have a Panel that I initially replace with another panel (a spinning circle saying "loading") and after a while in an AJAX event replace back. So what I do is:
#Override
protected void onConfigure() {
if (!content.isLoaded()) {
tmp = new LoadingCircle(getId(), "Loading...");
this.replaceWith(tmp);
}
}
public void onLoaded(AjaxRequestTarget target) {
if (tmp != null) {
tmp.replaceWith(this);
tmp = null;
}
target.add(this);
}
This works, so the panel is shown back in the page. However, this panel has some subpanels with some labels and they are not refreshed. I see the updated labels when I reload the page but not immediately after showing the whole panel with AJAX.
I notice that the onConfigure and onBeforeRender methods are called only once - when the panel is created but not again when it is actually shown for the first time using AJAX. The same for the subpanels, so that would explain why they are not refreshed. But the question is - why is the panel not refreshed (the updated model values are not used) when it is added to the AJAX request target?
EDIT: I think this may be relevant: https://issues.apache.org/jira/browse/WICKET-5107. Still, I wonder what I should change in my code to make it work?
IMO it looks like you should use an AjaxCallDecorator instead to do what you are trying to do with the spinner.