I've created a tool that is used with a fairly popular music retailer.
The tool provides an enhanced search feature (transparent last.fm results, no ads, no lameness, nothing creepy) and I've found the most useful and unobtrusive way to display the search is as a toolbar using the much maligned iframe. This allows users to load search without stealing the users focus.
Not being a particularly big fan of iframes I thought it would be trivial to add a “close this frame” (ala Google image search) link allowing users to quickly and easily regain control of their browser.
However unlike google, I doen't know what the location of the content in the iframe is (only where it started, via src).
So now I find myself in world of XSS and all the security related concerns.
Using Javascript I've added 'back' and 'forward' buttons with the history object called from links in the parent (when a user does a search the results load into the iframe, so the back button allows them to go back to the primary site after their done using/perusing the search results).
Is there any way to call the current location within the iframe and reload the page (now sans frames) to that location?
I've checked PHP $GLOBAL/$_SERVER variables to see I might get lucky. I understand that there are security concerns, but I don't see how this particular function would be any different then history.back() where the browser makes the call itself without 'notifying' my parent frame.
I know I can retrieve the src location from the iframe itself, but of course that presumes the user doesn't navigate beyond this page, and if they do they don't mind losing their current location and being redirected back to page called initially by the frame (...ah...).
It almost seems like frames are designed to steal windows with no means for gracefully 'breaking out' and preserving integrity of the users session.
No wonder people hate them. :)
On Google image search when you remove the frame it functions as you currently propose, by returning to the original frame source. For XSS reasons, finding the current location shouldn't be possible, and if it was it would be considered a bug and fixed in subsequent browser patches, so it might not be best to rely on such a quirk if one existed. A way to elegantly break out without revealing location would be nice, though. This might be something you can propose to the HTML5 group.
Not that it would really be all too helpful, but the closest you can come is detecting if someone actually left the original frame source page. When you navigate a frame the history object maintains entries, and if your original history length when the page loads is greater than the history length when someone clicks "Break out of this frame...", then you know they were browsing in the frame.
I appreciate both you (hal10001) and Zach taking the time to answer. It appears I'm suck (as I suspected) because of the xss security concerns.
I suppose I could simply wrap all the content within a php based proxy, but that would clearly entering the creepy zone, not to mention the added latency and what-not.
I'll keep spinning the idea incase I do come across something sane and usable, but until then I guess I'll just use the slightly less freindly approach of promoting the original frame and wiping out their current location (and if they don't like I can revisit the iframe/toolbar situation.
Thanks again!
Related
A common question I've seen is "How do you detect if a user is leaving a web page?". I've reviewed the answers for almost all of these questions, but they often don't provide visibility into the initiator of that redirect.
I'm trying to tell from a Chrome extension where if a tab changes pages:
Did the user use the address bar to change pages?
Did javascript on the web page cause the page change?
Did the user interact with an HTML component which caused the page change (i.e form or anchor)?
Did the user simply close the tab?
I might be missing some other variations, feel free to include additional ones in your answer. What I'm really after is: "Was the page change intended by the website, or did the user arbitrarily change the page?".
Some of the questions I listed are not directly possible. For example, my first question about if the address bar changed has been addressed before with no solution. A valid solution to this would be through process of elimination. If you could prove that question #2-#4 were not the case, then it would only leave #1 as the option.
#2 is another interesting one because there are many ways to change the page through javascript. You could set window.location.href, you could use window.navigate, you could use window.history.* (back, go), etc. Chrome DevTools is able to trace back most javascript redirects though, maybe that's an angle.
#3 similar to the problems with #2, there are problems with the number of elements that can cause page changes. Also, any solution would need to work with dynamically generated content as well (i.e $([some selector]).on(...).
#4 is solvable via Chrome's tabs API.
If you have cross-browser approaches, that'd be a plus. Thanks.
I am working on an application that asks individuals to paste links to their social media and other relevant sites as part of the setup process. These later get turned into links on a compiled HTML page.
We are seeing a user issue that is arduous to deal with. When setting up the account on mobile, individuals do not understand that they need the website link to these various places, they try and open the target sites app instead (Facebook as an example), or they stare blankly unsure of what to put in the box or how to proceed.
I'm looking for a solution to this conundrum to help people achieve their ultimate end goal of putting the link on the resultant page.
I tried an iframe approach, whereby the thought was to load the target site, allow the user to navigate to the desired page, and hit a done button. Obviously this approach ran into several road blocks due to security restrictions and frame busting implemented by the various sites.
What would be great would be something like the file input, but for websites instead. We have the "URL" input; however, that only checks the URL for validity, I'm more interested in an approach that would open something akin to the file dialog, that would allow a user to navigate to a site and hit "done"
I've not been able to find anything that comes close. I'm seeing if someone knows something I do not, or has an idea I haven't yet tried. Ultimately, I'm trying to make it easy for an average consumer to choose the target for a link. (I'm fully aware how easy it is to open a browser tab and copy the link, I am truly supporting consumers who find that somewhat difficult / confusing)
Thank you for your time and consideration
I have a single-page mobile application developed with Backbone and Zepto.
It works correctly with the back/forward buttons in the browser.
When the user navigates to a page, the new content slides in from the right as the old contents slides away to the left (and out of the viewport). I want the same thing to happen if the user presses the "forward" browser button. This all works.
I've got a class that I add to the body element navigate-back that will flip this behaviour, so when the user navigates back with the browser's back button, they see the content sliding back in from the left and the other content sliding into the right. Basically just the opposite of going forward.
I need to detect if the user is navigating backwards so I can invoke the alternate behaviour. I have tried implementing my own history stack, but I've ran into lots of problems where sometimes it marks a forward as a back navigation which ruins the visual cue. It's descended into a kludge of hacks now and probably would only embarrass me if I posted it.
What is the best way to implement my own history stack so I can detect if the user is navigating forward/back in the context of a single-page Backbone mobile application?
I don't know about backbone.js1, but I have helped develop a mobile application which had to implement exactly this behavior in html5, so I should be able go give some good advice:
First of all it's good to know that the history.pushState function exists. The big problem with it though is that it is supported up to android 2.3, but not on android 3 till android 4.0.3. As kiranvj points out correctly this can be solved by using the popular history.js library which provides a polyfill solution for the lack of the history functionality.
Now, getting to your actual problem, the way I implemented the history direction animations was by adding data to the pushState function ( history.pushState(data,title,url) ) with which I identified the logical position of the page. In my application I wasn't only limited to a horizontal bar, but in your case you would keep track of position where any new loaded page get's a position which is one higher then your current page. E.g.
History.pushState({position:History.getState().data.position+1},"Your title","Your URL");
Next, when the window.onstatechange or window.onanchorchange event triggers you observe whether the position is higher or lower than your current page (e.g. by using the history.js History.getState() function which I used above) and depending on this you decide in which direction to move (lower is to the left, and higher is to the right), as is illustrated by the image below:
You will also note that I already assumed on the first page that you have {position:1}, whereas normally the first page will have no state information. The way this can be achieved is by using history.replaceState which replaces the current empty state with a more informative state. Alternatively you can also check for an empty state on any of the previously mentioned events and if it's empty you assume it to be the left most one ({position:1}).
Hope this helps and if you have any additional questions feel free to ask.
Please note that this answer assumes you are using history.js and you would need to listen to slightly different events (such as onpopstate) and use slightly different structures (history rather than History) if you would want to build your own solution.
It is also useful to note that it is possible to build this with your own queue array which gives you a lot more control, but will not work in combination with the browser's back button. This is a big issue with browser sites, however is far easier in case you are building a cordova (a.k.a. phonegap) web application.
1 Just read about it and it appears to do some history handling of its own, which might make it more complex to integrate the technique described above.
If you're working on a true single-page app, why not you set up an array to hold history urls in a js variable (as opposed to relying on something like history.pushState and its support)?
Whenever a new page is navigated to, you can push its url into the array, whenever a "back" button is pressed, you can retrieve the url needed as far back as you want. This will work perfectly as long as you correctly discard urls when the user goes back a few steps and then navigates to a new link.
I've never tried implementing this to be used for page history, but this worked perfectly well for in-page undo-redo logic.
Update:
After further research, the approach above would not work for a page reload as it would be an action occuring outside of history handling available through JS. It would still work for tracking back/forward transitions, but such history will be lost on navigating to a url external to the app or a page refresh. David Mulder's answer seems to lack this limitation by relying on browser-level history that persists outside of the page scope.
I had the same issue when working with Zepto on mobile with single page - multiple views.
Initially I used html5 statechange and onhashchange. It all have some issues in one or other mobile device. Finally I used Zepto history plugin from here https://github.com/browserstate/history.js
It somewhat solved most of the issues. Try it, it will be useful, it handle html4 and html5 features wherever possible.
Use this thing in single page mobile application this will allow to the history and move the user to back.
function onBackKeyDown() {
history.go(-1);
navigator.app.backHistory();
}
Sammy.js's v.6.x branch (the one that relies just on hash changes) is a perfect, simplest, most browser-compatible approach to tracking history. There, history is not tracked at all, as Sammy just watches for hashchange.
Relying on "#/slide/123" allows you to support hard page reloads, and simplifies the work
Peel off the last part (slide number) on each page view, push into global. On new route, see if number is more or less than what is stored in global and do the correct (left or right) animation. If global is undefined, no animation.
We have a problem with IE. On a web page with a form, multiple frames are created via javascript. This is due to some crappy WYSIWYG. The problem is that when the user clicks on the browser "back" button, you'd expect the entire page to go back. This is what happens in FF, Chrome, Opera, Safari, etc. But for IE, you have to click the back button for each frame on the page, even if you don't do anything else but load the page.
Knowing that sometimes +20 frames can be on the same page (many textfields), this is a real pain for users.
The question: how do you make the browser back button work for the main page, regardless of the amount of frames you have on the page ?
Thanks so much !
One way of doing it is to implement clientside routing using hash bang urls.
Every time a page is changed on an iframe the hash part of the parent url is changed to reflect it.
You then have some js that listens for these changes and does what is needed to updated the full page state across frames.
Now when the user pushes the back button, the parents url changes to the hash it was before the last change in the child iframe, and everything is updated accordingly.
Be aware: that this is a non-trivial thing to implement if you have many iframes, and i can't give you a working script that will fix your problem. This is meant for inspiration only.
(following Martin Jespersen response)
Yes, I also thinks that it is only solution. And it is hard to implement, but there is a lot of good libraries to handle # changes.
The lightweight jQuery solution: http://tkyk.github.com/jquery-history-plugin/.
Very complex solution with ExtJS: http://dev.sencha.com/deploy/dev/examples/history/history.html#tab1:subtab1.
And I think, that is not possible to do it realibly cross-browser without javascript.
As is well known, in XHR (aka AJAX) web applications no history for your app is build and clicking the refresh button often moves the user out of his/her current activity. I stumbled upon location.hash (e.g. http://anywhere/index.html#somehashvalue) to circumvent the refresh problem (use location.hash to inform your app of it's current state and use a page load handler to reset that state). It's really nice and simple.
This brought me to thinking about using location.hash to track the history of my app. I don't want to use existing libraries, because they use iframes etc. So here's my nickel and dime: when the application page loads I start this:
setInterval(
function(){
if (location.hash !== appCache.currentHash) {
appCache.currentHash = location.hash;
appCache.history.push(location.hash);
/* ... [load state using the hash value] ... */
return true;
}
return false;
}, 250
);
(appCache is a predefined object containing application variables) The idea is to trigger every action in the application from the hash value. In decent browsers a hash value change adds an entry to the history, in IE (<= 7) it doesn't. In all browsers, navigating back or forward to a page with another hash value doesn't trigger a page refresh. That's where the intervalled function takes over. With the function everytime the hash value change is detected (programmatically, or by clicking back or forward) the app can take appropriate action. The application can keep track of it's own history and I should be able to present history buttons in the application (especially for IE users).
As far as I can tell this works cross browser and there's no cost in terms of memory or processor resources. So my question is: would this be a viable solution to manage the history in XHR-apps? What are the pros and cons?
Update: because I use my homebrew framework, I didn't want to use one of the existing frameworks. To be able to use location.hash in IE and having it in it's history too, I created a simple script (yes, it's needs an iframe) which may be of use to you. I published it on my site, feel free to use/modify/critizise it.
There are 3 issues that tend to get munged together by most solutions:
back button
bookmarkability
refresh button
The window.location.hash based solutions can solve all three for most cases: the value in the hash maps to a state of the application/webpage, so a user can press one of "back"/"forward"/"refresh" and jump to the state now in the hash. They can also bookmark because the value in the address bar has changed. (Note that a hidden iframe is needed for IE related to the hash not affecting the browser's history).
I just wanted to note however that an iframe only solution can be used without monitoring window.location.hash for a very effective solution too.
Google maps is a great example of this. The state captured for each user action is way too large to be placed into window.location.hash (map centroid, search results, satellite vs map view, info windows, etc). So they save state into a form embedded in a hidden iframe. Incidentally this solves the [soft] "refresh" issue too. They solve bookmarkability separately via a "Link to this page" button.
I just thought it's worthing knowing/separating the problem domains you are thinking about.
I think you'll have a tricky time knowing if a user went forward or back.
Say the url starts /myapp#page1 so you start tracking states.
Then the user does something to make the url /myapp#page2
Then the user does something to make the url /myapp#page1 again. Now their history is ambiguous and you won't know what to remove or not.
The history frameworks use iframes to get around the browser inconsistencies you mentioned. You only need to use iframes in the browsers that need them.
Another con is that users will always go for their browsers back button before they will go for your custom back button. I have a feeling the delay on reading the history every 250ms will be noticeable too. Maybe you can do the interval even tighter, but then I don't know if that'll make things perform badly.
I've used yui's history manager, and although it doesn't work perfectly all the time in all browsers (especially ie6), it's been used by a lot of users and developers. The pattern they use is pretty flexible too.
All that stuff is important for supporting the full range of browsers, but hopefully the need for it will go away. IE8 and FF3.6 both introduced support for onhashchange. I imagine that others will follow suit. It would be a good idea to check for the availability of this functionality before using timeouts or iframes, as it is really the nicest solution currently out there - and it even works in IE!