{"id":38,"date":"2009-10-20T22:30:24","date_gmt":"2009-10-20T21:30:24","guid":{"rendered":"http:\/\/blogs.igalia.com\/ltilve\/?p=38"},"modified":"2009-10-20T22:30:24","modified_gmt":"2009-10-20T21:30:24","slug":"javascript-component-scrolls-synchronization","status":"publish","type":"post","link":"https:\/\/blogs.igalia.com\/ltilve\/2009\/10\/20\/javascript-component-scrolls-synchronization\/","title":{"rendered":"Components scroll synchronization with Javascript"},"content":{"rendered":"<p>Management of scrolls in different areas of your page it&#8217;s as easy to attach a listener to the component where the scrolls are shown in order to change the positioning properties of the simulated scrollable attached area when the scrollbars are used. A couple of things to keep in mind are that the javascript-scrolled area should have the same dimensions that the main one in order to handle the same amount of displacement with the scrolls, and depending on the need of explicitly\u00a0 showing the secondary scrollbar or not CSS display property should be set properly.<\/p>\n<p>A very basic example would be just moving this examples here, a couple of simple HTML boxes:<\/p>\n<pre>\n<div>\n   <div class=\"inner-block\">Lorem ipsum...<\/div>\n<\/div>\n<div>\n   <div class=\"inner-block\">Lorem ipsum...<\/div>\n<\/div>\n<\/pre>\n<p> with its asociatted CSS properties:<\/p>\n<pre>\n#scrolled-block {\n   overflow:auto;\n}\n#javascript-scrolled-block {\n   overflow:hidden;\n}\n.content-box {\n   background-color: #EEEEEE;\n   margin:20px;\n   padding:10px;\n   width: 200px;\n   height: 200px;\n   float:left;\n}\n<\/pre>\n<p>This produces the displayed result. Our purpose is to add an action listener to onScroll first component in order to get the second box moved when we scroll the first one:<\/p>\n<div style=\"background-color: #EEEEEE;margin:20px;padding:10px;width: 200px;height: 200px;float:left;overflow:auto\">\n<div class=\"inner-block\">\n<h2>Scrollable box<\/h2>\n<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.<br \/>\nNunc mollis ante ut sapien venenatis porttitor. Aliquam vel justo nec nisi mattis tempus<br \/>\nin eget magna. Sed vitae risus et nulla placerat laoreet. Pellentesque habitant morbi tristique<br \/>\nsenectus et netus et malesuada fames ac turpis egestas. Duis vestibulum velit a sapien bibendum<br \/>\nlaoreet. Nulla nec sapien et orci tincidunt tempor. Proin orci metus, egestas at sodales vitae, sodales vitae erat.<\/p><\/div>\n<\/div>\n<div style=\"background-color: #EEEEEE;margin:20px;padding:10px;width: 200px;height: 200px;float:left;overflow:hidden\">\n<div class=\"inner-block\">\n<h2>Self-scrolled box<\/h2>\n<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.<br \/>\nNunc mollis ante ut sapien venenatis porttitor. Aliquam vel justo nec nisi mattis tempus<br \/>\nin eget magna. Sed vitae risus et nulla placerat laoreet. Pellentesque habitant morbi tristique<br \/>\nsenectus et netus et malesuada fames ac turpis egestas. Duis vestibulum velit a sapien bibendum<br \/>\nlaoreet. Nulla nec sapien et orci tincidunt tempor. Proin orci metus, egestas at sodales vitae, sodales vitae erat.<\/p><\/div>\n<\/div>\n<p>listenToScroll();<br \/>\nfunction listenToScroll() {<br \/>\nscroll.block_ = document.getElementById(&#8216;scrolled-block&#8217;);<br \/>\nscroll.jsblock_ = document.getElementById(&#8216;javascript-scrolled-block&#8217;);<br \/>\n    onScroll = function() {<br \/>\n        scroll.jsblock_.scrollTop = scroll.block_.scrollTop;<br \/>\n    };<br \/>\n    scroll.block_.onscroll = onScroll;<br \/>\n}<\/p>\n<pre>\nlistenToScroll();\n\nfunction listenToScroll() {\n    scroll.block_ = document.getElementById('scrolled-block');\n    scroll.jsblock_ = document.getElementById('javascript-scrolled-block');\n    onScroll = function() {\n        scroll.jsblock_.scrollTop = scroll.block_.scrollTop; \n    };\n    scroll.block_.onscroll = onScroll;\n}\n<\/pre>\n<p>This strategy works fine for simultaneous synchronization but we have checked a different behavior for listeners attached to scrollbars in the different browser engines. In <a href=\"http:\/\/webkit.org\/WebKIT\">WebKIT<\/a> based browsers with its Javascript engines (as Squirrelfish in Epiphany or v8 in Chrome), all listeners are processed when the browser implementation fiscally scrolls the component, so the movement is perfectly synchronized. For Gecko plus Tracemonkey, the effect that can be perceived is that scroll in the first component is slightly delayed. To avoid this, it&#8217;s necessary to scroll both components using Javascript, and creating a third element just with the scrollbar. The result can be checked evolving the previous example.<\/p>\n<div style=\"background-color: #EEEEEE;margin:20px;padding:10px;width: 200px;height: 200px;float:left;overflow:hidden\">\n<div class=\"inner-block\">\n<h2>Self-scrolled box 1<\/h2>\n<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.<br \/>\nNunc mollis ante ut sapien venenatis porttitor. Aliquam vel justo nec nisi mattis tempus<br \/>\nin eget magna. Sed vitae risus et nulla placerat laoreet. Pellentesque habitant morbi tristique<br \/>\nsenectus et netus et malesuada fames ac turpis egestas. Duis vestibulum velit a sapien bibendum<br \/>\nlaoreet. Nulla nec sapien et orci tincidunt tempor. Proin orci metus, egestas at sodales vitae, sodales vitae erat.<br \/>\nAliquam sed lorem a ante vestibulum pharetra. Nullam lobortis facilisis tincidunt. Quisque id libero ante, in aliquet orci. <\/p><\/div>\n<\/div>\n<div style=\"background-color: #EEEEEE;margin:20px;padding:10px;width: 200px;height: 200px;float:left;overflow:hidden\">\n<div class=\"inner-block\">\n<h2>Self-scrolled box 2<\/h2>\n<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.<br \/>\nNunc mollis ante ut sapien venenatis porttitor. Aliquam vel justo nec nisi mattis tempus<br \/>\nin eget magna. Sed vitae risus et nulla placerat laoreet. Pellentesque habitant morbi tristique<br \/>\nsenectus et netus et malesuada fames ac turpis egestas. Duis vestibulum velit a sapien bibendum<br \/>\nlaoreet. Nulla nec sapien et orci tincidunt tempor. Proin orci metus, egestas at sodales vitae, sodales vitae erat.<br \/>\nAliquam sed lorem a ante vestibulum pharetra. Nullam lobortis facilisis tincidunt. Quisque id libero ante, in aliquet orci. <\/p><\/div>\n<\/div>\n<div style=\"background-color: #EEEEEE;overflow:auto;padding-left:0;width:5px;margin:20px;padding:10px;padding-right:0px;height: 200px;float:left\">\n<div class=\"inner-simulated-block\" style=\"height:300px\">\n<\/div>\n<\/div>\n<p>listenToSimulatedScroll();<br \/>\nfunction listenToSimulatedScroll() {<br \/>\n\tscroll.simulatedblock_ = document.getElementById(&#8216;simulated-scrolled-block&#8217;);<br \/>\n\tscroll.jsblock1_ = document.getElementById(&#8216;javascript-scrolled-block-1&#8217;);<br \/>\n\tscroll.jsblock2_ = document.getElementById(&#8216;javascript-scrolled-block-2&#8217;);<br \/>\n    onScroll = function() {<br \/>\n        scroll.jsblock1_.scrollTop = scroll.simulatedblock_.scrollTop;<br \/>\n        scroll.jsblock2_.scrollTop = scroll.simulatedblock_.scrollTop;<br \/>\n    };<br \/>\n    scroll.simulatedblock_.onscroll = onScroll;<br \/>\n}<\/p>\n<pre>\nlistenToSimulatedScroll();\nfunction listenToSimulatedScroll() {\n\tscroll.simulatedblock_ = document.getElementById('simulated-scrolled-block');   \n\tscroll.jsblock1_ = document.getElementById('javascript-scrolled-block-1');\t\n\tscroll.jsblock2_ = document.getElementById('javascript-scrolled-block-2');\t\n    onScroll = function() {\n        scroll.jsblock1_.scrollTop = scroll.simulatedblock_.scrollTop; \n        scroll.jsblock2_.scrollTop = scroll.simulatedblock_.scrollTop; \n    };\n    scroll.simulatedblock_.onscroll = onScroll;\n}\n<\/pre>\n<p>It would be necessary to remove the paddings, and that&#8217;s it.<\/p>\n<pre>\n#javascript-scrolled-block-1, #javascript-scrolled-block-2 {   \n\toverflow:hidden;\n}\n#simulated-scrolled-block {\n    overflow:auto;\n    background-color: #EEEEEE;\n    padding-left:0;\n    width: 5px;\n}\n.inner-simulated-block {\n   height:300px;\n}\n.content-box {\n    background-color: #EEEEEE;\n    overflow:auto;\n    padding-left:0;\n    width: 5px;\t\n    margin:20px;\n    padding:10px;\n    height: 200px;\n    float:left;\n}\n<\/pre>\n<pre>\n<div class=\"content-box\">\n   <div class=\"inner-block\">Lorem ipsum...<\/div>\n<\/div>\n<div class=\"content-box\">\n   <div class=\"inner-block\">Lorem ipsum...<\/div>\n<\/div>\n<div class=\"content-box\">\n   <div class=\"inner-simulated-block\">\n<\/div>\n<\/pre>\n<p>Things get really complicated when it&#8217;s necessary to scroll intersected areas inside a complex layout with the need to stick scrollable components over the main area, in a desktop application style approach, let&#8217;s say for instance a Gantt application layout. In this scenario we have to combine this technique with CSS positioning of related elements, manage its resizing and binding different methods to each dimension scrolling, but the main idea is the same one.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Management of scrolls in different areas of your page it&#8217;s as easy to attach a listener to the component where the scrolls are shown in order to change the positioning properties of the simulated scrollable attached area when the scrollbars &hellip; <a href=\"https:\/\/blogs.igalia.com\/ltilve\/2009\/10\/20\/javascript-component-scrolls-synchronization\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":24,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[9,31],"class_list":["post-38","post","type-post","status-publish","format-standard","hentry","category-javascript","tag-css","tag-javascript"],"_links":{"self":[{"href":"https:\/\/blogs.igalia.com\/ltilve\/wp-json\/wp\/v2\/posts\/38","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.igalia.com\/ltilve\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.igalia.com\/ltilve\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.igalia.com\/ltilve\/wp-json\/wp\/v2\/users\/24"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.igalia.com\/ltilve\/wp-json\/wp\/v2\/comments?post=38"}],"version-history":[{"count":1,"href":"https:\/\/blogs.igalia.com\/ltilve\/wp-json\/wp\/v2\/posts\/38\/revisions"}],"predecessor-version":[{"id":205,"href":"https:\/\/blogs.igalia.com\/ltilve\/wp-json\/wp\/v2\/posts\/38\/revisions\/205"}],"wp:attachment":[{"href":"https:\/\/blogs.igalia.com\/ltilve\/wp-json\/wp\/v2\/media?parent=38"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.igalia.com\/ltilve\/wp-json\/wp\/v2\/categories?post=38"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.igalia.com\/ltilve\/wp-json\/wp\/v2\/tags?post=38"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}