Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

calculated scroll position is wrong #243

Closed
thenewguy opened this issue Aug 24, 2015 · 30 comments
Closed

calculated scroll position is wrong #243

thenewguy opened this issue Aug 24, 2015 · 30 comments

Comments

@thenewguy
Copy link
Contributor

I have encountered an issue where the host page is being scrolled to the wrong offset.

I've attempted to override scrollCallback like the following:

        iFrameResize({
            log: true,
            checkOrigin: false,
            heightCalculationMethod: 'taggedElement',
            autoResize: false,
            enablePublicMethods: true,
            scrollCallback: function (x, y) {
                console.log("[OVERRIDE] overrode scrollCallback");
                window.scrollTo(x, y-50);
                return false;
            }
        })

The issue is that any time the page is manually resized via the contentframe's parentIFrame.size function the scrollCallback override function is bypassed.

I am testing embedding the iframe into a boostrapped page. The problem is that the bootstrap header overlaps the iFrame when it is scrolled to. In this case the header is 50px. So when the host page scrolls to the iFrame it scrolls too far and the header covers some of the iframe.

Is there any way to adjust the coords for all scrolling instead of only scroll methods triggered by the content frame? If not, what is the recommend approach for something like this?

@davidjbradshaw
Copy link
Owner

Can you not just put a 50px margin on the top of the iFrame?

@thenewguy
Copy link
Contributor Author

That was my first thought and I could, but the iframe isn't the only content on the page. So it creates an excessively large gap between content before the iframe and the content in the iframe.

@thenewguy
Copy link
Contributor Author

Are you against a scroll coords transform method? I think that would be the most flexible approach

@davidjbradshaw
Copy link
Owner

Not sure I full understand the issue, do you have a simple test case?

@thenewguy
Copy link
Contributor Author

Something like

        defaults = ...
                         pagePositionTransformCallback           : function(coords){return coords}

        ...


    function getPagePosition (){
        if(null === pagePosition){
            pagePosition = settings[iframeId].pagePositionTransformCallback({
                x: (window.pageXOffset !== undefined) ? window.pageXOffset : document.documentElement.scrollLeft,
                y: (window.pageYOffset !== undefined) ? window.pageYOffset : document.documentElement.scrollTop
            });
            log(' Get page position: '+pagePosition.x+','+pagePosition.y);
        }
    }

@thenewguy
Copy link
Contributor Author

I will put one together.

@thenewguy
Copy link
Contributor Author

Sorry it took me a minute [hour wow sorry had to deal with some other issues. lost track of time] to put one together. Not sure how to pass it to you. I've added the html for the host page (host.html) and the iframe page (iframe.html).

Notice that the parentIFrame.scrollToOffset(0, 0); call is correctly positioned with the scrollCallback, but the resize event scrolls to an incorrect position.

host.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
    <title>host for iframe-resizer issue 243</title>

    <!-- Bootstrap -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">

    <!-- Custom styles for this template -->
    <link rel="stylesheet" href="http://getbootstrap.com/examples/jumbotron/jumbotron.css">

    <!-- iFrame Resizer CSS -->
    <style>
        body {
            height: 300vh;
        }
        iframe {
            width: 100%;
            border: none;
        }
    </style>

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->

    <!-- iFrame Resizer JavaScript -->
    <script type="text/javascript">
        // https://raw.githubusercontent.com/davidjbradshaw/iframe-resizer/master/js/iframeResizer.min.js
        !function(a){"use strict";function b(b,c,d){"addEventListener"in a?b.addEventListener(c,d,!1):"attachEvent"in a&&b.attachEvent("on"+c,d)}function c(){var b,c=["moz","webkit","o","ms"];for(b=0;b<c.length&&!E;b+=1)E=a[c[b]+"RequestAnimationFrame"];E||f(" RequestAnimationFrame not supported")}function d(){var b="Host page";return a.top!==a.self&&(b=a.parentIFrame?a.parentIFrame.getId():"Nested host page"),b}function e(a){return B+"["+d()+"]"+a}function f(b){x&&"object"==typeof a.console&&console.log(e(b))}function g(b){"object"==typeof a.console&&console.warn(e(b))}function h(b){function c(){function a(){l(K),j()}h("Height"),h("Width"),m(a,K,"resetPage")}function d(a){var b=a.id;f(" Removing iFrame: "+b),a.parentNode.removeChild(a),G[b].closedCallback(b),delete G[b],f(" --")}function e(){var a=J.substr(C).split(":");return{iframe:G[a[0]].iframe,id:a[0],height:a[1],width:a[2],type:a[3]}}function h(a){var b=Number(G[L]["max"+a]),c=Number(G[L]["min"+a]),d=a.toLowerCase(),e=Number(K[d]);if(c>b)throw new Error("Value for min"+a+" can not be greater than max"+a);f(" Checking "+d+" is in range "+c+"-"+b),c>e&&(e=c,f(" Set "+d+" to min value")),e>b&&(e=b,f(" Set "+d+" to max value")),K[d]=""+e}function p(){function a(){function a(){f(" Checking connection is from allowed list of origins: "+d);var a;for(a=0;a<d.length;a++)if(d[a]===c)return!0;return!1}function b(){var a=G[L].remoteHost;return f(" Checking connection is from: "+a),c===a}return d.constructor===Array?a():b()}var c=b.origin,d=G[L].checkOrigin;if(d&&""+c!="null"&&!a())throw new Error("Unexpected message received from: "+c+" for "+K.iframe.id+". Message was: "+b.data+". This error can be disabled by setting the checkOrigin: false option or by providing of array of trusted domains.");return!0}function q(){return B===(""+J).substr(0,C)&&J.substr(C).split(":")[0]in G}function r(){var a=K.type in{"true":1,"false":1,undefined:1};return a&&f(" Ignoring init message from meta parent page"),a}function s(a){return J.substr(J.indexOf(":")+A+a)}function t(a){f(" MessageCallback passed: {iframe: "+K.iframe.id+", message: "+a+"}"),G[L].messageCallback({iframe:K.iframe,message:JSON.parse(a)}),f(" --")}function u(){return null===K.iframe?(g(" IFrame ("+K.id+") not found"),!1):!0}function v(a){var b=a.getBoundingClientRect();return i(),{x:parseInt(b.left,10)+parseInt(D.x,10),y:parseInt(b.top,10)+parseInt(D.y,10)}}function w(b){function c(){D=h,y(),f(" --")}function d(){return{x:Number(K.width)+e.x,y:Number(K.height)+e.y}}var e=b?v(K.iframe):{x:0,y:0},h=d();f(" Reposition requested from iFrame (offset x:"+e.x+" y:"+e.y+")"),a.top!==a.self?a.parentIFrame?a.parentIFrame["scrollTo"+(b?"Offset":"")](h.x,h.y):g(" Unable to scroll to requested position, window.parentIFrame not found"):c()}function y(){!1!==G[L].scrollCallback(D)&&j()}function z(b){function c(a){var b=v(a);f(" Moving to in page link (#"+d+") at x: "+b.x+" y: "+b.y),D={x:b.x,y:b.y},y(),f(" --")}var d=b.split("#")[1]||"",e=decodeURIComponent(d),g=document.getElementById(e)||document.getElementsByName(e)[0];a.top!==a.self?a.parentIFrame?a.parentIFrame.moveToAnchor(d):f(" In page link #"+d+" not found and window.parentIFrame not found"):g?c(g):f(" In page link #"+d+" not found")}function E(){switch(G[L].firstRun&&I(),K.type){case"close":d(K.iframe);break;case"message":t(s(6));break;case"scrollTo":w(!1);break;case"scrollToOffset":w(!0);break;case"inPageLink":z(s(9));break;case"reset":k(K);break;case"init":c(),G[L].initCallback(K.iframe),G[L].resizedCallback(K);break;default:c(),G[L].resizedCallback(K)}}function F(a){var b=!0;return G[a]||(b=!1,g(K.type+" No settings for "+a+". Message was: "+J)),b}function H(){for(var a in G)n("iFrame requested init",o(a),document.getElementById(a),a)}function I(){G[L].firstRun=!1,Function.prototype.bind&&(G[L].iframe.iFrameResizer={close:d.bind(null,G[L].iframe),resize:n.bind(null,"Window resize","resize",G[L].iframe),moveToAnchor:function(a){n("Move to anchor","inPageLink:"+a,G[L].iframe,L)},sendMessage:function(a){a=JSON.stringify(a),n("Send Message","message:"+a,G[L].iframe,L)}})}var J=b.data,K={},L=null;"[iFrameResizerChild]Ready"===J?H():q()?(K=e(),L=K.id,!r()&&F(L)&&(x=G[L].log,f(" Received: "+J),u()&&p()&&E())):f(" Ignored: "+J)}function i(){null===D&&(D={x:void 0!==a.pageXOffset?a.pageXOffset:document.documentElement.scrollLeft,y:void 0!==a.pageYOffset?a.pageYOffset:document.documentElement.scrollTop},f(" Get page position: "+D.x+","+D.y))}function j(){null!==D&&(a.scrollTo(D.x,D.y),f(" Set page position: "+D.x+","+D.y),D=null)}function k(a){function b(){l(a),n("reset","reset",a.iframe,a.id)}f(" Size reset requested by "+("init"===a.type?"host page":"iFrame")),i(),m(b,a,"init")}function l(a){function b(b){a.iframe.style[b]=a[b]+"px",f(" IFrame ("+e+") "+b+" set to "+a[b]+"px")}function c(b){y||"0"!==a[b]||(y=!0,f(" Hidden iFrame detected, creating visibility listener"),s())}function d(a){b(a),c(a)}var e=a.iframe.id;G[e].sizeHeight&&d("height"),G[e].sizeWidth&&d("width")}function m(a,b,c){c!==b.type&&E?(f(" Requesting animation frame"),E(a)):a()}function n(a,b,c,d){d=d||c.id,c&&c.contentWindow?(f("["+a+"] Sending msg to iframe["+d+"] ("+b+")"),c.contentWindow.postMessage(B+b,G[d].targetOrigin)):(g("["+a+"] IFrame("+d+") not found"),G[d]&&delete G[d])}function o(a){return a+":"+G[a].bodyMarginV1+":"+G[a].sizeWidth+":"+G[a].log+":"+G[a].interval+":"+G[a].enablePublicMethods+":"+G[a].autoResize+":"+G[a].bodyMargin+":"+G[a].heightCalculationMethod+":"+G[a].bodyBackground+":"+G[a].bodyPadding+":"+G[a].tolerance+":"+G[a].inPageLinks+":"+G[a].resizeFrom+":"+G[a].widthCalculationMethod}function p(a,c){function d(){function b(b){1/0!==G[t][b]&&0!==G[t][b]&&(a.style[b]=G[t][b]+"px",f(" Set "+b+" = "+G[t][b]+"px"))}b("maxHeight"),b("minHeight"),b("maxWidth"),b("minWidth")}function e(b){return""===b&&(a.id=b="iFrameResizer"+w++,x=(c||{}).log,f(" Added missing iframe ID: "+b+" ("+a.src+")")),b}function h(){f(" IFrame scrolling "+(G[t].scrolling?"enabled":"disabled")+" for "+t),a.style.overflow=!1===G[t].scrolling?"hidden":"auto",a.scrolling=!1===G[t].scrolling?"no":"yes"}function i(){("number"==typeof G[t].bodyMargin||"0"===G[t].bodyMargin)&&(G[t].bodyMarginV1=G[t].bodyMargin,G[t].bodyMargin=""+G[t].bodyMargin+"px")}function j(){var b=G[t].firstRun,c=G[t].heightCalculationMethod in F;!b&&c&&k({iframe:a,height:0,width:0,type:"init"})}function l(c){function d(){n("iFrame.onload",c,a),j()}b(a,"load",d),n("init",c,a)}function m(a){if("object"!=typeof a)throw new TypeError("Options is not an object.")}function p(a){for(var b in I)I.hasOwnProperty(b)&&(G[t][b]=a.hasOwnProperty(b)?a[b]:I[b])}function q(a){return(""===a||"file://"===a)&&(a="*"),a}function r(b){b=b||{},G[t]={firstRun:!0,iframe:a,remoteHost:a.src.split("/").slice(0,3).join("/")},m(b),p(b),G[t].targetOrigin=!0===G[t].checkOrigin?q(G[t].remoteHost):"*",x=G[t].log}function s(){return t in G&&"iFrameResizer"in a}var t=e(a.id);s()?g(" Ignored iFrame, already setup."):(r(c),h(),d(),i(),l(o(t)))}function q(a,b){null===H&&(H=setTimeout(function(){H=null,a()},b))}function r(a){return null!==a.offsetParent}function s(){function b(){function a(a){function b(b){return"0px"===G[a].iframe.style[b]}r(G[a].iframe)&&(b("height")||b("width"))&&n("Visibility change","resize",G[a].iframe,a)}for(var b in G)a(b)}function c(a){f(" Mutation observed: "+a[0].target+" "+a[0].type),q(b,16)}function d(){var a=document.querySelector("body"),b={attributes:!0,attributeOldValue:!1,characterData:!0,characterDataOldValue:!1,childList:!0,subtree:!0},d=new e(c);d.observe(a,b)}var e=a.MutationObserver||a.WebKitMutationObserver;e&&d()}function t(){function c(a){function b(){e("Window "+a,"resize")}f(" Trigger event: "+a),q(b,16)}function d(){function a(){e("Tab Visable","resize")}"hidden"!==document.visibilityState&&(f(" Trigger event: Visiblity change"),q(a,16))}function e(a,b){function c(a){return"parent"===G[a].resizeFrom&&G[a].autoResize&&!G[a].firstRun}for(var d in G)c(d)&&n(a,b,document.getElementById(d),d)}b(a,"message",h),b(a,"resize",function(){c("resize")}),b(document,"visibilitychange",d),b(document,"-webkit-visibilitychange",d),b(a,"focusin",function(){c("focus")}),b(a,"focus",function(){c("focus")})}function u(){function a(a,b){if(!b.tagName)throw new TypeError("Object is not a valid DOM element");if("IFRAME"!==b.tagName.toUpperCase())throw new TypeError("Expected <IFRAME> tag, found <"+b.tagName+">.");p(b,a)}return c(),t(),function(b,c){switch(typeof c){case"undefined":case"string":Array.prototype.forEach.call(document.querySelectorAll(c||"iframe"),a.bind(void 0,b));break;case"object":a(b,c);break;default:throw new TypeError("Unexpected data type ("+typeof c+").")}}}function v(a){a.fn.iFrameResize=function(a){return this.filter("iframe").each(function(b,c){p(c,a)}).end()}}var w=0,x=!1,y=!1,z="message",A=z.length,B="[iFrameSizer]",C=B.length,D=null,E=a.requestAnimationFrame,F={max:1,scroll:1,bodyScroll:1,documentElementScroll:1},G={},H=null,I={autoResize:!0,bodyBackground:null,bodyMargin:null,bodyMarginV1:8,bodyPadding:null,checkOrigin:!0,inPageLinks:!1,enablePublicMethods:!0,heightCalculationMethod:"bodyOffset",interval:32,log:!1,maxHeight:1/0,maxWidth:1/0,minHeight:0,minWidth:0,resizeFrom:"parent",scrolling:!1,sizeHeight:!0,sizeWidth:!1,tolerance:0,widthCalculationMethod:"scroll",closedCallback:function(){},initCallback:function(){},messageCallback:function(){g("MessageCallback function not defined")},resizedCallback:function(){},scrollCallback:function(){return!0}};a.jQuery&&v(jQuery),"function"==typeof define&&define.amd?define([],u):"object"==typeof module&&"object"==typeof module.exports?module.exports=u():a.iFrameResize=a.iFrameResize||u()}(window||{});
    </script>
  </head>
  <body>

    <nav class="navbar navbar-inverse navbar-fixed-top">
      <div class="container">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="/">Project name</a>
        </div>
        <div id="navbar" class="navbar-collapse collapse">
          <form class="navbar-form navbar-right">
            <div class="form-group">
              <input type="text" placeholder="Email" class="form-control">
            </div>
            <div class="form-group">
              <input type="password" placeholder="Password" class="form-control">
            </div>
            <button type="submit" class="btn btn-success">Sign in</button>
          </form>
        </div><!--/.navbar-collapse -->
      </div>
    </nav>

    <!-- Main jumbotron for a primary marketing message or call to action -->
    <div class="jumbotron">
      <div class="container">
        <h1>Hello, world!</h1>
        <p>This is a template for a simple marketing or informational website. It includes a large callout called a jumbotron and three supporting pieces of content. Use it as a starting point to create something more unique.</p>
        <p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more &raquo;</a></p>
      </div>
    </div>

    <iframe src="iframe.html" scrolling="no"></iframe>

    <div class="container">
      <!-- Example row of columns -->
      <div class="row">
        <div class="col-md-4">
          <h2>Heading</h2>
          <p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui. </p>
          <p><a class="btn btn-default" href="#" role="button">View details &raquo;</a></p>
        </div>
        <div class="col-md-4">
          <h2>Heading</h2>
          <p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui. </p>
          <p><a class="btn btn-default" href="#" role="button">View details &raquo;</a></p>
       </div>
        <div class="col-md-4">
          <h2>Heading</h2>
          <p>Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p>
          <p><a class="btn btn-default" href="#" role="button">View details &raquo;</a></p>
        </div>
      </div>

      <hr>

      <footer>
        <p>&copy; Company {% now 'Y' %}</p>
      </footer>
    </div> <!-- /container -->

    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
    <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
    <script src="http://getbootstrap.com/assets/js/ie10-viewport-bug-workaround.js"></script>
    <!-- iFrame Resizer Init -->
    <script type="text/javascript">
        iFrameResize({
            log: true,
            checkOrigin: false,
            heightCalculationMethod: 'taggedElement',
            autoResize: false,
            enablePublicMethods: true,
            scrollCallback: function (coords) {
                console.log("[OVERRIDE] overrode scrollCallback x: " + coords.x + " y: " + coords.y);
                window.scrollTo(coords.x, coords.y-50);
                return false;
            }
        })
    </script>
  </body>
</html>

iframe.html

<!DOCTYPE html>
<html>
    <head>
        <title>content for iframe-resizer issue 243</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <link rel="stylesheet" type="text/css" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.css" />

        <script type="text/javascript">
            // https://raw.githubusercontent.com/davidjbradshaw/iframe-resizer/master/js/iframeResizer.contentWindow.min.js
            !function(a){"use strict";function b(b,c,d){"addEventListener"in a?b.addEventListener(c,d,!1):"attachEvent"in a&&b.attachEvent("on"+c,d)}function c(a){var b,c,d,e=null,f=0,g=function(){f=rb(),e=null,d=a.apply(b,c),e||(b=c=null)};return function(){var h=rb();f||(f=h);var i=mb-(h-f);return b=this,c=arguments,0>=i||i>mb?(e&&(clearTimeout(e),e=null),f=h,d=a.apply(b,c),e||(b=c=null)):e||(e=setTimeout(g,i)),d}}function d(a){return cb+"["+eb+"] "+a}function e(b){bb&&"object"==typeof a.console&&console.log(d(b))}function f(b){"object"==typeof a.console&&console.warn(d(b))}function g(){h(),e("Initialising iFrame ("+location.href+")"),i(),l(),k("background",O),k("padding",R),t(),q(),r(),m(),v(),s(),_=u(),F("init","Init message from host page")}function h(){function a(a){return"true"===a?!0:!1}var b=$.substr(db).split(":");eb=b[0],P=void 0!==b[1]?Number(b[1]):P,S=void 0!==b[2]?a(b[2]):S,bb=void 0!==b[3]?a(b[3]):bb,ab=void 0!==b[4]?Number(b[4]):ab,M=void 0!==b[6]?a(b[6]):M,Q=b[7],Y=void 0!==b[8]?b[8]:Y,O=b[9],R=b[10],jb=void 0!==b[11]?Number(b[11]):jb,_.enable=void 0!==b[12]?a(b[12]):!1,gb=void 0!==b[13]?b[13]:gb,pb=void 0!==b[14]?b[14]:pb}function i(){function b(){var b=a.iFrameResizer;e("Reading data from page: "+JSON.stringify(b)),qb=void 0!==b.messageCallback?b.messageCallback:qb,hb=void 0!==b.targetOrigin?b.targetOrigin:hb,Y=void 0!==b.heightCalculationMethod?b.heightCalculationMethod:Y,pb=void 0!==b.widthCalculationMethod?b.widthCalculationMethod:pb}"iFrameResizer"in a&&Object===a.iFrameResizer.constructor&&b()}function j(a,b){return-1!==b.indexOf("-")&&(f("Negative CSS value ignored for "+a),b=""),b}function k(a,b){void 0!==b&&""!==b&&"null"!==b&&(document.body.style[a]=b,e("Body "+a+' set to "'+b+'"'))}function l(){void 0===Q&&(Q=P+"px"),j("margin",Q),k("margin",Q)}function m(){document.documentElement.style.height="",document.body.style.height="",e('HTML & body height set to "auto"')}function n(c){function d(d){function e(){F(c.eventName,c.eventType)}b(a,d,e)}c.eventNames&&Array.prototype.map?(c.eventName=c.eventNames[0],c.eventNames.map(d)):d(c.eventName),e("Added event listener: "+c.eventType)}function o(){n({eventType:"Animation Start",eventNames:["animationstart","webkitAnimationStart"]}),n({eventType:"Animation Iteration",eventNames:["animationiteration","webkitAnimationIteration"]}),n({eventType:"Animation End",eventNames:["animationend","webkitAnimationEnd"]}),n({eventType:"Orientation Change",eventName:"orientationchange"}),n({eventType:"Input",eventName:"input"}),n({eventType:"Print",eventName:["afterprint","beforeprint"]}),n({eventType:"Transition End",eventNames:["transitionend","webkitTransitionEnd","MSTransitionEnd","oTransitionEnd","otransitionend"]}),n({eventType:"Mouse Up",eventName:"mouseup"}),n({eventType:"Mouse Down",eventName:"mousedown"}),"child"===gb&&n({eventType:"IFrame Resized",eventName:"resize"})}function p(a,b,c,d){b!==a&&(a in c||(f(a+" is not a valid option for "+d+"CalculationMethod."),a=b),e(d+' calculation method set to "'+a+'"'))}function q(){p(Y,X,sb,"height")}function r(){p(pb,ob,tb,"width")}function s(){!0===M?(o(),y()):e("Auto Resize disabled")}function t(){var a=document.createElement("div");a.style.clear="both",a.style.display="block",document.body.appendChild(a)}function u(){function c(){return{x:void 0!==a.pageXOffset?a.pageXOffset:document.documentElement.scrollLeft,y:void 0!==a.pageYOffset?a.pageYOffset:document.documentElement.scrollTop}}function d(a){var b=a.getBoundingClientRect(),d=c();return{x:parseInt(b.left,10)+parseInt(d.x,10),y:parseInt(b.top,10)+parseInt(d.y,10)}}function g(a){function b(a){var b=d(a);e("Moving to in page link (#"+c+") at x: "+b.x+" y: "+b.y),J(b.y,b.x,"scrollToOffset")}var c=a.split("#")[1]||a,f=decodeURIComponent(c),g=document.getElementById(f)||document.getElementsByName(f)[0];g?b(g):(e("In page link (#"+c+") not found in iFrame, so sending to parent"),J(0,0,"inPageLink","#"+c))}function h(){""!==location.hash&&"#"!==location.hash&&g(location.href)}function i(){function a(a){function c(a){a.preventDefault(),g(this.getAttribute("href"))}"#"!==a.getAttribute("href")&&b(a,"click",c)}Array.prototype.forEach.call(document.querySelectorAll('a[href^="#"]'),a)}function j(){b(a,"hashchange",h)}function k(){setTimeout(h,U)}function l(){Array.prototype.forEach&&document.querySelectorAll?(e("Setting up location.hash handlers"),i(),j(),k()):f("In page linking not fully supported in this browser! (See README.md for IE8 workaround)")}return _.enable?l():e("In page linking not enabled"),{findTarget:g}}function v(){e("Enable public methods"),a.parentIFrame={close:function(){J(0,0,"close")},getId:function(){return eb},moveToAnchor:function(a){_.findTarget(a)},reset:function(){I("parentIFrame.reset")},scrollTo:function(a,b){J(b,a,"scrollTo")},scrollToOffset:function(a,b){J(b,a,"scrollToOffset")},sendMessage:function(a,b){J(0,0,"message",JSON.stringify(a),b)},setHeightCalculationMethod:function(a){Y=a,q()},setWidthCalculationMethod:function(a){pb=a,r()},setTargetOrigin:function(a){e("Set targetOrigin: "+a),hb=a},size:function(a,b){var c=""+(a?a:"")+(b?","+b:"");G(),F("size","parentIFrame.size("+c+")",a,b)}}}function w(){0!==ab&&(e("setInterval: "+ab+"ms"),setInterval(function(){F("interval","setInterval: "+ab)},Math.abs(ab)))}function x(a){return void 0===a||0===a}function y(){function b(a){function b(a){var b=F.bind(null,"imageLoad","Image loaded",void 0,void 0);(x(a.height)||x(a.width))&&(e("Attach listerner to "+a.src),a.addEventListener("load",b,!1))}"attributes"===a.type&&"src"===a.attributeName?b(a.target):"childList"===a.type&&Array.prototype.forEach.call(a.target.querySelectorAll("img"),b)}function c(a){F("mutationObserver","mutationObserver: "+a[0].target+" "+a[0].type),b(a[0])}function d(){var a=document.querySelector("body"),b={attributes:!0,attributeOldValue:!1,characterData:!0,characterDataOldValue:!1,childList:!0,subtree:!0},d=new h(c);e("Enable MutationObserver"),d.observe(a,b)}var g=0>ab,h=a.MutationObserver||a.WebKitMutationObserver;h?g?w():d():(f("MutationObserver not supported in this browser!"),w())}function z(a){function b(a){var b=/^\d+(px)?$/i;if(b.test(a))return parseInt(a,N);var d=c.style.left,e=c.runtimeStyle.left;return c.runtimeStyle.left=c.currentStyle.left,c.style.left=a||0,a=c.style.pixelLeft,c.style.left=d,c.runtimeStyle.left=e,a}var c=document.body,d=0;return"defaultView"in document&&"getComputedStyle"in document.defaultView?(d=document.defaultView.getComputedStyle(c,null),d=null!==d?d[a]:0):d=b(c.currentStyle[a]),parseInt(d,N)}function A(a,b){for(var c=b.length,d=0,f=rb(),g=0;c>g;g++)b[g].getBoundingClientRect()[a]>d&&(d=b[g].getBoundingClientRect()[a]);return f=rb()-f,e("Parsed "+c+" HTML elements"),e("Element position calculated in "+f+"ms"),f>mb/2&&(mb=2*f,e("Event throttle increased to "+mb+"ms")),d}function B(a){return[a.bodyOffset(),a.bodyScroll(),a.documentElementOffset(),a.documentElementScroll()]}function C(a,b){function c(){return f("No tagged elements ("+b+") found on page"),W}var d=document.querySelectorAll("["+b+"]");return 0===d.length?c():A(a,d)}function D(){return document.querySelectorAll("body *")}function E(a,b,c,d){function f(){W=l,nb=m,J(W,nb,a)}function g(){function a(a,b){var c=Math.abs(a-b)<=jb;return!c}return l=void 0!==c?c:sb[Y](),m=void 0!==d?d:tb[pb](),a(W,l)||S&&a(nb,m)}function h(){return!(a in{init:1,interval:1,size:1})}function i(){return Y in fb||S&&pb in fb}function j(){e("No change in size detected")}function k(){h()&&i()?I(b):a in{interval:1}||j()}var l,m;g()||"init"===a?(G(),f()):k()}function F(a,b,c,d){function f(){a in{reset:1,resetPage:1,init:1}||e("Trigger event: "+b)}function g(){return kb&&a in T}g()?e("Trigger event cancelled: "+a):(f(),ub(a,b,c,d))}function G(){kb||(kb=!0,e("Trigger event lock on")),clearTimeout(lb),lb=setTimeout(function(){kb=!1,e("Trigger event lock off"),e("--")},U)}function H(a){W=sb[Y](),nb=tb[pb](),J(W,nb,a)}function I(a){var b=Y;Y=X,e("Reset trigger event: "+a),G(),H("reset"),Y=b}function J(a,b,c,d,f){function g(){void 0===f?f=hb:e("Message targetOrigin: "+f)}function h(){var g=a+":"+b,h=eb+":"+g+":"+c+(void 0!==d?":"+d:"");e("Sending message to host page ("+h+")"),ib.postMessage(cb+h,f)}g(),h()}function K(b){function c(){return cb===(""+b.data).substr(0,db)}function d(){$=b.data,ib=b.source,g(),V=!1,setTimeout(function(){Z=!1},U)}function h(){Z?e("Page reset ignored by init"):(e("Page size reset by host page"),H("resetPage"))}function i(){F("resizeParent","Parent window requested size check")}function j(){var a=l();_.findTarget(a)}function k(){return b.data.split("]")[1].split(":")[0]}function l(){return b.data.split(":")[1]}function m(){return"iFrameResize"in a}function n(){var a=l();e("MessageCallback called from parent: "+a),qb(JSON.parse(a)),e(" --")}function o(){return b.data.split(":")[2]in{"true":1,"false":1}}function p(){switch(k()){case"reset":h();break;case"resize":i();break;case"moveToAnchor":j();break;case"message":n();break;default:m()||o()||f("Unexpected message ("+b.data+")")}}function q(){!1===V?p():o()?d():e('Ignored message of type "'+k()+'". Received before initialization.')}c()&&q()}function L(){"loading"!==document.readyState&&a.parent.postMessage("[iFrameResizerChild]Ready","*")}var M=!0,N=10,O="",P=0,Q="",R="",S=!1,T={resize:1,click:1},U=128,V=!0,W=1,X="bodyOffset",Y=X,Z=!0,$="",_={},ab=32,bb=!1,cb="[iFrameSizer]",db=cb.length,eb="",fb={max:1,min:1,bodyScroll:1,documentElementScroll:1},gb="child",hb="*",ib=a.parent,jb=0,kb=!1,lb=null,mb=0,nb=1,ob="scroll",pb=ob,qb=function(){f("MessageCallback function not defined")},rb=Date.now||function(){return(new Date).getTime()},sb={bodyOffset:function(){return document.body.offsetHeight+z("marginTop")+z("marginBottom")},offset:function(){return sb.bodyOffset()},bodyScroll:function(){return document.body.scrollHeight},documentElementOffset:function(){return document.documentElement.offsetHeight},documentElementScroll:function(){return document.documentElement.scrollHeight},max:function(){return Math.max.apply(null,B(sb))},min:function(){return Math.min.apply(null,B(sb))},grow:function(){return sb.max()},lowestElement:function(){return Math.max(sb.bodyOffset(),A("bottom",D()))},taggedElement:function(){return C("bottom","data-iframe-height")}},tb={bodyScroll:function(){return document.body.scrollWidth},bodyOffset:function(){return document.body.offsetWidth},documentElementScroll:function(){return document.documentElement.scrollWidth},documentElementOffset:function(){return document.documentElement.offsetWidth},scroll:function(){return Math.max(tb.bodyScroll(),tb.documentElementScroll())},max:function(){return Math.max.apply(null,B(tb))},min:function(){return Math.min.apply(null,B(tb))},leftMostElement:function(){return A("left",D())},taggedElement:function(){return C("left","data-iframe-width")}},ub=c(E);b(a,"message",K),L()}(window||{});
        </script>
        <script type="text/javascript" src="https://code.jquery.com/jquery-1.11.3.js"></script>
        <script type="text/javascript">
            var running = false;
            function scroll() {
                if ('parentIFrame' in window) {
                    parentIFrame.scrollToOffset(0, 0);
                }
                if (running){ setTimeout(resize, 3000);}
            }

            function resize() {
                var hewHeight = (0.5 < Math.random()) ? $(".ui-content").height() + 20 : $(".ui-content").height() - 20;
                    hewHeight = (hewHeight < 400) ? 400 : hewHeight;
                if ('parentIFrame' in window) {
                    $(".ui-content").height(hewHeight);
                    parentIFrame.size();
                }
                if (running){ setTimeout(scroll, 3000); }
            }            
        </script>
        <script type="text/javascript" src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.js"></script>
    </head>
    <body>
        <div data-role="page">
            <div data-role="header">
                <nav data-role="controlgroup" data-type="horizontal">
                    <button class="ui-btn ui-btn-b" disabled>top</button>
                    <a class="ui-btn ui-btn-b" href="#" onclick="running=!running;resize();">toggle</a>
                </nav>
            </div>
            <div role="main" class="ui-content">    
                content
            </div>
            <div data-role="footer" data-iframe-height>
                <nav>
                    <button class="ui-btn ui-btn-b" disabled>bottom</button>
                </nav>
            </div>
        </div>
    </body>
</html>

@thenewguy
Copy link
Contributor Author

The host page is a getting started template from boostrap. The iframe page is a getting started template from jquery mobile. I don't think this is important but it configures the css styles to demonstrate my issues.

@thenewguy
Copy link
Contributor Author

Forgot to mention... click the toggle button to start the timers to demonstrate the issue

@thenewguy
Copy link
Contributor Author

Also, I am assuming iframeResizer.js#L113 calls setPagePosition() instead of scrollTo() on purpose. If it can be changed to scrollTo() then that would fix the issue I think.

@davidjbradshaw
Copy link
Owner

Can you please host that somewhere and I will take a quick look tomorrow.

thenewguy added a commit to thenewguy/thenewguy.github.io that referenced this issue Aug 24, 2015
@thenewguy
Copy link
Contributor Author

@thenewguy
Copy link
Contributor Author

It is set to 3 second timeouts. So after you click toggle it will switch between the two scroll positions every 3 seconds

@thenewguy
Copy link
Contributor Author

I think this is a simple fix for the issue: thenewguy@58e1beb

I updated the demo with that version and increased the speed at http://thenewguy.github.io/iframe-resizer/issues/243/2/host.html

@thenewguy
Copy link
Contributor Author

I updated the first example to use the same dimensions and timing as the second so they can be compared side by side

@davidjbradshaw
Copy link
Owner

Just looked at this a bit more. I think the solution is to change the code in the iFrame to the following.

parentIFrame.scrollToOffset(0, -50);

The scrollCallback method is their to implement animation, rather than to change the passed in values.

@thenewguy
Copy link
Contributor Author

The problem is also seen if you are trying to do a custom animation because the scrollCallback isn't always called. The small changeset thenewguy@58e1beb just ensures that the scrollCallback is called if it exists instead of calling window.scrollTo directly.

With respect to my particular use case, it is much easier to calculate the scroll position offset on the host side. It is not very feasible for the content to be embedded into a generic host page if the content page is responsible for determining the host page layout.

The -50px in my example is just a sample using one of the default bootstrap themes. But in practice every host page is different. Since the iframe is used as a generic widget to be incorporated into any host page, it isn't a simple -50px constant.

Since the fix is so simple, wouldn't it be better to allow the host page to adjust for site specific style? If there is not a chance of this happening in the scrollCallback, could an offsetCallback be added to allow the host page to inject knowledge of its layout?

@thenewguy
Copy link
Contributor Author

@davidjbradshaw not sure if you're automatically pinged after the issue is closed. comment above ^

@davidjbradshaw
Copy link
Owner

The fix isn't that simple, because if you use scrollCallback for animation, you don't what to have the scrolling animated when the position is moved due to the library moving the page during a resize event.

That said it only needs to move the page when using height calculation methods that require the force down sizing code to run. Currently it runs on all methods. So fixing that is a possible solution.

@thenewguy
Copy link
Contributor Author

I guess I did not see the case where you would not want animation. In my particular widget I found it distracting to be animated some times but not others so I put together a css transform for the height and used the scrollCallback method to cover scrolling issues.

@davidjbradshaw I feel like maybe I am missing your point... so my apologies if it went over my head. Is there already a way to handle a layout like the "getting started" bootstrap template in my example using the api in its current state (assuming that the host page layout is not known in advance and the offset adjustment is arbitrary and varies between host pages)? Since the default configuration was scrolling the host page such that the host page header overlapped the top of the iframe content, it was my understanding that this level of detail would be required from the host page integration overriding a callback option of some sort. Maybe I have overlooked something?

Can this issue be re-opened?

@davidjbradshaw
Copy link
Owner

The issue is that some height calc methods require a double resize to detect downsizing, once down to zero and then again to find the correct new size. To stop the page scrolling during this process the page position is saved and then set back again on the new size. During this you don't want animation, because the idea is to give the impression that the page has not moved.

Now that this repositioning is only need when the page is double resized, so I've restricted it to only happen in those cases, rather than all resize event types. Have a play with the version in the dev branch and let me know if that fixes things for you.

@thenewguy
Copy link
Contributor Author

@davidjbradshaw
Copy link
Owner

Just tried it on my iPad and it worked. Do a hard reload.

@thenewguy
Copy link
Contributor Author

Hrm... I just cleared cache and checked again in IE11 and Chrome44 against http://thenewguy.github.io/iframe-resizer/issues/243/3/host.html and they scroll over the iframe header after about 2 or 3 seconds. I don't have an ipad available.

Also chrome on my android phone

@davidjbradshaw
Copy link
Owner

Hmm, try adding the following to the if statement that calls the scroll callback function.

else {
   pagePosition = null;
}

@davidjbradshaw
Copy link
Owner

OK now that I'm back at my Mac I've just had a bit more of a look and updated the dev channel again. The issue seems to be that when scrollCallback returns false the pagePosition var is not reset to null. So the next resize event causes the page to jump.

Let me know if it does the trick.

@thenewguy
Copy link
Contributor Author

I've put up a new demo at http://thenewguy.github.io/iframe-resizer/issues/243/4/host.html Now it scrolls below the header every time.

The scrollCallback is called, but then it seems that immediately afterwards the setPagePosition function is called and window.scrollTo(pagePosition.x,pagePosition.y) is called directly. So the -50 adjustment in scrollCallback isn't shown on screen.

Since I've set autoResize: false, heightCalculationMethod: 'taggedElement', and scrollCallback should setPagePosition even be called at all after the initial sizing? These are the only two calls made in the content page: parentIFrame.scrollToOffset(0, 0) and parentIFrame.size()

--- ps ---

If you're familiar with django and/or python, I added a simple django project to thenewguy.github.io that runs the example locally with python /path/to/thenewguy.github.io/project/manage.py runserver 0.0.0.0:8080 (this may require pip install django if it isn't already installed or pip install -U django to upgrade if too old of a version is installed and the version isn't important to you). Then the demo is available at http://127.0.0.1:8080/ or any ip address assigned to the machine.

@davidjbradshaw
Copy link
Owner

Seems another change I made prevented the return false working and the tests didn't pick it up. Try dev now.

@thenewguy
Copy link
Contributor Author

@davidjbradshaw I think that solved the issue. I will do more cross browser testing but it works on chrome and ie just fine (only two browsers on this tablet). See http://thenewguy.github.io/iframe-resizer/issues/243/5/host.html

Though I am observing an odd issue that I couldn't reproduce it at first... if you open the page at 100% zoom it works as expected. If you change the zoom to 110% it alternates between scrolling to y coord of 429px and 430px. This was observed at a window width between 548px and 637px according to the chrome web developer plugin. Is the use of parseInt in getElementPosition important?

Changing it to the following prevents the pixel jumping I am observing:

        function getElementPosition(target){
            var iFramePosition = target.getBoundingClientRect();

            getPagePosition(iframeId);

            return {
                x: Math.floor( Number(iFramePosition.left) + Number(pagePosition.x) ),
                y: Math.floor( Number(iFramePosition.top)  + Number(pagePosition.y) )
            };
        }

The modified getElementPosition function is demonstrated here: http://thenewguy.github.io/iframe-resizer/issues/243/6/host.html

I probably should have opened a new issue... it was late. Let me know if you would prefer for me to do so.

@davidjbradshaw
Copy link
Owner

Can you open a PR for it if it has fixed the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants