Strategien für das iFrame-Problem auf dem iPad
Worum geht es dabei?
Ursprünglich verhielten sich IFrames auf iOS Mobile Safari so, wie man es erwarten würde: Der Iframe hatte die von Ihnen festgelegte Größe, und der Inhalt konnte mit zwei Fingern gescrollt werden. Apple änderte dies mit einem Update im Jahr 2011. Das aktuelle Verhalten ist, dass die Breite eines iframe so ist, wie Sie es in Ihrem Code angeben, aber der iframe dehnt sich vertikal aus, um das gesamte Dokument anzuzeigen, das er enthält. Aha.
Ich kann mir vorstellen, warum sie das für eine gute Lösung hielten. Jetzt ist das Iframe-Dokument innerhalb des Host-Dokuments vollständig sichtbar, und die Benutzer werden nicht durch einen scrollenden Bereich innerhalb einer scrollenden Seite verwirrt. Aber ein expandierender iframe ist für eine Reihe von Situationen nicht so gut geeignet, z. B. wenn Sie eine Webanwendung erstellen, die sich wie eine typische Desktop-Anwendung verhält. Bei einer Desktop-Anwendung gibt es zwar Bildlaufbereiche, aber das Hauptfenster selbst lässt sich normalerweise nicht verschieben.
Dies ist alles im Zusammenhang mit einem anderen iOS mobile safari Problem, wollen eine große Menge an Inhalt in einem div auf der Seite, die eine feste Größe hat setzen. Zuvor hat die "overflow:scroll;" css-Eigenschaft nicht funktioniert, und es hat immer noch nicht "Momentum Scrolling" wie erwartet und so mehrere Plugins wurden erstellt, um dieses Problem zu beheben, wie iScroll. Aber iScroll funktioniert nicht für einen iFrame, weil die Berührungsereignisse im iFrame-Dokument ausgelöst werden, aber das übergeordnete Dokument muss den Bildlauf durchführen.
Apple/Webkit hat das Problem kürzlich mit einer CSS-Eigenschaft namens -webkit-overflow-scrolling: touch etwas entschärft;
Mit dieser Eigenschaft verhält sich ein Div mit viel Inhalt so, wie Sie es gerne hätten, und es hat sogar den netten mobilen Safari "Momentum Scroll", wenn Sie es mit dem Finger streichen. Cool, oder? Als die Funktion angekündigt wurde, stieß ich auf tonnenweise enthusiastische Blogposts dazu. Das Problem ist, dass es fehlerhaft ist. Unter bestimmten Umständen wird der Inhalt eines Containers mit dieser Eigenschaft nicht richtig angezeigt, Teile davon werden leer sein.
Leider habe ich keine Lösung gefunden, mit der sich ein iframe in allen Fällen so verhält, wie Sie es wünschen. Ich habe eine Lösung gefunden, die in unserem speziellen Fall funktioniert - und auf dem Weg dorthin eine Reihe von Strategien, die ich hier auflisten werde. Überlegen Sie, ob Sie wirklich einen iframe benötigen. Wenn nicht, reicht ein Überlauf-Div, Sie können den Standard "overflow:scroll;" oder "-webkit-overflow-scrolling:touch;" verwenden, oder eine Bibliothek wie iScroll einsetzen. Sie könnten sogar ein anderes Dokument mit AJAX laden. In unserem Fall benötigen wir einen iframe, damit wir einen separaten Kontext von CSS und Javascript haben. Sie sollten sich die Testfälle auf Ihrem iPad oder Android-Tablet ansehen - ich habe Site To Phone als ein praktisches Tool entdeckt, wenn Sie Link Sharer noch nicht haben.
Strategien
iFrame in einem Overflow-Div #1: -webkit-overflow-scrolling: touch
Testfall:
iFrame will sich vertikal ausdehnen? Gut, lassen Sie es einfach zu. Setzen Sie es in einem div mit einer festen Höhe. Aber wie scrollt man ihn dann, wenn man "overflow:scroll;" auf das umschließende Div anwendet, funktioniert das nicht. Aber das neue "-webkit-overflow-scrolling: touch;" auf dem umschließenden div funktioniert! Aber es kann zu Rendering-Problemen kommen.
Einige Personen haben diese Probleme gelöst, indem sie dafür gesorgt haben, dass die Div-Inhalte nur statisch positioniert sind. Eine Person berichtete, dass sie das Problem durch Erzwingen der Hardwarebeschleunigung lösen konnte.(SO: Bilder verschwinden)(SO: webkit-overflow-overflow-Fehler)
In unserem Fall haben wir keine Kontrolle über den Inhalt des iframe, so dass dies nicht in Frage kommt.
Bei meinen eigenen Experimenten mit Iframes trat das Rendering-Problem immer dann auf, wenn der Inhalt des Iframes eine größere Breite oder Höhe hatte als die enthaltende Seite. (Vielleicht iOS zuweist Grafikspeicher auf der Grundlage der übergeordneten Dokumentgröße?)
Eine Strategie hierfür ist einfach, die Größe des übergeordneten Körpers so zu erhöhen, dass sie der Größe des Iframe-Inhalts entspricht (wie hier vorgeschlagen: SO iframe content not rendering).
Das ist die Lösung, die bei uns funktioniert. Der Rest unserer App-Elemente sind absolut positioniert und unser iframe hat eine Breite von 100 %, so dass ein Benutzer nicht bemerkt, dass wir eine hohe Seite erstellt haben, wenn wir den iframe-Inhalt laden. Das zusätzliche Problem, das dadurch entstanden ist, besteht darin, dass, sobald der Benutzer bis zum unteren Rand des iframe gescrollt hat, bei der *nächsten* Abwärts-Scroll-Geste die gesamte App nach oben gescrollt wird. Ups. Bis Apple den Fehler behebt, besteht unsere Lösung darin, Scroll-Ereignisse zu erfassen und das Dokument scrollTop wieder auf Null zu setzen, wenn wir ein Scrollen des Dokuments feststellen.
iFrame in einem Overflow Div #2: Javascript Scrolling
Testfall:
Ein anderes Konzept, das hier erforscht wurde(SO: iframe js scrolling), besteht darin, den iframe wie oben in ein div zu setzen und Touch-Event-Handler in das iframe-Dokument einzufügen, um die Drag-Events zu erfassen. Dieser Code ruft dann das übergeordnete Dokument auf, um den iframe innerhalb des div zu verschieben. Ich habe damit experimentiert, aber in meinen kurzen Tests ruckelte der iframe-Inhalt herum, anstatt gleichmäßig zu scrollen - verständlich angesichts der seltsamen Situation. Vielleicht ließe sich das glatt machen - aber ich habe noch kein Beispiel dafür gesehen. Diese Lösung könnte nur funktionieren, wenn Sie den Inhalt des iframe kontrollieren, da Sie Ihr Javascript zur Berührungserkennung darin haben müssen.
Wenn jemand diesen Ansatz gut hinbekommt, bitte einen Kommentar abgeben.
Feste Steuerelemente & Scrolling iFrame Seite
Testfall:
Eine andere Strategie besteht darin, den iframe einfach zu erweitern und die Seite zu füllen, ihn aber mit position:fixed-Elementen wie einer Kopfzeile, Seitenleisten und sogar einer Fußzeile zu umgeben. Elemente mit fester Position funktionieren gut in iOS 5, und Sie könnten auf Javascript zurückgreifen, um die festen Elemente nach einem Scrollen in iOS 4 und darunter neu zu positionieren. Das hat bei uns fast funktioniert.
Popup-Fenster
Der Vollständigkeit halber: Wenn alles andere fehlschlägt, können Sie den Iframe-Inhalt einfach in einem Popup-Browserfenster aufrufen. Ich weiß, ich weiß.
Objekt anstelle von iFrame
Testfall: Abgedeckt in der ersten Strategie.
Ich habe einige Hinweise auf die Verwendung eines Objekt-Tags anstelle eines iframe gesehen, um das Problem zu lösen.
Ein Objekt-Tag behält in der Tat seine Größe, wie es sollte - aber der Inhalt war in meinen Tests immer noch nicht scrollbar. Ich habe versucht, die Größe des Objekt-Elements zu erweitern und es in ein Überlauf-Div zu setzen, genau wie der iframe - aber es hatte genau die gleichen Rendering-Probleme wie der iframe, also sehe ich keinen Vorteil für das Objekt-Tag.
Beispiel eines Object-Tags zum Einbetten einer anderen Webseite:
<object type="text/html" data="content-to-scroll.html"></object>
Anmerkungen
Es ist interessant zu sehen, dass ein div mit -webkit-overflow-scrolling: touch anders gerendert, es scheint ein bisschen verschwommen. Ich vermute, dies ist die Hardware-Beschleunigung - wie es erinnert mich an eine, wie eine Hardware-beschleunigte css Übergang div aussieht, können Sie sehen, es Pop zurück zu Schärfe, wenn der Übergang vorbei ist.
Fazit
Was für ein Schlamassel, nicht wahr? Es gibt einige Strategien, aber sie haben alle ihre Tücken. Ich freue mich über Ihr Feedback und Ihre Vorschläge. Wie haben Sie die Situation gemeistert? Möchte sich jemand von Apple dazu äußern?
Kommentare
Christopher Zimmermann am 18. Mai 2012: Nachtrag: Der Kontext dieses Problems ist, dass wir an einem Seiteneditor für unser Produkt Magnolia CMS arbeiten. Wir brauchen den Kontext des CMS-Editors mit seinen Werkzeugen und seiner Navigation - und müssen auch die Website laden, die gerade bearbeitet wird. Ein IFrame ist eine großartige Lösung, da er den CSS/Javascript-Kontext getrennt hält und es uns im Idealfall ermöglicht, unsere Anwendung als Vollbildanwendung beizubehalten - und dennoch durch die zu bearbeitende Website zu scrollen.
David Gee am 27. Juli 2012: Danke für den sehr informativen Artikel. Ich verbrachte einige Zeit mit diesem Problem zu kämpfen, keine der Lösungen, die Sie oben aufgeführt haben, funktionierte ganz perfekt für mich. Ich nahm schließlich einen völlig anderen Ansatz, der bisher sehr robust zu sein scheint - das Anhängen eines Load-Handlers an den iframe, der den Dokumentinhalt des iframe in ein scrollbares div umhüllt. Ich rufe diese Funktion aus dem Parent auf:
$(function(){ if (/iPhone|iPod|iPad/.test(navigator.userAgent)) { $("iframe").bind("load",function() { var b = this.contentWindow.document.body; var div = $(document.createElement("div")); var divCSS = { 'height' : $(this.parentNode).height(), 'width' : $(this.parentNode).width(), 'overflow' : 'scroll' } div.css(divCSS); // Verschieben Sie die Kinder des Körpers in diesen Wrapper while (b.firstChild) { div.append(b.firstChild); } // Fügen Sie den Wrapper an den Körper an $(b).append(div); }); } })
Christopher Zimmermann am 8. August 2012: Danke David, guter Ansatz! Ich weiß es zu schätzen, dass du deinen Code zu dem Beitrag hinzugefügt hast. In meinem Fall brauche ich jedoch den unabhängigen Webkontext, den ein Iframe für das Javascript & CSS bietet.
Helle am 18. Juni 2013: Danke, du hast heute meine Welt gerettet! Danke auch für den ganzen Artikel!
Rich Brill am 12. November 2014: @David, danke für diesen Ansatz und danke @Christopher für einen interessanten Artikel.
@David Ich fand Ihren Ansatz nicht ganz für mich arbeiten, es sei denn, ich legte eine Höhe auf die div mit dem Körper (alle innerhalb der iframe Dokument).
Ich hatte Probleme mit dem Kendo UI Editor von Kendo Version v2013.2.918 auf einem iPad und dein kleines Skript hat mich inspiriert, es mit jQuery zu versuchen:
$('iframe.k-content').contents().find('body').wrapInner(");
Rich Brill am 12. November 2014: @Christopher, sorry, mein obiger Code scheint von Ihrem Blog "gefiltert" worden zu sein. Der String innerhalb der wrapInner-Methode sollte sein:
ein öffnendes div-Tag mit dem folgenden Attribut:
style="overflow:auto;-webkit-overflow-scrolling:touch;height: 100%"
und dann ein abschließendes div-Tag. Prost!
Deb am 2. August 2012: Einer(https://topherzee.github.io/iframe-on-ipad-blog/iframe-js.html) Ihrer Ansätze funktioniert bei mir, aber ich stehe vor einem Problem. Die Website, an der ich arbeite, erfordert die Anzeige mehrerer (dynamischer Anzahl) PDFs auf einer Seite. Während die PDFs mit Hilfe Ihres Codes mit zwei Fingern gescrollt werden können, sind sie nicht zentriert und werden auf der rechten Seite abgeschnitten.
Haben Sie das gleiche Problem? Haben Sie eine Idee, wie man sie innerhalb des iFrames zentrieren kann?
Vielen Dank für diesen Beitrag. Dies ist die einzige Lösung, die ich gefunden habe und die mit einer kleinen Änderung funktioniert.
Christopher Zimmermann am 8. August 2012: Hallo Deb, es freut mich, dass dir der Beitrag ein wenig geholfen hat. Ich habe noch nicht mit PDFs in einem iframe gearbeitet, ich nehme an, dass es nicht viel Kontrolle darüber gibt, wie sie angezeigt werden. Könnten Sie das Problem vielleicht durch die Positionierung des iframe auf der Seite lösen (und nicht durch die Positionierung der PDF im iframe)? Könnten Sie zum Beispiel einen negativen linken Rand (margin-left: -200px;) auf den iframe anwenden?
Ich habe das nicht ausprobiert - das ist nur eine Idee.
Viel Glück!
Auch Im neugierig: Was war der Tweak, die Sie brauchte, um eine meiner Ansätze zu arbeiten?
Bob Jansen am 27. September 2012: Der Benutzer csdco(https://github.com/fancyapps/fancyBox/issues/2#issuecomment-5997068) hat eine einfache Antwort gegeben, die das Problem für mich gelöst hat.
------------
Es ist viel einfacher, überlaufende Divs zu steuern als Iframes, und die Probleme mit dem Scrollen und dem leeren Inhalt gehen bis zu iOS 4 zurück, wo ich früher nicht einmal in der Lage war, das 2-Finger-Scrollen (in Iframes) zum Laufen zu bringen.
Es geht ungefähr so:
a-file.html:
…
Silvia on 29 November, 2012: Have u ever use “include ” instead of iframe ? “Server side include” ~ It is similar to PHP’s include ~ Need a sever to test the result , but I don’t have a server…XD
Squriber on 14 February, 2013: If you have control over the contents in your iframe, add the -webkit-transform: translate3d(0,0,0) CSS style to force hardware rendering to get rid of the blank space when the iFrame is scrolled (iOS5 bug). Try something like this:
$('iframe').load(function(){ $('iframe').contents().find('body').css('-webkit-transform', 'translate3d(0, 0, 0)'); });
jaewik@gmail.com on 20 March, 2013: You could even load another document in with AJAX. In our case we require an iframe so that we have a separate context of CSS and Javascript.
Tobias on 10 October, 2013: Great explanation, which helped me a lot! I finally used the following approach (since I required a “sticky-top-right” container):
HTML: CSS: #container { width: 210px; height: 100%; display: block; z-index:100000001; position:fixed; top: 0px; right: 0px; -webkit-overflow-scrolling: touch; } #frame { height: 100%; width:195px; z-index:100000002; } // Und etwas jQuery: if (/iPhone|iPod|iPad/.test(navigator.userAgent)) { $("#container").css({ overflow: 'scroll' }); }
Maybe this helps others, too.
John on 4 December, 2013: Thanks, Christopher and Tobias! I had the similar problem, which you’ve solved!
Christopher Zimmermann on 18 November, 2013: Cool – thanks for your input.
Timothy Nott on 10 March, 2014: Even though the object example didn’t work for you, I am infinitely grateful that you included it — it does the job perfectly for my use case. Thanks! Why on earth can’t they just FIX this?
Ben Pearson on 2 April, 2014: Thanks very much for this post Christopher. I found it very helpful.
Mike on 5 August, 2014: I am having an issue where the iframe on the ipad just will not expand to teh full width no matter what. Has anyone seen that?
Robert Kirkwood on 25 September, 2015: I am seeing that as well. I have tried many CSS tricks to no avail. This is a big issue for us.
Alan on 23 August, 2014: Thanks for the post. Confirms I’m on the right path for my project. One issue I’m finding is links within the iFrame. How do you scroll the parent DIV back to the top when a user clicks a link to load new content in iFrame?
Was using the following code:
window.parent.parent.scrollto(0,0);
This doesn’t work when setting the parent DIV with -webkit-overflow-scrolling: touch;
thinsoldier on 20 October, 2014: Forcing hardware rendering on the page within the iframe by using a css 3d transform will fix the problem with some parts not being rendered.
But there is another problem if you have a form submit button on the page within your iframe. It will not work if it is very far down the page (or it might work once but if the form is displayed again due to user input error it won’t work a second time). But if you style a div to look like a submit button and use JS on that div to force the form.submit() event you can get the form to work.
Oh, and a form submit button, within an iframe, within an iframe – is out of the question.
Raja on 10 June, 2015: Hi , I have a strange problem, I have a cordova application that provides an Iframe to show another application(web ) content, i have few text fields on the iframe content, when I tap on the text field it opens the key board but when I tap any where on the out side of the test box, keyboard is loosing its focus, now I can not type anything in the text box, it works if I explicitly dismiss the key board and tap text box. this is not an issue in android platform