{"id":1925,"date":"2013-11-14T19:41:28","date_gmt":"2013-11-14T19:41:28","guid":{"rendered":"http:\/\/www.sitekickr.com\/blog\/?p=1925"},"modified":"2013-11-14T19:41:28","modified_gmt":"2013-11-14T19:41:28","slug":"technique-avoid-jumpy-links","status":"publish","type":"post","link":"https:\/\/www.sitekickr.com\/blog\/technique-avoid-jumpy-links\/","title":{"rendered":"Dynamic Fragment Identifiers &#8211; A New Technique to avoid &#8220;jumpy links&#8221;"},"content":{"rendered":"<p>I found myself working on a user profile management tool this week. This tool had dozens of internal links to other areas of the user profile.<\/p>\n<p>During testing, I became quickly annoyed by the fact that each time I clicked a link to another part of the profile, the page scroll position was &quot;reset&quot; to the top.<\/p>\n<p>Within this tool, I&#39;d ideally like it to maintain the scroll position between page loads.<\/p>\n<p>For the purposes of this article, I&#39;ll call this technique <strong>persistent scroll location<\/strong>.<\/p>\n<p>&nbsp;<\/p>\n<h3>Why not fragment identifiers?<\/h3>\n<p><a href=\"http:\/\/www.sitekickr.com\/blog\/wp-content\/uploads\/2013\/11\/fragment-identifier.png\"><img loading=\"lazy\" align=\"right\" alt=\"fragment-identifier\" border=\"2\" class=\"alignright size-full wp-image-1965\" height=\"128\" src=\"http:\/\/www.sitekickr.com\/blog\/wp-content\/uploads\/2013\/11\/fragment-identifier.png\" style=\"padding:5px;margin-left: 5px;\" width=\"128\" \/><\/a>The traditional way to approach this is to provide an <em>id<\/em> attribute to the element you wish to scroll to, then append a <a href=\"http:\/\/en.wikipedia.org\/wiki\/Fragment_identifier\" target=\"_blank\">fragment identifier<\/a> to the end of the link.<\/p>\n<p>For &quot;one-off&quot; links, this technique is perfect and is exactly what fragment identifiers were meant to do.<\/p>\n<p><strong>There are two problems with fragment identifiers though:<\/strong><\/p>\n<ol>\n<li><strong>They may require additional markup.<\/strong> The <em>id<\/em> attribute may need to be added to an element that didn&#39;t otherwise require it.<\/li>\n<li><strong>Difficult to maintain.<\/strong> If you have 100 links containing the same fragment identifier, they would all need to be changed if the <em>id<\/em> of the referenced element is changed.<\/li>\n<\/ol>\n<p>&nbsp;<\/p>\n<h3>Session Storage might work<\/h3>\n<p><a href=\"http:\/\/www.sitekickr.com\/blog\/wp-content\/uploads\/2013\/11\/session-storage.png\"><img loading=\"lazy\" align=\"right\" alt=\"session-storage\" border=\"2\" class=\"alignright size-full wp-image-1966\" height=\"128\" src=\"http:\/\/www.sitekickr.com\/blog\/wp-content\/uploads\/2013\/11\/session-storage.png\" style=\"padding:5px;margin-left: 5px;\" width=\"128\" \/><\/a>HTML 5 Storage may be the next logical choice. If we create click event handlers on links that require <strong>persistent scroll location<\/strong>, we can store the current scroll position in session storage, then retrieve it on the next page load.<\/p>\n<p><strong>Issues with Session Storage:<\/strong><\/p>\n<ol>\n<li>It&#39;s not available on legacy browsers.<\/li>\n<li>It doesn&#39;t allow us to share a link that includes the scroll location (as fragment identifiers do).<\/li>\n<\/ol>\n<p>&nbsp;<\/p>\n<h3>Passing in the Query String<\/h3>\n<p><a href=\"http:\/\/www.sitekickr.com\/blog\/wp-content\/uploads\/2013\/11\/query-string.png\"><img loading=\"lazy\" align=\"right\" alt=\"query-string\" border=\"2\" class=\"alignright size-full wp-image-1964\" height=\"128\" src=\"http:\/\/www.sitekickr.com\/blog\/wp-content\/uploads\/2013\/11\/query-string.png\" style=\"padding:5px;margin-left: 5px;\" width=\"128\" \/><\/a>My next thought was to hijack the link click event as in the Session Storage technique above, but instead, append a query string parameter to the link which indicated the scroll position.<\/p>\n<p>For example:<\/p>\n<p><kbd>http:\/\/www.sitekickr.com\/?scroll=190<\/kbd><\/p>\n<p><strong>Issues with appending a query string parameter:<\/strong><\/p>\n<ol>\n<li>This might create duplicate content issues from an SEO perspective if too many variations are spread around the web. Fragment identifiers appear to be &quot;SEO-safe&quot;. All of my research indicates that Google ignores the fragment identifier.<\/li>\n<li>A scroll query string parameter would conflict with a document fragment identifier if there was one.<\/li>\n<\/ol>\n<p>&nbsp;<\/p>\n<h3>Dynamic Fragment Identifiers<\/h3>\n<p><a href=\"http:\/\/www.sitekickr.com\/blog\/wp-content\/uploads\/2013\/11\/dynamic-fragment-id.png\"><img loading=\"lazy\" align=\"right\" alt=\"dynamic-fragment-id\" border=\"2\" class=\"alignright size-full wp-image-1963\" height=\"128\" src=\"http:\/\/www.sitekickr.com\/blog\/wp-content\/uploads\/2013\/11\/dynamic-fragment-id.png\" style=\"padding:5px;margin-left: 5px;\" width=\"128\" \/><\/a>There is a simple solution to eliminate all six issues discussed above. We can maintain the positive aspects of fragment identifiers (SEO &amp; link sharing), while removing the negatives (additional markup and maintainability).<\/p>\n<p>I&#39;ll call this technique <strong><em>Dynamic Fragment Identifiers<\/em><\/strong>. The premise is simple:<\/p>\n<ul>\n<li>&quot;Hijack&quot; selected link click events.<\/li>\n<li>When those link click even handlers are called:\n<ul>\n<li>If the link does not already contain a fragment identifier\n<ul>\n<li>Retrieve the current window scroll position<\/li>\n<li>Append the scroll position in a fragment identifier<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<li>On page load, if a scroll position-related fragment identifier is detected:\n<ul>\n<li>Obtain the scroll position from the fragment identifier<\/li>\n<li>Scroll the window to that position<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p>Example: <kbd>http:\/\/www.sitekickr.com\/#sy-350<\/kbd> &nbsp; (meaning: scroll y-axis 350 pixels)<\/p>\n<p>&nbsp;<\/p>\n<h3>The Code<\/h3>\n<p>After writing this post, I realized how simple the implementation would be. So I whipped up a quick jQuery plugin. You can&#39;t go wrong with a 538-byte plugin (I think).<\/p>\n<p><a href=\"http:\/\/www.sitekickr.com\/code\/jquery\/jquery.AntiScroll\/\">http:\/\/www.sitekickr.com\/code\/jquery\/jquery.AntiScroll\/<\/a><\/p>\n<p>On GitHub&nbsp;<a href=\"https:\/\/github.com\/lanasa\/antiscroll\">https:\/\/github.com\/lanasa\/antiscroll<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Maintaining scroll position between page loads is one of those &#8220;silent&#8221; usability enhancements that makes users love using your website without really knowing why. It only makes sense in certain applications and there are a few ways to make it happen.<\/p>\n","protected":false},"author":1,"featured_media":1962,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"amp_status":""},"categories":[5],"tags":[120],"_links":{"self":[{"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/posts\/1925"}],"collection":[{"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/comments?post=1925"}],"version-history":[{"count":15,"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/posts\/1925\/revisions"}],"predecessor-version":[{"id":1968,"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/posts\/1925\/revisions\/1968"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/media\/1962"}],"wp:attachment":[{"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/media?parent=1925"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/categories?post=1925"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sitekickr.com\/blog\/wp-json\/wp\/v2\/tags?post=1925"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}