I am playing with jquery and js, trying to build an ajax overlay image viewer for a PHP website. With this code included at the bottom of the 'gallery page', the viewer opens and i can navigate with next and previous links inside the viewer. But the back button and the history is hard to understand. The browser often shows only the response of the ajax call, without the underlying page and css files, after some clicks back.
Perhaps somebody knows what is generally happening in such a case? I would like to understand why back sometimes results in a broken page, i.e. only the ajax response.
<script type="text/javascript">
$(document).ready(function() {
function loadOverlay(href) {
$.ajax({
url: href,
})
.done(function( data ) {
var theoverlay = $('#flvr_overlay');
theoverlay.html( data );
var zoompic = $('#zoompic');
zoompic.load(function() {
var nih = zoompic.prop('naturalHeight');
var photobox = $('#photobox');
if($(window).width() >= 750){
photobox.css('height',nih);
}
theoverlay.show();
$('body').css('overflow-y','hidden');
$(window).resize(function () {
var viewportWidth = $(window).width();
if (viewportWidth < 750) {
photobox.css('height','auto');
zoompic.removeClass('translatecenter');
}else{
photobox.css('height',nih);
zoompic.addClass('translatecenter');
}
});
});
});
return false;
}
var inithref = window.location.href;
$(window).on('popstate', function (e) {
if (e.originalEvent.state !== null) {
//load next/previous
loadOverlay(location.href);
} else {
//close overlay
$('#flvr_overlay').hide().empty();
$('body').css('overflow-y','scroll');
history.replaceState(null, inithref, inithref);
}
});
$(document).on('click', '.overlay', function () {
var href = $(this).attr('href');
history.pushState({}, href, href);
loadOverlay(href);
return false;
});
});
</script>
edit
clicking forward works:
/photos (normal page)
/photos/123 (overlay with '/photos' below)
/locations/x (normal page)
/photos/567 (overlay with '/locations/x' below)
clicking back gives me the broken view at point 2.
Do you need to prevent the default behaviour in your popstate to prevent the browser from actually navigating back to the previous page?
you have to manage it by own code.
You have a few options.
Use localstorage to remember the last query
Use cookies (but don't)
Use the hash as you tried with document.location.hash = "last search" to update the url. You would look at the hash again and if it is set then do another ajax to populate the data. If you had done localstorage then you could just cache the last ajax request.
I would go with the localstorage and the hash solution because that's what some websites do. You can also copy and paste a URL and it will just load the same query. This is pretty nice and I would say very accessible
Changing to document.location.hash = "latest search" didn't change anything.t.
This goes into the rest of the jQuery code:
// Replace the search result table on load.
if (('localStorage' in window) && window['localStorage'] !== null) {
if ('myTable' in localStorage && window.location.hash) {
$("#myTable").html(localStorage.getItem('myTable'));
}
}
// Save the search result table when leaving the page.
$(window).unload(function () {
if (('localStorage' in window) && window['localStorage'] !== null) {
var form = $("#myTable").html();
localStorage.setItem('myTable', form);
}
});
Another solution is that use INPUT fields to preserved while using back button. So, I do like that :
My page contains an input hidden like that :
Once ajax content is dynamicaly loaded, I backup content into my hidden field before displaying it:
function loadAlaxContent()
{
var xmlRequest = $.ajax({
//prepare ajax request
// ...
}).done( function(htmlData) {
// save content
$('#bfCache').val( $('#bfCache').val() + htmlData);
// display it
displayAjaxContent(htmlData);
});
}
And last thing to do is to test the hidden field value at page loading. If it contains something, that because the back button has been used, so, we just have to display it.
jQuery(document).ready(function($) {
htmlData = $('#bfCache').val();
if(htmlData)
displayAjaxContent( htmlData );
});
Related
I have a page with navigation links on the left and a content div on the right. The navigation links HTML are set up like this.
Test
I have set up a JavaScript function as seen below that I thought would bind to the links, push a new state into the browser history and load the content for that link based on the data-mode value. Going FORWARD works perfectly, however when I go backward sometimes I have to click back twice to get go back to the previous hash and I'm not sure why.
Here is what I have so far.
<script language="javascript">
// Create var indicating the CURRENT mode we're on - home being the default when page loads
currentMode = "home";
// Push the default page (mode) into the history
history.pushState(null, document.title, location.pathname + "#!/detail-mode/" + currentMode);
// bind to my nav links
$(document).on('click', '.nav-link', function(e){
e.preventDefault();
ajaxLoadVehicleSearchDetailContent($(this).data('mode'));
});
function ajaxLoadVehicleSearchDetailContent(mode) {
// Get the mode and link object
var thisMode = mode.toString().toLowerCase();
var thisLink = $('a[data-mode="' + mode + '"]');
// If we're switching to a different tab - continue
if (currentMode != thisMode) {
currentMode = thisMode;
// Get the content via AJAX
$.ajax({
url: "/myAjaxFunctionFileURL",
type: "POST",
data: { method:"getTabContent", mode:thisMode },
success: function(result) {
history.pushState(null, document.title, location.pathname + "#!/detail-mode/" + thisMode);
// Update the content area - fadeOut,replace,fadeIn
$('#contentArea').fadeOut(globalAnimationDuration, function() {
$('#contentArea').html(result).fadeIn(globalAnimationDuration);
$('.nav-link').removeClass('current');
thisLink.addClass('current');
});
}
});
}
}
// Listen for the popstate and load correct content when user goes BACK in history
window.addEventListener("popstate", function() {
if (location.hash.indexOf("#!") != -1) {
var thisMode = location.hash.split("/").slice(-1)[0];
ajaxLoadVehicleSearchDetailContent(thisMode);
}
}, false);
</script>
The globalAnimationDuration var is simply a variable with a value of 500.
Could anyone shine some light onto why the strange behavior when going back? Or even a good example showing how to perform the task that I could follow and update my methods accordingly?
JSFiddle Here
Thank you!
I figured it out. Since I'm calling the same ajaxLoadVehicleSearchDetailContent on both forward and back navigation, it was adding items to the stack errorneously when clicking back. So it would pop the items from the stack, call the function which then re-added it to the stack unless it was the current tab.
My solution added an additional argument to my ajaxLoadVehicleSearchDetailContent() method as seen below.
function ajaxLoadVehicleSearchDetailContent(mode,backward) {
// Get the mode and link object
var thisMode = mode.toString().toLowerCase();
var thisLink = $('a[data-mode="' + mode + '"]');
// If we're switching to a different tab - continue
if (currentMode != thisMode) {
currentMode = thisMode;
// Get the content via AJAX
$.ajax({
url: "/myAjaxFunctionFileURL",
type: "POST",
data: { method:"getTabContent", mode:thisMode },
success: function(result) {
if (!backward) history.pushState(null, document.title, location.pathname + "#!/detail-mode/" + thisMode);
// Update the content area - fadeOut,replace,fadeIn
$('#contentArea').fadeOut(globalAnimationDuration, function() {
$('#contentArea').html(result).fadeIn(globalAnimationDuration);
$('.nav-link').removeClass('current');
thisLink.addClass('current');
});
}
});
}
}
Then when I call it from my nav link - send false, and when I call it from my popstate event I call true.
I'm working with a Google-Extention which allows me to open a new tab containing a form. After the form gets filled out and saved, every time I open this tab again the form should be prefilled with the data saved earlier.
Here is how the data gets saved: WORKS!
function saveCheckoutData() {
var vName = document.getElementById('txbx_name').value;
chrome.storage.sync.set({'name': vName}, function() {
console.log(vName);
})
}
Here is how i get the data: WORKS!
function getdata() {
chrome.storage.sync.get('name', function(data) {
var name = data.name;
if(name != null){
document.getElementById("txbx_name").value = name;
}
});
}
The code above gets called on button click and works perfectly!
But as soon I try to do this when the tab gets opened it doesn't work (the tab gets opened but there is nothing in the textbox): DOESN'T WORK!
function configAutofill(){
var newURL = "autofill_data.html";
chrome.tabs.create({ url: newURL });
chrome.storage.sync.get('name', function(data) {
var name = data.name;
if(name != null){
document.getElementById("txbx_name").value = name;
}
});
}
Does some one have an Idea why these lines do not work when creating a new tab?
Many thanks in advance.
Here's a question for you.
After creating a new tab, you access document.getElementById. Yes, but which document?
In your case, it would be the page calling create - which is not the created page.
In your case, it seems like you're opening a page that's part of the extension. Then you should just include code in it that will run on load.
You may want to check document.readyState:
if (document.readyState === "loading") {
document.addEventListener('DOMContentLoaded', getdata);
} else {
getdata();
}
If you're trying to do this with a webpage, you'll need a content script. Again, those normally execute after DOM is parsed - so just call getdata() at top level.
//Gather AJAX links
var ajaxLink = $("#logo, .navLink, .tableLink, .footerLink");
//Mark the recent state as null (because there is none yet)
var recentState = null;
//Initialize the page state based on the URL (bookmarking compatibility)
window.onload = function() {
//If no page state exists, assume the user is at index.html
if (window.location.hash == "") {
window.location.hash = "page=index";
}
//Load the page state based on the URL
loadStateFromURL();
//Keep the page state synchronized (back/forward button compatibility)
setInterval(loadStateFromURL, 500);
//Exit
return;
}
//Use AJAX for certain links
ajaxLink.click(function() {
//Update the URL
window.location.hash = "page=" + $(this).attr("id");
//Load the page state based on the URL
loadStateFromURL();
//Return false or else page will refresh
return false;
});
//Load the page state based on the URL
function loadStateFromURL() {
//If nothing has changed, exit
if (window.location.hash == recentState) {
return;
}
//Mark the recent state
recentState = window.location.hash;
//Go through an array of all AJAX links and check their IDs
for (var i = 0; i < ajaxLink.length; i++) {
//If we find a link's ID that matches the current state, load the relevant content
if ("#page=" + ajaxLink[i].id == window.location.hash) {
//Load contents into article.main
$("article.main").fadeOut(0).load(ajaxLink[i].href, function(response, status, xhr) {
//Show an error if the request fails
if (status == "error") {
$("article.main").load("./404.html");
window.location.hash = "page=404";
}
}).fadeIn(500);
//Update the page title
document.title = "\u2622 My Website Name \u2622 " + ajaxLink[i].text;
document.getElementById("headH2").textContent = ajaxLink[i].text;
//State has been fixed, exit
return;
}
}
}
This code works flawlessly when I run it locally!!!
But when I throw it on the web server my AJAX'd links will refresh the page when I first visit. However, if I use the back button then try the link again (or I'm assuming if the page is already in the browser cache), it will work properly.
I cannot allow this, because when people first visit my page the first link they click on will not operate as intended.
One of things I've also been testing is I'll bookmark my own site with a breadcrumb bookmark (example.com/#page=14) and see if it updates without my page already being in the browser cache. Again, it works on my local machine but not on my web server.
use event.preventDefault()
ajaxLink.click(function(e) {
e.preventDefault();
//Update the URL
window.location.hash = "page=" + $(this).attr("id");
//Load the page state based on the URL
loadStateFromURL();
//Return false or else page will refresh
return false;
});
The issue maybe is that when you are applying your click event to these links, they may not be loaded to the DOM. So the possible solution is to put ajaxLink.click(function() { ... }); part inside window.load event or document.ready event. Since you have used window.load event, you can do something like this.
//Initialize the page state based on the URL (bookmarking compatibility)
window.onload = function() {
//If no page state exists, assume the user is at index.html
if (window.location.hash == "") {
window.location.hash = "page=index";
}
//Load the page state based on the URL
loadStateFromURL();
//Keep the page state synchronized (back/forward button compatibility)
setInterval(loadStateFromURL, 500);
//Use AJAX for certain links
ajaxLink.click(function() {
//Update the URL
window.location.hash = "page=" + $(this).attr("id");
//Load the page state based on the URL
loadStateFromURL();
//Return false or else page will refresh
return false;
});
//Exit
return;
}
Solved my own question, had to continuously parse the AJAX links to stay updated with the DOM as it changes.
First I put the ajaxLink declaration into a function:
//Gather AJAX links
function parseAjaxLinks() {
var ajaxLink = $("#logo, .navLink, .tableLink, .footerLink");
return ajaxLink;
}
Then I had to put the ajaxLink click events into a function:
//Load the page state from an AJAX link click event
function loadStateFromClick() {
//Update the AJAX links
var ajaxLink = parseAjaxLinks();
ajaxLink.click(function() {
//Update the URL
window.location.hash = "page=" + $(this).attr("id");
//Load the page state based on the URL
loadStateFromURL();
//Return false or else page will refresh
return false;
});
}
Then I added a line in my window.onload event to keep my AJAX click events synchronized with the DOM (this adds overhead, but oh well):
//Initialize the page state based on the URL (bookmarking compatibility)
window.onload = function() {
//If no page state exists, assume the user is at index.html
if (window.location.hash == "") {
window.location.hash = "page=index";
recentState = window.location.hash;
}
//Load the page state based on the URL
loadStateFromURL();
//Keep the page state synchronized (back/forward button compatibility)
setInterval(loadStateFromURL, 250);
//Keep AJAX links synchronized (with DOM)
setInterval(loadStateFromClick, 250);
//Exit
return;
}
If you have a keen eye, you saw I had called the new parseAjaxLinks in my new loadStateFromClick function, so I added a line to the top of my loadStateFromURL function to keep the links updated in there as well:
//Load the page state based on the URL
function loadStateFromURL() {
//Update the AJAX links
var ajaxLink = parseAjaxLinks();
...
What I learned from this is the variables which are dependent on the DOM need to be continuously updated. While the DOM is loading, things are unpredictable and kind of sucks. **Drinks beer**
I have a simple site, which slides the content off the page before taking you to the URL. However, I need to be able to determine the slide direction, depending on which page you're currently on.
I need to do this, but I'm not sure how to articulate it properly with jQuery:
if (current page == about.php) {
animate right then go to target URL }
else {
do default behaviour }
At the moment, I have this function which doesn't work well, because it animates in a certain way regardless of which URL you're going to:
function animateLeftAndGo(x) {
$(x).click(function (e) {
e.preventDefault();
var href = this.href;
$('.pop-up').fadeOut(function(){
$('.wrapper').animate({left: "+=150%"}, "slow", function(){
window.location = href;
});
});
});
}
Which is called by:
animateLeftAndGo('a.archive');
How can I set up an if statement that asks if the current URL is about.php?
In JavaScript use the location object: http://www.w3schools.com/jsref/obj_location.asp
if (location.pathname == '') {
}
I have a Google Instant style search script written in jQuery. When a user searches, a URL is created which is something like #search/QUERY/1/. However, when you either reload the page, click a result which goes to a different page or return back from a previous page the search results are no longer there. Why could this be?
My jQuery code is:
$(document).ready(function(){
$("#search").keyup(function(){
var search=$(this).val();
var query=encodeURIComponent(search);
var yt_url='search.php?q='+query+'&category=web';
window.location.hash='search/'+query+'/1/';
document.title=$(this).val()+" - My Search Script";
if(search==''){
window.location.hash='';
document.title='My Search Script';
}
$.ajax({
type:"GET",
url:yt_url,
dataType:"html",
success:function(response){
if(response !=""){
$("#result").html(response);
} else {
$("#result").html("No results were found.");
}
}
});
});
});
$(document).ready(function(){
/// Your original key up here...
// With browser's back you should get your hash back, so you should be
// able to fill in the query value back and simulate key release to trigger search
if(window.location.hash.indexOf('#search/') == 0) {
query = window.location.hash.replace('#search/', '').replace('/1/', '');
$('#search').val(decodeURIComponent(query)).keyup();
}
});
Because its return to the previous state of the page before the search.
Maybe this can be a workaround:
$(document).ready(function(){
/*
your code here
*/
if ($("#search").val() != "")
$("#search").keyup();
});
So it will mimic the behavior of when the user is searching when the page reloads, return back, etc..