I am using the history.js plugin here: https://github.com/browserstate/history.js/ for handling HTML5 History in my application. And I want to handle both HTML5 and HTML4 versions (using the hash fallback).
You can see the demo here: http://dev.driz.co.uk/history45/
Question 1
How do I load the correct content based on the hash when a person visits a url with a hash in (hasn't clicked a link on the website) As the server won't understand what the code is and I don't want to have to double the request by checking if the hash exists and then calling the content via AJAX. So for example: http://dev.driz.co.uk/history45/#about.php doesn't load in the about.php content.
Update:
I've seen some examples here: https://gist.github.com/balupton/858093 which seem to cover loading the correct content (even optimised versions). But I'm struggling to get it to work in combination with my HTML5 version. View source: http://dev.driz.co.uk/history45/ AND it only runs when a person clicks a link instead of on page load which is the original issue.
Update 2:
Tried an example here: http://dev.driz.co.uk/history4/#/contact.php to get the content to load in contact.php content using the snippet in the source but doesn't work...? BUT according to the article here: https://github.com/browserstate/history.js/wiki/Intelligent-State-Handling#wiki-why-the-hashbang-is-unnecessary this code SHOULD be AJAX loading in the correct content if it has a hash on it. Any ideas why?
Question 2
How do I force the /#/ on the root and prefix all urls with `#/'
As currently if you try and visit a page via a link in a HTML4 browser, you end up with weird urls like:
http://dev.driz.co.uk/history45/#contact.php and http://dev.driz.co.uk/history45/#./
instead of:
http://dev.driz.co.uk/history45/#/contact.php and http://dev.driz.co.uk/history45/#/
And if I start on an inner page with:
http://dev.driz.co.uk/history45/contact.php and choose a link then I end up with: http://dev.driz.co.uk/history45/contact.php#about.php when what it should be is: http://dev.driz.co.uk/history45/#/about.php
How do I load the correct content based on the hash when a person visits a url with a hash in (hasn't clicked a link on the website) As the server won't understand what the code is
The server won't see the # or anything after it
and I don't want to have to double the request by checking if the hash exists and then calling the content via AJAX.
Your only other option is to redirect (by setting location with JavaScript) to the URL that you would have used if the history API was available.
How do I force the /#/ on the root?
You'd have to redirect to the homepage (again by setting location) if the history API isn't supported.
I take the view that if they are using a browser which doesn't support the history API (which probably means Windows XP and IE 8 these days) then they can get the non-Ajax fallbacks and I never have to deal with the hash hack.
If all you want to do is to force all browsers (both HTML4/5) to use hashes, you'd use change the History.js Options within your init
History.options.html4Mode = true
However, you may also want to consider using the $.address functionality from jQuery which can append any new URL you want with a hash.
Also within the $.address method, you could also determine where to redirect the user based on their init URL if they were accessing the site for the first time:
Say, have a user access a URL with say
http://www.site.com/#/about.php
you could then have a function which listens to any changes to any changes based on the URL changes
$.address.change(fn)
REF: http://www.asual.com/jquery/address/docs/
UPDATE
One thing I've discovered that may apply here is that if you have a click event handler which behaves by pushing a new state, even if you are currently on that same page, History js will append the same url to the end of the existing URL with a hash
e.g.
If on About Page (and your state is "about") and you click the about link -
your URL will change from
http://www.site.com/about
to
http://www.site.com/about#/about
To save yourself the hassle, have an if statement on that button that checks your History State for "about" and if not, pushState "About"
e.g.
$('.about.btn').on('click', function(e){
if(History.getState().data.State != "About") {
e.preventDefault();
History.pushState({"State": "About"}, null, "/about");
}
})
Related
I am working on a Chrome extension, I want to detect when the user has typed a URL. I know about:
chrome.tabs.onUpdated.addListener(eventLisenerObj.onUpdated);
But, it gets called whenever the URL is changed (e.g. when the page is auto reloads, or user clicks on a link, etc.)
I desire to be able to determine that the URL was changed only by the user typing a URL.
You can get this information using the webNavigation.onCommitted(MDN) event. The event listener receives a property transitionType(MDN), which will be different values(MDN) based on the cause of the navigation. Which values you trigger on will depend on exactly what you are desiring. For what you describe, you will probably want 'typed'(MDN), but potentially also 'generated'(MDN), 'keyword'(MDN) and/or 'keyword_generated'(MDN).
The list of possible values is explained on Chrome's History API page (they are listed on the Chrome webNavigation page, but not explained there) (On MDN: TransitionType) (text from the Chrome History API page):
"link"
The user got to this page by clicking a link on another page.
"typed"
The user got this page by typing the URL in the address bar. Also used for other explicit navigation actions. See also generated(MDN), which is used for cases where the user selected a choice that didn't look at all like a URL.
"auto_bookmark"
The user got to this page through a suggestion in the UI — for example, through a menu item.
"auto_subframe"
Subframe navigation. This is any content that is automatically loaded in a non-top-level frame. For example, if a page consists of several frames containing ads, those ad URLs have this transition type. The user may not even realize the content in these pages is a separate frame, and so may not care about the URL (see also manual_subframe(MDN)).
"manual_subframe"
For subframe navigations that are explicitly requested by the user and generate new navigation entries in the back/forward list. An explicitly requested frame is probably more important than an automatically loaded frame because the user probably cares about the fact that the requested frame was loaded.
"generated"
The user got to this page by typing in the address bar and selecting an entry that did not look like a URL. For example, a match might have the URL of a Google search result page, but it might appear to the user as "Search Google for ...". These are not quite the same as typed(MDN) navigations because the user didn't type or see the destination URL. See also keyword(MDN).
"auto_toplevel"
The page was specified in the command line or is the start page.
"form_submit"
The user filled out values in a form and submitted it. Note that in some situations — such as when a form uses script to submit contents — submitting a form does not result in this transition type.
"reload"
The user reloaded the page, either by clicking the reload button or by pressing Enter in the address bar. Session restore and Reopen closed tab use this transition type, too.
"keyword"
The URL was generated from a replaceable keyword other than the default search provider. See also keyword_generated(MDN).
"keyword_generated"
Corresponds to a visit generated for a keyword. See also keyword(MDN).
To differentiate some types of transitions, in addition to the transitionType values, you will also want to look at the TransitionQualifier(MDN). The possible values are (from the Chrome documentation, which are described somewhat differently on MDN):
"client_redirect"
One or more redirects caused by JavaScript or meta refresh tags on the page happened during the navigation.
"server_redirect"
One or more redirects caused by HTTP headers sent from the server happened during the navigation.
"forward_back"
The user used the Forward or Back button to initiate the navigation.
"from_address_bar"
The user initiated the navigation from the address bar (aka Omnibox).
You can have a look at $locationChangeSuccess.
You can get the path like this:
var loc = $location.path();
Then on change of loc you can attach your function.
I'm trying to create a gallery that allow custom url rather than url prefix with hashtag.
For example:
http://www.myportfolio.com/gallery/3
rather than
http://www.myportfolio.com/gallery#3
so far everything is working fine, if I access from http://www.myportfolio.com/gallery I was able to go to the next and previous image with the url updated.
My main issue now is although the url is now dynamic but it still cannot be bookmarked, if I enter http://www.myportfolio.com/gallery/4 to go the 4th image it doesn't work.
Is there a Javascript approach to this or do you need a combination of PHP to redirect the url?
It is possible to use client side JavaScript to handle this, although you'll need to set up the server so that every URL (that isn't for something like an image or script) loads the bootstrap document your SPA runs on. You just need to check location.href when the page loads and then set up the content you want.
That said, doing so is a very bad idea that completely misses the point of using pushState and friends in the first place.
The two points of being able to have a normal URL are that:
Clients where the JavaScript fails still get a useful page
The content for that URL is loaded in the initial page load (so it is available faster)
If you aren't going to take advantage of that, you might as well go back to hashbangs.
I have a section of a site with multiple categories of Widget. There is a menu with each category name. For anybody with Javascript enabled, clicking a category reveals the content of the category within the page. They can click between categories at will, seeing the DOM updated as needed. The url is also updated using the standard hash/hashbang (if we are being Google-friendly). So for somebody who lands on example.com/widgets, they can navigate around to example.com/widgets#one, example.com/widgets#two, example.com/widgets#three etc.
However, to support user agents without Javascript enabled, following one of these category links must load a new page with the category displayed, so for someone without javascript enabled, they would navigate to example.com/widgets/one, example.com/widgets/two, example.com/widgets/three etc.
My question is: What should happen when somebody with Javascript enabled lands on one of these URLS? What should someone with Javascript enabled be presented with when landing on example.com/widgets/one for example? Should they be redirected to example.com/widgets#one?
Please note that I need a single page site experience for anybody with Javascript enabled, but I want a multi-page site for a user agent without JavaScript. Any answer that doesn't address this fact doesn't answer the question. I am not interested in the merits or problems of hashbangs or single-page-sites vs multi-page-sites.
This is how I would structure it:
Use HistoryJS to manage the URL. JS pushstate browsers got full correct URLs and JS non-pushstate browsers got hashed urls. Non-JS users went to the full URL as normal with a page reload.
When a user clicks a link:
If they have JS:
All clicks to other pages are handled by a function that prevents the default action, grabs the HREF and passes the URL to an ajax request and updates the URL at the same time. The http response for that ajax request is then parsed and then loaded into the content area.
Non JS:
Page refreshed as normal and loads the whole document.
When a page loads:
With JS: Attach an event handler to all your links to prevent the default so their href is dealt with via Ajax.
Without JS: Nothing. Allow anchors to work as normal.
I think you should definitely have all of your content accessible via a full, correct URL and being loading it in via ajax then updating the URL to reflect the address where you got your content from. That way, when JS isn't running, you don't have to change anything.
Is that what you mean?
Apparently your question already contains the answer. You say:
I need a single page site experience for anybody with Javascript enabled
and then ask:
What should someone with Javascript enabled be presented with when landing on example.com/widgets/one for example? Should they be redirected to example.com/widgets#one?
I'd say yes, they should be redirected. I don't see any other option, given your requirements (and the fact that information about JavaScript capabilities and the hash fragment of the URL are not available on the server side).
If you can accept relaxing the requirements a bit, I see another option. Remember when the web was crowded with framesets, and we landed on a specific frame via AltaVista (Google wasn't around yet!) search? It was common to see a header saying that page was supposed to be displayed as a frame, and a link to take the user to the frameset version.
You could do something similar: when scripting is available, detect that you're at example.com/widgets/one and add a link to the single-page version. I know that's not ideal, but it's better than nothing, and maybe better than a nasty client-side redirect.
Why should you need to redirect them to a different page. The user arrived at the page looking for an answer. He gets the answer even if he has javascript enabled. It doesn't matter. The user's query has been fulfilled.
But what would happen if the user lands on example.com/widgets#one ? You would need to set up an automatic redirect to example.com/widgets/one in that case. That could be done by checking the if javascript is enabled in the onload event and redirect to the appropriate page.
One way for designing such pages is to design without javascript first.
You can use anchors in the page so:
example.com/widgets#one
Will be a link to the element with id 'one'
Once your page works without javascript, then you add the javascript layer. You can prevent links to be followed by using the event.preventDefault.
(https://developer.mozilla.org/fr/docs/DOM/event.preventDefault), then add the desired javascript functionality.
Bleacherreport has a function on their website that lets you browse between stories with arrow keys. While that is nothing spectacular, I would like to understand how they do so AND change the URL in the address bar in the browser.
It's one thing to load up new content via AJAX, but I've never seen it done alongside refreshing the URL. There is also a slide to the left animation from one content to the next.
example:
http://bleacherreport.com/articles/1295213-in-depth-look-at-the-business-behind-a-holdout
use arrow keys
They aren't really "refreshing" the URL. As you said, they are using AJAX to grab the new content, and then once it is loaded, updating the URL (probably via window.history.pushState) to match what the route for that specific article is (that way if you actually did refresh the page, you'd still be taken to the same content).
You can do this manually (with the aforementioned window.history.pushState), or there are lots of frameworks that handle client-side URL routing, such as Backbone.js and Sammy.js.
I am using unobtrusive javascript to render search results from a get request -- a search.
When a user follows a search result and doesn't like it they need to be able to hit back and get to the same results.
How do I solve this problem?
You can use the HTML5 History API for modern browsers to change the URL every time you change the application state, and you call fallback on changing the URL anchor tag when you want to capture a history state. An example of the former is source code navigation in Github using PJax and Twitter is the example of the latter.