iScroll url hash change support - javascript

This is a known issue for iScroll and it only seems to happen in iOS5 where the menu completely stops working. All my sub links in iScroll are hash anchors. Does anyone have a workaround for this?

The way I handled it was to hijack the anchor links themselves and replace them with scrollToElement calls instead.
// Hijack hash anchors and scroll to them
$('a').click ( function (e) {
var id = $(this).attr('href');
if (id.substr(0,1) == '#') {
e.preventDefault();
setTimeout( function() {
scroller.scrollToElement ( id, 0 );
}, 0);
return false;
} else {
return true;
}
});
This code should only hijack links that begin with #. It then handles the scrollToElement in a setTimeout which fixes some other intermittent bugs. It works well on my end as long as your anchors are properly named with id's. If you are using name attributes instead of id attributes, you'll need to rewrite these.
This code will copy name attributes and put them in the id attribute if it is blank. You probably won't need this, though.
$('a').each (function (i, e) {
var n = $(e).attr('name');
var id = $(e).attr('id');
if ( typeof id == 'undefined' || id === null || id === '') {
$(e).attr('id', n);
}
});

Related

Get query string from href attribute via Javascript

I'm building an interface that uses AJAX with an HTML fallback. I'm setting up all my <a> tags to work without AJAX first, and if Javascript is enabled, each link will have an "onclick" function attached to it that sends the same exact query string to a different page on my server.
My original link will look like this:
<a class="ajax" href="http://example.com/page?key1=value1&key2=value2">Link</a>
How do I retrieve "key1=value1&key2=value2" as a string from the above href link via Javascript? I will be making AJAX requests that look like http://example.com/ajax?key1=value1&key2=value2.
You can attach a click handler either to individual links:
var links = document.getElementsByTagName('a');
var index;
for (index = 0; index < links.length; ++index) {
links.onclick = linkClickHandler;
}
function linkClickHandler() {
var x = this.href.indexOf('?');
var query = x >= 0 ? this.href.substring(x + 1) : null;
if (query) {
// Do the ajax thing...
// (your code here)
// ...and prevent the link from being followed
return false;
}
}
...or (and this is probably better) to document itself:
document.onclick = function(e) {
var target, x, query;
e = e || window.event;
target = e.target;
if (target.tagName.toUpperCase() === "A") {
x = target.indexOf('?');
query = x >= 0 ? target.substring(x + 1) : null;
if (query) {
// Do the ajax thing...
// (your code here)
// ...and prevent the link from being followed
return false;
}
}
};
In either case, on modern browsers you might want to use addEventListener rather than onclick, and call preventDefault on the event object. But IE8 still uses attachEvent rather than addEventListener.
(return false; from an old-fashioned DOM0 event handler like onclick prevents the default action of the event; details.)
tl;dr
Looking at your comments on other answers, this is what you need
linkElement.search.substr(1)
The answer...
You can access the same properties you would with window.location.
For querystring of a href it would be (document.querySelector('a#mylink')).search
Other accessible properties
.hash
.host
.hostname
.href
.origin
.pathname
.port
.protocol
.search
In your case, for all the links on a page use this little script
*I am only selecting links with actual hrefs.
[].forEach.call(document.querySelectorAll('a[href]'), function(el) {
var queryString = el.search.substr(1)
el.onclick = function(e){
e.preventDefault() // don't redirect and stuff...
// do the magic here with the queryString
}
})
This sample code should be enough to help you parse what you need. Note that I added an id to the anchor to make it easy to access.
<!DOCTYPE html>
<HTML>
<HEAD>
<SCRIPT type="text/javascript">
function parse() {
var el = document.getElementById("foo");
var href = el.href;
var pos = href.indexOf("?");
alert(href.substring(pos+1));
}
</SCRIPT>
</HEAD>
<BODY bgcolor="white" onLoad="parse()">
<a id="foo" class="ajax" href="http://example.com/page?key1=value1&key2=value2">Link</a>
</BODY>
</HTML>

prevent default behaviour of a 'hashchange' event

I am using the jQuery $(window).on('hashchange') method to hide irrelevant sections except the one that the URI-hash points to. It works beautifully, back button and everything. Except for the fact that I can't seem to prevent the default behaviour of the browsers, that is to scroll down to the section with the matching id.
Here is my function.
var AddHashNav = function (hashmatch, container) {
$(window).on('hashchange', function (e) {
if ( !window.location.hash ) {
// empty hash, show only the default header
change_preview(container, container + ' > header');
return false;
}
// Don't do anything to hash links who's ids don't match
else if ( window.location.hash.match(hashmatch) ) {
change_preview(container, window.location.hash);
return false;
}
});
}
var changePreview = function (container, preview) {
$(container + ' >').addClass('hidden');
$(preview).removeClass('hidden');
}
And the caller is simply
$(document).ready(function () {
// applay AddHashNav to all sections who's id ends in 'preview'
AddHashNav(/preview?/, '.hash-nav-container');
});
I've tried both e.preventDefault(); and return false; and neither seem to work.
Note that I'm trying to prevent the behaviour of the hashChange event, not the click event, here it seems not to be possible, but I'm sure at least somebody has managed to figure out how to do this.
I fixed it by running the change_preview() on the first call of the AddHashNav constructor, and therefor hiding the sections on the $(document).load() call.
var AddHashNav = function (hashmatch, container) {
// hide all sections except header on load
change_preview()
$(window).on('hashchange', function (e) {
if ( !window.location.hash ) {
// empty hash, show only the default header
change_preview(container, container + ' > header');
return false;
}
// Don't do anything to hash links who's ids don't match
else if ( window.location.hash.match(hashmatch) ) {
change_preview(container, window.location.hash);
return false;
}
});
}
It is not the most beautiful solution. I'm not sure why it works (I assume it is because the sections have no position in the window object, and hence can't be scrolled into), but it works for now.

Prevent default behavior when setting location.hash

When I do this,
location.hash = "test"
the url is updated and the page homes in on the element with that id.
Is there a way to stop the page from homing in to that element?
Solution
You cannot prevent this behaviour, but you can fool it by temporarily hiding the target, eg. like that (it has nothing to do with jQuery):
// obtain "target" DOM (not jQuery) element and do this:
var original_id = target.id; // save original ID
target.id = null; // set target's ID
location.hash = 'test'; // issue location.hash change
target.id = original_id; // set ID to original value
Generalized solution
Or more general example:
// make target_hash set to a hash you want to not lead you to page section and:
var element = document.getElementById(target_hash); // find element by ID
var original_id = element.id; // save original ID
location.hash = target_hash; // previously defined by you (eg. 'test')
element.id = original_id; // reset ID
Demo / proof
The live example can be as follows, in the event handler attached through jQuery (demo here: http://jsfiddle.net/DaZfH/):
some_link.on('click', function(event){
event.preventDefault();
var target = document.getElementById('target');
var original_id = target.id;
target.id = null; // unset ID
location.hash = 'target';
target.id = original_id;
});
Disclaimer
But indeed others are right: moving you to the correct place in the document is the correct behaviour. If you are doing things like I mentioned, then your solution is pretty hakish and there is definitely a better way to do that.
Is there a way to stop the page from homing in to that element
Yes. Although the hashchange event is not cancelable, you can reset its unwanted behavior like this.
var popY,
popX;
//When location.hash is changed, popstate is the first event to fire
//Use this event to record current state
window.addEventListener('popstate', popstateHandler);
function popstateHandler() {
popY = window.scrollY;
popX = window.scrollX;
}
//Reset scroll position with recorded values when hashchange fires (after page is scrolled)
window.addEventListener('hashchange', hashchangeHandler);
function hashchangeHandler() {
window.scrollTo(popX, popY);
}
That's the basis of it. You might want to do some proofing for IE, and implement your reasons for doing this: Animate scroll, active something etc..
Polyfill for scrollY, scrollX:
if(!('scrollY' in window)) {
Object.defineProperty(window, 'scrollY', {
get: function () {
return window.pageYOffset
}
});
Object.defineProperty(window, 'scrollX', {
get: function () {
return window.pageXOffset
}
})
}

Smooth Scrolling Interferes with Hash Mark

EDIT (12/26/2012)
I found the following code which does exactly what I want, except now when a page's URL has a trailing slash (e.g. example.com/page/), the page doesn't scroll. Works fine if the page's URL ends with '.php' or '.html', etc. Any thoughts on how to get the following script to work with the trailing slash in a URL?
jQuery('a[href*=#]').bind('click', function(e) {
// Get the target
var target = jQuery(this).attr("href");
// prevent the "normal" behaviour which would be a "hard" jump
e.preventDefault();
// perform animated scrolling by getting top-position of target-
// element and set it as scroll target
jQuery('html, body').stop().animate({
scrollTop: jQuery(target).offset().top
}, 500, function() {
location.hash = target; //attach the hash (#jumptarget) to the pageurl
});
return false;
});
I've been using a script successfully for the last couple of years, but have recently run into some issues with it. Basically what the script does is scroll the page to a specific point. This happens with link anchors. For example, if one link is:
anchor link
The page will smoothly scroll to that anchor on the page:
<a name="anchor"></a>
Or:
<a id="anchor"></a>
The issue that occurs arises when some other JS is being used in the page which requires a link to be formatted as such:
other link
When this "other link" is clicked, the page will smoothly scroll, BUT to the top or bottom of the page where there is NO anchor.
What should happen when this "other link" is clicked? The other JS action should occur (which it does), but the smooth page scrolling script should not occur.
Here's a working example from where I got this script:
http://www.dezinerfolio.com/wp-content/uploads/smoothscrolldemo/df_smooth_scroll.html
Here's the JS in full:
Scroller = {
// control the speed of the scroller.
// dont change it here directly, please use Scroller.speed=50;
speed: 10,
// returns the Y position of the div
gy: function (d) {
gy = d.offsetTop
if (d.offsetParent) while (d = d.offsetParent) gy += d.offsetTop
return gy
},
// returns the current scroll position
scrollTop: function (){
body = document.body
d = document.documentElement
if (body && body.scrollTop) return body.scrollTop
if (d && d.scrollTop) return d.scrollTop
if (window.pageYOffset) return window.pageYOffset
return 0
},
// attach an event for an element
// (element, type, function)
add: function(event, body, d) {
if (event.addEventListener) return event.addEventListener(body, d,false)
if (event.attachEvent) return event.attachEvent('on'+body, d)
},
// kill an event of an element
end: function(e){
if (window.event) {
window.event.cancelBubble = true
window.event.returnValue = false
return;
}
if (e.preventDefault && e.stopPropagation) {
e.preventDefault()
e.stopPropagation()
}
},
// move the scroll bar to the particular div.
scroll: function(d){
i = window.innerHeight || document.documentElement.clientHeight;
h = document.body.scrollHeight;
a = Scroller.scrollTop()
if (d>a)
if(h-d>i)
a += Math.ceil((d-a)/Scroller.speed)
else
a += Math.ceil((d-a-(h-d))/Scroller.speed)
else
a = a + (d-a)/Scroller.speed;
window.scrollTo(0,a)
if (a==d || Scroller.offsetTop==a)
clearInterval(Scroller.interval)
Scroller.offsetTop = a
},
// initializer that adds the renderer to the onload function of the window
init: function(){
Scroller.add(window,'load', Scroller.render)
},
// this method extracts all the anchors and validates then as # and attaches the events.
render: function(){
a = document.getElementsByTagName('a');
Scroller.end(this);
window.onscroll
for (i=0;i<a.length;i++) {
l = a[i];
if (l.href && l.href.indexOf('#') != -1 && ((l.pathname==location.pathname) || ('/'+l.pathname==location.pathname)) ){
Scroller.add(l,'click',Scroller.end)
l.onclick = function(){
Scroller.end(this);
l = this.hash.substr(1);
a = document.getElementsByTagName('a');
for (i=0;i<a.length;i++) {
if (a[i].name == l){
clearInterval(Scroller.interval);
Scroller.interval = setInterval('Scroller.scroll('+Scroller.gy(a[i])+')',10);
}
}
}
}
}
}
}
// invoke the initializer of the scroller
Scroller.init();
I would think that there is a way to write the script so that if the href is equal to just the hash mark # without any text after the hash, that the scroller wouldn't be triggered.
Does anyone have any better ideas?
Thanks in advance!
I can't help you with your jQuery function, but there are two simple solutions to your original script. The first is a small modification to tell the script to ignore the special case where an anchor's URL is only the hash tag.
In the render function, change the line:
if (l.href
&& l.href.indexOf('#') != -1
&& (l.pathname == location.pathname
|| '/' + l.pathname == location.pathname)
) {
To:
if (l.href
&& l.href != '#' // <<< Added this conditional >>>
&& l.href.indexOf('#') != -1
&& (l.pathname == location.pathname
|| '/' + l.pathname == location.pathname)
){
This will tell the script to ignore the special case, but won't prevent the browser from reacting normally to the link, so the browser may still jump to the top of the page. The special case you've mentioned is almost always used in javascript constructions to provide an anchor tag with an href attribute, because some older browsers would ignore the tag without one. The '#' was used as the URL to prevent the link from leaving the page.
Instead of the '#', you could use an empty javascript call in your link, like so:
other link
This will avoid your issues with the scrollers completely.
Thanks again, Jarred, for your help! I did come across a script that does just what I want. Here's the better script I found:
jQuery('a[href*=#]').bind('click', function(e) {
e.preventDefault(); //prevent the "normal" behaviour which would be a "hard" jump
var target = jQuery(this).attr("href"); //Get the target
// perform animated scrolling by getting top-position of target-element and set it as scroll target
jQuery('html, body').stop().animate({ scrollTop: jQuery(target).offset().top }, 1000, function() {
location.hash = target; //attach the hash (#jumptarget) to the pageurl
});
return false;
});

Preventing blur when user clicks on specific div not working in Firefox

I am using jquery to keep the focus on a text box when you click on a specific div. It works well in Internet Explorer but not in Firefox. Any suggestions?
var clickedDiv = false;
$('input').blur(function() { if (clickedDiv) { $('input').focus(); } });
$('div').mousedown(function() { clickedDiv = true; })
.mouseup(function() { clickedDiv = false });
Point to note: the focus() method on a jquery object does not actually focus it: it just cases the focus handler to be invoked! to actually focus the item, you should do this:
var clickedDiv = false;
$('input').blur( function() {
if(clickeddiv) {
$('input').each(function(){this[0].focus()});
}
}
$('div').mousedown(function() { clickedDiv = true; })
.mouseup(function() { clickedDiv = false });
Note that I've used the focus() method on native DOM objects, not jquery objects.
This is a direct (brute force) change to your exact code. However, if I understand what you are trying to do correctly, you are trying to focus an input box when a particular div is clicked when that input is in focus.
Here's my take on how you would do it:
var inFocus = false;
$('#myinput').focus(function() { inFocus = true; })
.blur(function() { inFocus = false; });
$('#mydiv').mousedown(function() {
if( inFocus )
setTimeout( function(){ $('#myinput')[0].focus(); }, 100 );
}
Point to note: I've given a timeout to focussing the input in question, so that the input can actually go out of focus in the mean time. Otherwise we would be giving it focus just before it is about to lose it. As for the decision of 100 ms, its really a fluke here.
Cheers,
jrh
EDIT in response to #Jim's comment
The first method probably did not work because it was the wrong approach to start with.
As for the second question, we should use .focus() on the native DOM object and not on the jQuery wrapper around it because the native .focus() method causes the object to actually grab focus, while the jquery method just calls the event handler associated with the focus event.
So while the jquery method calls the focus event handler, the native method actually grants focus, hence causing the handler to be invoked. It is just unfortunate nomenclature that the name of this method overlaps.
I resolved it by simply replace on blur event by document.onclick and check clicked element if not input or div
var $con = null; //the input object
var $inp = null; // the div object
function bodyClick(eleId){
if (eleId == null || ($inp!= null && $con != null && eleId != $inp.attr('id') &&
eleId != $con.attr('id'))){
$con.hide();
}
}
function hideCon() {
if(clickedDiv){
$con.hide();
}
}
function getEl(){
var ev = arguments[0] || window.event,
origEl = ev.target || ev.srcElement;
eleId = origEl.id;
bodyClick(eleId);
}
document.onclick = getEl;
hope u find it useful

Categories