iframe causes parent elements to scroll up on Google Chrome when URL contains fragment

后端 未结 5 803
南旧
南旧 2021-02-04 11:53

On Google Chrome (37.0.2062.122, OSX / Windows), an iframe with an URL containing a fragment causes the parent elements to scroll up.

It\'s only happening in Chrome (tes

相关标签:
5条回答
  • 2021-02-04 12:17

    As other answers have already mentioned, the issue here is specific to JSFiddle. I can show you that with ease by simply including the same code within a StackOverflow snippet on this question:

    iframe {
      position: absolute;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      width: 100%;
      height: 100%;
    }
    <iframe src="https://news02ycombinator02com2.mentionusercontent.net/item?id=8357207#up_8360397" width="100%" height="100%"></iframe>


    Simpler HTML document

    This issue happens with every fragment URL passed into an iframe on JSFiddle. Because of this, we can replicate this issue simply be creating a separate JSFiddle demo and linking to that in our iframe.

    I've created this new JSFiddle demo here: http://fiddle.jshell.net/JamesD/k5vk2u9h/1/show/light/#test.

    As you can see, the fragment here is #test. This issue is now replicable in a new JSFiddle demo using the following markup:

    <iframe src="http://fiddle.jshell.net/JamesD/k5vk2u9h/1/show/light/#test"></iframe>
    

    The height and width attributes are irrelevant as the issue still appears without them.

    JSFiddle demo.


    A workaround

    The other answer here suggests that the fix is to modify JSFiddle's own HTML in order to re-render the page correctly. This isn't a great solution as in a real world situation (i.e. when linking someone to a JSFiddle demo), they may not have the expertise to achieve this.

    We can get around this by using JavaScript. As the new HTML document I've created is on the same domain (http://jshell.net) we can execute JavaScript within the iframe itself. Manually setting the location.hash property will also trigger this issue, so what we can do instead is scroll the iframe directly to the element we wish to focus on.

    Firstly, remove the fragment from the URL in the src attribute of our iframe:

    <iframe src="http://fiddle.jshell.net/JamesD/k5vk2u9h/1/show/light/"></iframe>
    

    As mentioned above, the fragment I've used is an element whose id is "test". This means the easiest way to get our element is to select it by its id attribute:

    var frame = frames[0],
        elem = frame.document.querySelector('#test'),
    

    Here, frame is a reference to our iframe within our own document, and elem is our #test element. We can get our matched element's position by using getBoundingClientRect():

        position = elem.getBoundingClientRect();
    

    This will give us a ClientRect object like the following:

    ClientRect

    With this we now know that our #test element is positioned 3034px from the top of our iframe. Using this, we can now scroll the page ourselves to this position using window.scrollTo() (where the window of our iframe in this case is held in our frame variable):

    frame.scrollTo(0, position.top);
    

    JSFiddle demo.

    Note that this solution will only work if the iframe points to a URL hosted on the same domain or if CORS has been enabled on the page the iframe is embedding.


    The best option

    After doing all this though, the best bet is to simply notify the developers of JSFiddle that this is a bug. I've raised this as an issue on their GitHub repository here: https://github.com/jsfiddle/jsfiddle-issues/issues/547.

    0 讨论(0)
  • 2021-02-04 12:17

    The problem is not to include # in a iframe url, if you try with the next url in your example you will see there is no scroll: http://www.w3.org/TR/scxml/#scxml

    So, I reviewed the page that you are showing to see if there is something weird: https://news02ycombinator02com2.mentionusercontent.net/item?id=8357207#up_8360397

    If you check the JS it has something like the next:

          var e = window,
        g = document,
        h = "documentElement",
        k = "CriticalImages",
        l = "scrollTop",
        n = "prototype",
        p = "body",
        q = "getAttribute",
    

    There is a function called "scrollTop" which I think is a good candidate to investigate.

    If you check the code a little bit more you will see that in some point the JS is getting the offsetTop and the offsetParent:

     u = function(a, b) {
            var c = b[q]("pagespeed_lazy_position");
            if (c) return parseInt(c, 0);
            var c = b.offsetTop,
                d = b.offsetParent;
            d && (c += u(a, d));
            c = Math.max(c, 0);
            b.setAttribute("pagespeed_lazy_position", c);
            return c
        },
    

    Conclusion: the issue is with the page that your are trying to show in your iframe, not with fiddle nor Chrome or # in the urls.

    I hope this help.


    EDIT

    After reviewing the wiki example I see that is some js in the fiddle (so the first answer is right).

    To avoid this with js you just include this:

    $( window.parent ).scroll(function(event) {
       // debugger;
      console.log('SCROLL',event);
      var scrollTop=$(this).scrollTop();
      if(scrollTop>0){
          console.log('fix scroll');
          $(this).scrollTop(0);
      }
    
    });
    

    Example:
    http://fiddle.jshell.net/xbnt3Lu6/21/

    I will try to review which js is causing the problem.

    0 讨论(0)
  • 2021-02-04 12:27

    Reproducing script

    Found a minimal way to reproduce the bug: http://jsfiddle.net/4oytgwon/1/embedded/result/ (you can see the bug happening after clicking on "load iframe").

    It also happens outside of jsfiddle using the following code:

    <!doctype html>
    <html>
        <head>
            <style>
                body, html {
                    padding: 0;
                    border: 0;
                    margin: 0;
                    height: 100%;
                    overflow: hidden;
                }
                #header {
                    height: 50px;
                    background: blue;
                }
                #content {
                    position: absolute;
                    top: 50px;
                    right: 0;
                    bottom: 0;
                    left: 0;
                }
                #overflow {
                    position: absolute;
                    height: 50px;
                    bottom: -50px;
                }
            </style>
            <script>
                function loadIframe() {
                    var ifr = document.createElement('iframe');
                    ifr.src = 'https://en.wikipedia.org/wiki/Stack_overflow#See_also';
                    var content = document.getElementById('content');
                    while (content.firstChild) {
                        content.removeChild(content.firstChild);
                    }
                    content.appendChild(ifr);
                }
                window.onload = function () {
                    document.getElementById('btn').onclick = loadIframe;
                };
            </script>
        </head>
        <body>
            <div id=header>
                <button id=btn>load iframe</button>
            </div>
            <div id=content>
            </div>
            <div id=overflow>
                overflow
            </div>
        </body>
    </html>
    
    0 讨论(0)
  • 2021-02-04 12:29

    Well its not only chrome , all browsers will have the same problem, seems like the site has heavy js scripting , no idea how but it does affect on the site you put iframe on, the easiest way solving it is removing the #up_8360397 from the link. Second way would be disabling javascript inside the iframe. but than you won't be able to auto scroll it down to the post you wanted, u'll have to set scroll positions manually.

    0 讨论(0)
  • 2021-02-04 12:31

    Revised Answer

    There are two bugs being displayed here.

    1. JSFiddle looses part of it's header when loading an iFrame with a fragment. This is discussed in other answers and in my "Old Answer" section. And a github issue has been raised.
    2. iFrames on chrome handles fragments badly. They cause scrolling on the parent. (This may be desired behavior to be fair).

    Chrome iFrame problem

    When you run this, your window will scroll so that the fragment in the window is at the top of your page:

    <iframe src="https://news02ycombinator02com2.mentionusercontent.net/item?id=8357207#up_8360397" width="100%" height="100%"></iframe>

    As you can imagine, this causes issues in certain fixed height websites like jsfiddle. It is also unique to Chrome (I've only tested on FF, not IE), Firefox doesn't scroll the parent window. There is a discussion about it not being in FF here.

    Chrome iFrame parent scrolling fix

    Fiddle

    Add an onload event to the iframe that calls the following function. And don't use the fragment in the original:

    <script>
        function loadorder() {
        document.getElementById("iframename").src= "https://news02ycombinator02com2.mentionusercontent.net/item?id=8357207#up_8360397";
        window.scrollTo(0,0);
    }
    </script>
    <iframe width="830" src="https://news02ycombinator02com2.mentionusercontent.net/item?id=8357207" height="500" id="iframename"  onLoad="loadorder();"></iframe>

    As you can see in both the fiddle and the above snippet, the parent window no longer scrolls and the behavior is now the same for both FF and Chrome.

    Source for fix.

    Old Answer (alternate fix for jsfiddle)

    Okay, so I did some testing:

    • Issue only on Chrome
    • Issue occurs with any fragment linked page: example
    • Issue is specific to jsfiddle. CodePen and jsbin work fine.

    So as a temporary workaround either use one of those or inject this into jsfiddle's html:

    margin-top: 30px; into the header bar like:
    
    <div id="header" style="margin-top: 30px;">
    

    Bit of a pain though since it needs to be done everytime you load jsfiddle (but not every time you run the fiddle).

    To get the problem fixed, it should be reported on the jsfiddle github page: https://github.com/jsfiddle/jsfiddle-issues/issues

    Edit

    And here is a way to automate it on chrome: Download the stylebot extension, and add this style to jsfiddle.com (or the specific fiddle):

    #header {
        margin-top: 30px;
    }
    

    It works pretty well.

    0 讨论(0)
提交回复
热议问题