I'm using the hash to load content dynamically. To make the back button work I am capturing hash changes. However sometimes I need to change the hash without triggering the hash changed function (eg, when the page was redirected server side and I need to update the hash once the content has returned.)
The best solution I have come up with is to unbind the hashchange event, make the change and then rebind it. However, as this happens asynchronously, I am finding that it rebinds too quickly and still catches the hash change.
My solution at the moment is very poor: Rebinding in a setTimeout. Does anyone have a better idea?
$(window).unbind( 'hashchange', hashChanged);
window.location.hash = "!" + url;
setTimeout(function(){
$(window).bind( 'hashchange', hashChanged);
}, 100);
Edit:
Amir Raminfar's suggestion prompted me to a solution that does not require a timeout.
I added a class variable
_ignoreHashChange = false;
When I want to change the hash silently I do this:
_ignoreHashChange = true;
window.location.hash = "!" + url;
and the hash changed event does this :
function hashChanged(event){
if(_ignoreHashChange === false){
url = window.location.hash.slice(2);
fetchContent(url);
}
_ignoreHashChange = false;
}
You could use history.replaceState and append the hash, to replace the current URI without triggering the hashchange event:
var newHash = 'test';
history.replaceState(null, null, document.location.pathname + '#' + newHash);
JSFiddle example
You can have a function like this:
function updateHash(newHash){
...
oldHash = newHash
}
then in your setTimeOut you need to do
function(){
if(oldHash != currenHash){
updateHash(currenHash);
}
}
So now you can call update hash manually and it won't be triggered by the event. You can also have more parameters in updateHash to do other things.
By the way, have you looked at the jquery history plugin? http://tkyk.github.com/jquery-history-plugin/
Related
I am trying to call JavaScript function when # is present in URL. I know normal behavior is to navigate / scroll to the specific tag. But could not find how to invoke a JavaScript function.
The below example is close but not solving my problem.
What is the meaning of # in URL and how can I use that?
You might be able to leverage the hashchange event to trigger the function, assuming you don't just want to keep polling the location to see if it changes.
DOCS: https://developer.mozilla.org/en-US/docs/Web/API/Window/hashchange_event
This code snippet will add the listener to the current page, then manipulate the hash and fire the function, displaying the new hash value. You could call any function here.
window.addEventListener('hashchange', function() {
alert(location.hash);
});
window.location += "#test";
var hash = window.location.hash;
if(hash){
// do something
}
<script>
if (window.location.href.includes('#')) {
// run your code here
}
</script>
use a location.hash function will solve your problem
var hash = window.location.hash.replace(/#/g, '');
if(hash){
// found a hash
console.log("heyy I found a hash")'
}
else{
// did not find a hash
console.log("Uh oh")
/*
try using :
window.location = window.location + '#' + "some random variable"
to create a new URL and every time the page loads find the hash and
display the wanted data.
*/
}
PS: this only works if your URL is like example.com/#xyz
then it will give you xyz as a console output. This may sound
vague but if you do this you may get a Idea
i'm working with History.js and i'm trying to obtain an url as Google Plus urls.
(function(window, undefined){
var History = window.History; // Note: We are using a capital H instead of a lower h
if ( !History.enabled ) {
// History.js is disabled for this browser.
// This is because we can optionally choose to support HTML4 browsers or not.
return false;
}
History.Adapter.bind(window, 'statechange', function(){ // Note: We are using statechange instead of popstate
var State = History.getState(); // Note: We are using History.getState() instead of event.state
History.log(State.data, State.title, State.url);
console.log(State.data.page);
});
$('.item').live('click', function(e){
e.preventDefault();
var url = $(this).children(1).attr('href');
$(document).remove('#content');
$('#loaded').load('/controlpanel' + url + '.php #content');
History.pushState({page: url + '.php'}, "Prova Pagina", History.getRootUrl() + 'controlpanel' + url); // logs {state:1}, "State 1", "?state=1"
});
})(window);
this script works when I click on the link, but when I manually refresh the page, and I've already clicked on a link and the url becomes http://www.mysite.com/something/page, the browser gives me an 404 Error.
How can I solve it?
I would like to obtain something like: https://plus.google.com/explore
Even though JS allows you to not reload the whole page, the URL should still be accessible on the server side, so that someone accessing directly the link will see something (not to mention graceful degradation).
Also, you have to add a click handler on the anchor links, so that you can prevent the default action (going to the link) and use the pushState method to change the URL. Something like this:
document.body.onclick = function( e ) {
var evt = e || window.event,
target = evt.target || evt.srcElement // Get the target cross-browser
// If the element clicked is a link
if ( target.nodeName === 'A' ) {
// Use the History API to change the URL
History.pushState() // Use the library correctly
// Don't forget to return false so that the link doesn't make you reload the page
return false
}
}
This is definitely not production code (you should check for external links, not bind on the body, etc), but you get the idea.
hi this all started when i ran a function (lets call it loadround) that altered the innerHTML of an iframe. now once loadframe was loaded there were links in the iframe that once clicked would change the iframe page. the only problem is when i click the back button the loadround page was gone. i've thought about this numerous times to no avail. so i tried this code.
loadround
then
function loadround(a,b){
window.location.hash = "#loadround('"+a+"','"+b+"')";
var code = "<(h2)>"+a+"</(h2)><(h2)>"+b+"</(h2)>"
var iFrame = document.getElementById('iframe');
var iFrameBody;
iFrameBody = iFrame.contentDocument.getElementsByTagName('body')[0]
iFrameBody.innerHTML = code;
}
(the brackets in the h2 are intentional)
then i would try to reload the function by possibly an onload function but for now i was testing with a simple href as followed.
function check(){
var func = location.hash.replace(/#/, '')
void(func);
}
check
unfortunately the check code doesn't work and im almost certain there is an easier way of doing this. i tried changing the src of the iframe instead of the innerhtml and there was the same problem. thanks in advance
The modern browsers are starting to support the event window.onhashchange
In the meantime you can use the workaround proposed by Lekensteyn or maybe you can find something useful here: JavaScript/jQuery - onhashchange event workaround
You are misunderstanding the function void, which just make sure the return value is undefined. That prevents the browser from navigating away when you put it in a link. You can test that yourself by pasting the next addresses in your browser:
javascript:1 // note: return value 1, browser will print "1" on screen
javascript:void(1) // note: undefined return value, browser won't navigate away
It's strongly discouraged to execute the hash part as Javascript, as it's vulnerable to XSS without proper validating it. You should watch the hash part, and on modification, do something.
An example; watch every 50 milliseconds for modifications in the hash part, and insert in a element with ID targetElement an heading with the hash part. If the hash part is not valid, replace the current entry with home.
var oldHash = '';
function watchHash(){
// strip the first character (#) from location.hash
var newHash = location.hash.substr(1);
if (oldHash != newHash) {
// assume that the parameter are alphanumeric characters or digits
var validated = newHash.match(/^(\w+)$/);
// make sure the hash is valid
if (validated) {
// usually, you would do a HTTP request and use the parameter
var code = "<h1>" + validated[1] + "</h1>";
var element = document.getElementById("targetElement");
element.innerHTML = code;
} else {
// invalid hash, redirect to #home, without creating a new history entry
location.replace("#home");
}
// and set the new state
oldHash = newHash;
}
}
// periodically (every 50 ms) watch for modification in the hash part
setInterval(watchHash, 50);
HTML code:
Home
About Me
Contact
<div id="targetElement">
<!-- HTML will be inserted here -->
</div>
I've got a jQuery routine I need to convert to MooTools, but I can't get it to work. Here's my jQuery version:
$(".google-analytics-link").click(function () {
var href = $(this).attr("href");
pageTracker._link(href);
location.href = href;
return false;
});
Here's my MooTools translation:
$$(".google-analytics-link").addEvent("click", function () {
var href = this.get("href");
pageTracker._link(href);
location.href = href;
return false;
});
Doesn't seem to work though. I don't understand MooTools selectors. Any help, please?
You don't need to explicitly set the window's location when clicking the link already does it. Currently the code stops the native event, calls a method on the pageTracker object, then redirects to the location of the clicked link.
Google Analytics documentation for the _link method says that
This method works in conjunction with the _setDomainName() and _setAllowLinker() methods to enable cross-domain user tracking. The _link() method passes the cookies from this site to another via URL parameters (HTTP GET). It also changes the document.location and redirects the user to the new URL.
implying that you simply have to stop the click event, and call the _link method which will take care of the rest.
var analyticsLinks = document.getElements('.google-analytics-link');
analyticsLinks.addEvent('click', function(event) {
// stop the page from navigating away
event.stop();
var href = this.get('href');
// let the Analytics API do its work, and then redirect to this link
pageTracker._link(href);
});
$$(".google-analytics-link").each(function (e) {
e.addEvent("click", function () {
var href = this.get("href");
pageTracker._link(href);
location.href = href;
return false;
});
});
I just set up my new homepage at http://ritter.vg. I'm using jQuery, but very minimally.
It loads all the pages using AJAX - I have it set up to allow bookmarking by detecting the hash in the URL.
//general functions
function getUrl(u) {
return u + '.html';
}
function loadURL(u) {
$.get(getUrl(u), function(r){
$('#main').html(r);
}
);
}
//allows bookmarking
var hash = new String(document.location).indexOf("#");
if(hash > 0)
{
page = new String(document.location).substring(hash + 1);
if(page.length > 1)
loadURL(page);
else
loadURL('news');
}
else
loadURL('news');
But I can't get the back and forward buttons to work.
Is there a way to detect when the back button has been pressed (or detect when the hash changes) without using a setInterval loop? When I tried those with .2 and 1 second timeouts, it pegged my CPU.
The answers here are all quite old.
In the HTML5 world, you should the use onpopstate event.
window.onpopstate = function(event)
{
alert("location: " + document.location + ", state: " + JSON.stringify(event.state));
};
Or:
window.addEventListener('popstate', function(event)
{
alert("location: " + document.location + ", state: " + JSON.stringify(event.state));
});
The latter snippet allows multiple event handlers to exist, whereas the former will replace any existing handler which may cause hard-to-find bugs.
Use the jQuery hashchange event plugin instead. Regarding your full ajax navigation, try to have SEO friendly ajax. Otherwise your pages shown nothing in browsers with JavaScript limitations.
jQuery BBQ (Back Button & Query Library)
A high quality hash-based browser history plugin and very much up-to-date (Jan 26, 2010) as of this writing (jQuery 1.4.1).
HTML5 has included a much better solution than using hashchange which is the HTML5 State Management APIs - https://developer.mozilla.org/en/DOM/Manipulating_the_browser_history - they allow you to change the url of the page, without needing to use hashes!
Though the HTML5 State Functionality is only available to HTML5 Browsers. So you probably want to use something like History.js which provides a backwards compatible experience to HTML4 Browsers (via hashes, but still supports data and titles as well as the replaceState functionality).
You can read more about it here:
https://github.com/browserstate/History.js
Another great implementation is balupton's jQuery History which will use the native onhashchange event if it is supported by the browser, if not it will use an iframe or interval appropriately for the browser to ensure all the expected functionality is successfully emulated. It also provides a nice interface to bind to certain states.
Another project worth noting as well is jQuery Ajaxy which is pretty much an extension for jQuery History to add ajax to the mix. As when you start using ajax with hashes it get's quite complicated!
I do the following, if you want to use it then paste it in some where and set your handler code in locationHashChanged(qs) where commented, and then call changeHashValue(hashQuery) every time you load an ajax request.
Its not a quick-fix answer and there are none, so you will need to think about it and pass sensible hashQuery args (ie a=1&b=2) to changeHashValue(hashQuery) and then cater for each combination of said args in your locationHashChanged(qs) callback ...
// Add code below ...
function locationHashChanged(qs)
{
var q = parseQs(qs);
// ADD SOME CODE HERE TO LOAD YOUR PAGE ELEMS AS PER q !!
// YOU SHOULD CATER FOR EACH hashQuery ATTRS COMBINATION
// THAT IS PASSED TO changeHashValue(hashQuery)
}
// CALL THIS FROM YOUR AJAX LOAD CODE EACH LOAD ...
function changeHashValue(hashQuery)
{
stopHashListener();
hashValue = hashQuery;
location.hash = hashQuery;
startHashListener();
}
// AND DONT WORRY ABOUT ANYTHING BELOW ...
function checkIfHashChanged()
{
var hashQuery = getHashQuery();
if (hashQuery == hashValue)
return;
hashValue = hashQuery;
locationHashChanged(hashQuery);
}
function parseQs(qs)
{
var q = {};
var pairs = qs.split('&');
for (var idx in pairs) {
var arg = pairs[idx].split('=');
q[arg[0]] = arg[1];
}
return q;
}
function startHashListener()
{
hashListener = setInterval(checkIfHashChanged, 1000);
}
function stopHashListener()
{
if (hashListener != null)
clearInterval(hashListener);
hashListener = null;
}
function getHashQuery()
{
return location.hash.replace(/^#/, '');
}
var hashListener = null;
var hashValue = '';//getHashQuery();
startHashListener();
Try simple & lightweight PathJS lib.
Simple example:
Path.map("#/page").to(function(){
alert('page!');
});