I have a SammyJS-based single page application running under http://[mydomain]/[myapp]/[subPath]. The server is configured to return the same HTML "startup" page no matter what [subPath] is, i.e. the route is configured with a wildcard for the subpath.
Since I want users to be able to bookmark a particular subpath in the application (e.g., "/orders/123"), I need to force the client to navigate to that subpath once the "startup" page is loaded. What is the best way to do this? I've tried doing window.location.pathname = window.location.pathname after setting up my application, but that just caused an infinite loop of re-navigating to the page.
When creating the SammyJS application, simply pass window.location.pathname to the run() method as follows:
Sammy(function () {
this.get(/* custom route */, function (context) {
// ... handle navigation ...
});
}).run(window.location.pathname); // Voila!
Related
Is there any way to get the previous URL in JavaScript? Something like this:
alert("previous url is: " + window.history.previous.href);
Is there something like that? Or should I just store it in a cookie? I only need to know so I can do transitions from the previous URL to the current URL without anchors and all that.
document.referrer
in many cases will get you the URL of the last page the user visited, if they got to the current page by clicking a link (versus typing directly into the address bar, or I believe in some cases, by submitting a form?). Specified by DOM Level 2. More here.
window.history allows navigation, but not access to URLs in the session for security and privacy reasons. If more detailed URL history was available, then every site you visit could see all the other sites you'd been to.
If you're dealing with state moving around your own site, then it's possibly less fragile and certainly more useful to use one of the normal session management techniques: cookie data, URL params, or server side session info.
If you want to go to the previous page without knowing the url, you could use the new History api.
history.back(); //Go to the previous page
history.forward(); //Go to the next page in the stack
history.go(index); //Where index could be 1, -1, 56, etc.
But you can't manipulate the content of the history stack on browser that doesn't support the HTML5 History API
For more information see the doc
If you are writing a web app or single page application (SPA) where routing takes place in the app/browser rather than a round-trip to the server, you can do the following:
window.history.pushState({ prevUrl: window.location.href }, null, "/new/path/in/your/app")
Then, in your new route, you can do the following to retrieve the previous URL:
window.history.state.prevUrl // your previous url
document.referrer is not the same as the actual URL in all situations.
I have an application where I need to establish a frameset with 2 frames. One frame is known, the other is the page I am linking from. It would seem that document.referrer would be ideal because you would not have to pass the actual file name to the frameset document.
However, if you later change the bottom frame page and then use history.back() it does not load the original page into the bottom frame, instead it reloads document.referrer and as a result the frameset is gone and you are back to the original starting window.
Took me a little while to understand this. So in the history array, document.referrer is not only a URL, it is apparently the referrer window specification as well. At least, that is the best way I can understand it at this time.
<script type="text/javascript">
document.write(document.referrer);
</script>
document.referrer serves your purpose, but it doesn't work for Internet Explorer versions earlier than IE9.
It will work for other popular browsers, like Chrome, Mozilla, Opera, Safari etc.
If anyone is coming from React-world, I ended up solving my use-case using a combination of history-library, useEffect and localStorage
When user selects new project:
function selectProject(customer_id: string, project_id: string){
const projectUrl = `/customer/${customer_id}/project/${project_id}`
localStorage.setItem("selected-project", projectUrl)
history.push(projectUrl)
}
When user comes back from another website. If there's something in localStorage, send him there.
useEffect(() => {
const projectUrl = localStorage.getItem("selected-project")
if (projectUrl) {
history.push(projectUrl)
}
}, [history])
When user has exited a project, empty localStorage
const selectProject = () => {
localStorage.removeItem("selected-project")
history.push("/")
}
I had the same issue on a SPA Single Page App, the easiest way I solved this issue was with local storage as follows:
I stored the url I needed in local storage
useEffect(() => {
const pathname = window.location.href; //this gives me current Url
localstorage.setItem('pageUrl',JSON.stringify(pathname))
}, []);
On the next screen (or few screens later) I fetched the url can replaced it as follows
useEffect(() => {
const pathname = localstorage.getItem('pageUrl');
return pathname ? JSON.parse(pathname) : ''
window.location.href = pathname; //this takes prevUrl from local storage and sets it
}, []);
Those of you using Node.js and Express can set a session cookie that will remember the current page URL, thus allowing you to check the referrer on the next page load. Here's an example that uses the express-session middleware:
//Add me after the express-session middleware
app.use((req, res, next) => {
req.session.referrer = req.protocol + '://' + req.get('host') + req.originalUrl;
next();
});
You can then check for the existance of a referrer cookie like so:
if ( req.session.referrer ) console.log(req.session.referrer);
Do not assume that a referrer cookie always exists with this method as it will not be available on instances where the previous URL was another website, the session was cleaned or was just created (first-time website load).
Wokaround that work even if document.referrer is empty:
let previousUrl = null;
if(document.referrer){
previousUrl = document.referrer;
sessionStorage.setItem("isTrickApplied",false);
}else{
let isTrickApplied= sessionStorage.getItem("isTrickApplied");
if(isTrickApplied){
previousUrl = sessionStorage.getItem("prev");
sessionStorage.setItem("isTrickApplied",false);
}else{
history.back(); //Go to the previous page
sessionStorage.setItem("prev",window.location.href);
sessionStorage.setItem("isTrickApplied",true);
history.forward(); //Go to the next page in the stack
}
}
Is it possible to change the URL for $location in AngularJS within an Electon app but without implicitly loading that URL? The problem is that Electron is loading index.html (and other resources) locally, which is intended. But then of course it also sets $location to the local file system.
Background: The reason why I need $location to point to the server is that there is some existing legacy code (which I must not change) and this code uses e.g. $location.search. So after the Electron app has started I'd need to set the location correctly, so that this legacy code can work.
UPDATE 17.07.2020
Here is the requested example code:
I'm trying to set the location with window.location = "https://example.com?param1=test" so that the AngularJS function $location.search() returns param1=test. The problem is, as mentioned above, that when setting window.location, Electron loads the index.html from that server and replaces the content of the BrowserWindow. But I want to load those resources (index.html, *.js, *.css locally) I also tried:
window.location.href = ...
window.location.assign (...)
window.location.replace (...)
but all of these are reloading the page as well.
I think you'll want to add an event listener for will-navigate. Docs can be found here. The important piece:
[The will-event event will be] emitted when a user or the page wants to start navigation. It can happen when the window.location object is changed or a user clicks a link in the page.
I'd imagine your main.js file will look something like this, it's bare-bones but I hope you get the idea.
const {
app
} = require("electron");
let window;
function createWindow(){
window = new BrowserWindow(){...};
// etc....
}
app.on("ready", createWindow);
// For all BrowserWindows you make, the inner bindings will be applied to each;
// more information for "web-contents-created" is here: https://www.electronjs.org/docs/api/app#event-web-contents-created
app.on("web-contents-created", (event, contents) => {
contents.on("will-redirect", (event, navigationUrl) => {
// prevent the window from changing path via "window.location = '....'"
event.preventDefault();
return;
});
});
FYI: This event listener is mainly used for security reasons, but I don't see why you can't use it in your case.
I read the information on the browser history and I understand that I do not have access to the array of history objects, and even more so I can not delete them. However, my task is to exclude the addition of parameterized URLs to history. I have tabs in my project, their URLs:
/profile,
/profile?menu=1
/profile?menu=2
I use nextjs framework in my project. His navigation code:
public handleOnClickItem(key: string) {
const id = '1';
const href = `/profile?id=${id}&menu=${key}`;
Router.push(href, href, { shallow: true });
// tabs are navigated
Router.onBeforeHistoryChange = (url) => {
console.log('App is changing to before history change: ', url);
};
}
Can I not add this url to history? I get it in console.log. And as I understood from the description of the method, this URL should not yet go down in history.
Any ideas? Thanks!
This should solve it:
https://nextjs.org/docs/api-reference/next/router#routerreplace
Similar to the replace prop in next/link, router.replace will prevent adding a new URL entry into the history stack.
Meanwhile keep in mind shallow routing option is used to trigger/not trigger next.js lifecycle methods (eg. getInitialProps) when doing the routing.
Load your contents via AJAX once you're in the website, and do not keep updated URLs.
The downside to this is that your user's wouldn't be able to bookmark a specific page (no permalinks). In history it will only show the base url without parameters only.
There was a similar post here on SO
I have been working on dynamically generating tvml-templates with very frequently changing content for a tvOS app on Apple TV. Generating the templates works fine, however I have not been able to get the app to update/reload the content of a template when navigating back and forth between views or leaving and reentering the app. Only rebooting seems to reload the tvml template.
Your template will refresh itself automatically whenever you manipulate the TVML within the template document.
If you maintain a reference to the document like so:
var myDoc;
resourceLoader.loadResource(templateURL,
function(resource) {
if (resource) {
myDoc = self.makeDocument(resource);
});
}
you can manipulate the TVML using myDoc and your view will automatically change.
So if your template document includes a "collectionList" and you were to run this code:
//Removes the child elements of the first collectionList
var collectionLists = myDoc.getElementsByTagName("collectionList");
var collectionList = collectionLists.item(0);
while (collectionList.firstChild) {
collectionList.removeChild(collectionList.firstChild);
}
your view would no longer display the UI elements within the collectionList. The view will refresh itself the moment the code is run.
The answer by #shirefriendship pointed my in the right direction (thank you!). As another example, if you wanted to change the text of a single element in a template (such as the description), you would need to use the innerHTML property:
function changeDescription(incomingString) {
console.log("inside the change description function")
if (incomingString) {
var theDescription = myDoc.getElementsByTagName("description").item(0);
theDescription.innerHTML = incomingString;
}
}
This changes the description immediately to the viewer.
If you are using atvjs framework, you can easily create and navigate to dynamic pages which are regenerated while navigating.
ATV.Page.create({
name: 'home',
url: 'path/to/your/api/that/returns/json',
template: your_template_function
});
// navigate to your page
ATV.Navigation.navigate('home');
Set this in the header of your API:
Cache-Control:no-cache
Got it from Apple Docs: https://developer.apple.com/library/tvos/documentation/General/Conceptual/AppleTV_PG/YourFirstAppleTVApp.html
IMPORTANT
When serving JavaScript and XML files from your web server, you often
need to ensure that any changes to your pages are always visible to
the client app. To do this, your server must ensure that the client
does not cache any of the pages. When your server responds to an HTTP
request for a page that should not be cached, the server should
include Cache-Control:no-cache in the HTTP response header.
I'm building a tool that uses AJAX and pushState/replaceState on top of a non-javascript fallback (http://biologos.org/resources/find). Basically, it's a search tool that returns a list of real HTML links (clicking a link takes you out of the tool).
I am using onpopstate so the user can navigate through their query history created by pushState. This event also fires when navigating back from a real link (one NOT created with pushState but by actual browser navigation). I don't want it to fire here.
So here's my question: how can I tell the difference between a onpopstate event coming from a pushState history item, vs one that comes from real navigation?
I want to do something like this:
window.onpopstate = function(event){
if(event.realClick) return;
// otherwise do something
}
I've tried onpopstate handler - ajax back button but with no luck :(
Thanks in advance!
EDIT:
A problem here is the way different browsers handle the onpopstate event. Here's what seems to be happening:
Chrome
Fires onpopstate on both real and virtual events
Actually re-runs javascript (so setting loaded=false will actually test false)
The solution in the above link actually works!
Firefox
Only fires onpopstate on virtual events
Actually re-runs javascript (so setting loaded=false will actually test false)
For the linked solution to actually work, loaded needs to be set true on page load, which breaks Chrome!
Safari
Fires onpopstate on both real and virtual events
Seems to NOT re-run javascript before the event (so loaded will be true if previously set to be true!)
Hopefully I'm just missing something...
You may be able to use history.js. It should give you an API that behaves consistently across all major platforms (though it's possible that it does not address this specific issue; you'll have to try it to find out).
However, in my opinion, the best way to handle this (and other related issues too) is to design your application in such a way that these issues don't matter. Keep track of your application's state yourself, instead of relying exclusively on the state object in the history stack.
Keep track of what page your application is currently showing. Track it in a variable -- separate from window.location. When a navigation event (including popstate) arrives, compare your known current page to the requested next page. Start by figuring out whether or not a page change is actually required. If so, then render the requested page, and call pushState if necessary (only call pushState for "normal" navigation -- never in response to a popstate event).
The same code that handles popstate, should also handle your normal navigation. As far as your application is concerned, there should be no difference (except that normal nav includes a call to pushState, while popstate-driven nav does not).
Here's the basic idea in code (see the live example at jsBin)
// keep track of the current page.
var currentPage = null;
// This function will be called every time a navigation
// is requested, whether the navigation request is due to
// back/forward button, or whether it comes from calling
// the `goTo` function in response to a user's click...
// either way, this function will be called.
//
// The argument `pathToShow` will indicate the pathname of
// the page that is being requested. The var `currentPage`
// will contain the pathname of the currently visible page.
// `currentPage` will be `null` if we're coming in from
// some other site.
//
// Don't call `_renderPage(path)` directly. Instead call
// `goTo(path)` (eg. in response to the user clicking a link
// in your app).
//
function _renderPage(pathToShow) {
if (currentPage === pathToShow) {
// if we're already on the proper page, then do nothing.
// return false to indicate that no actual navigation
// happened.
//
return false;
}
// ...
// your data fetching and page-rendering
// logic goes here
// ...
console.log("renderPage");
console.log(" prev page : " + currentPage);
console.log(" next page : " + pathToShow);
// be sure to update `currentPage`
currentPage = pathToShow;
// return true to indicate that a real navigation
// happened, and should be stored in history stack
// (eg. via pushState - see `function goTo()` below).
return true;
}
// listen for popstate events, so we can handle
// fwd/back buttons...
//
window.addEventListener('popstate', function(evt) {
// ask the app to show the requested page
// this will be a no-op if we're already on the
// proper page.
_renderPage(window.location.pathname);
});
// call this function directly whenever you want to perform
// a navigation (eg. when the user clicks a link or button).
//
function goTo(path) {
// turn `path` into an absolute path, so it will compare
// with `window.location.pathname`. (you probably want
// something a bit more robust here... but this is just
// an example).
//
var basePath, absPath;
if (path[0] === '/') {
absPath = path;
} else {
basePath = window.location.pathname.split('/');
basePath.pop();
basePath = basePath.join('/');
absPath = basePath + '/' + path;
}
// now show that page, and push it onto the history stack.
var changedPages = _renderPage(absPath);
if (changedPages) {
// if renderPage says that a navigation happened, then
// store it on the history stack, so the back/fwd buttons
// will work.
history.pushState({}, document.title, absPath);
}
}
// whenever the javascript is executed (or "re-executed"),
// just render whatever page is indicated in the browser's
// address-bar at this time.
//
_renderPage(window.location.pathname);
If you check out the example on jsBin, you'll see that the _renderPage function is called every time the app requests a transition to a new page -- whether it's due to popstate (eg. back/fwd button), or it's due to calling goTo(page) (eg. a user action of some sort). It's even called when the page first loads.
Your logic, in the _renderPage function can use the value of currentPage to determine "where the request is coming from". If we're coming from an outside site then currentPage will be null, otherwise, it will contain the pathname of the currently visible page.