Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does Google create mobile friendly fixed backgrounds and parallax content in iframe?

My current test involves an intersectionobserver that uses iframe postMessage with current scroll position in order to translate3d background image in the iframe. But this creates a lot of jitter, a potential delay when in production and I can see more issues with this approach. From what I understand playing around in the web developer tool by Google, the ads are using parallax in order to make content responsive to the scroll position, but does Google rely on postMessage for this, or what do they do in order to make a smooth experience for fixed backgrounds and content responding to scroll events in parent window?

Some code example

DEMO || CODE

Jitter

As far as I understand, the component used is called Parallax and is available in Google Web Designer

Her face is turning as you scroll (series of images changing based on yPosition) Dial and background image changing based on scroll position Fixed position background

Regarding the fixed background, I know that iOS doesn't support background-attachment: fixed, why it has to be some JS based functionality. And then triggered by the intersectionobserver in the parent window I believe. If Google ads at all rely on the parent window to provide any information, or if everything is managed from inside the iframe - I don't know. But I would like to hear if anyone knows about these techniques and the work behind it, cause it seems like something very simple, yet very unachievable for mere mortals like me.

like image 714
Corfitz. Avatar asked Mar 03 '23 04:03

Corfitz.


1 Answers

What an interesting question! I would like to start with your last gif.

Gif #3

Regarding the fixed background, I know that iOS doesn't support background-attachment: fixed, why it has to be some JS based functionality.

Even if IOS would support background-attachment: fixed this wouldn't work, because this property is only for background images. It also isn't JS (at least not necessarily), as you can just do it with CSS:

* {
  margin: 0;
  padding: 0;
}

.section {
  width: 100%;
  height: 1000px;
  background: red;
}

.spacer {
  width: 100%;
  height: 200px;
  background: transparent;
}

.background-scroller {
  background: lightgrey;
  width: 100%;
  height: 100%;
  position: fixed;
  top: 0;
  z-index: -1;
  display: flex;
  align-items: center;
  justify-content: center;
}
<div class="section"></div>
<div class="spacer"></div>
<div class="section"></div>
<div class="background-scroller">
  This could be anything
</div>

I am aware that on some sites those ads change for each "spacer", but this is also possible with CSS only by using position: sticky.

* {
  margin: 0;
  padding: 0;
}

.section {
  width: 100%;
  height: 1000px;
  background: red;
}

.spacer {
  width: 100%;
  height: 200px;
  background: transparent;
}

.background-scroller {
  background: lightgrey;
  width: 100%;
  height: 100vH;
  position: sticky;
  top: 0;
  z-index: -1;
  display: flex;
  align-items: center;
  justify-content: center;
  float: left;
}

.section-wrapper {
  position: relative;
}
<div class="section-wrapper">
  <div class="background-scroller">
    This is one thing
  </div>
  <div class="section"></div>
  <div class="spacer"></div>

  <div class="background-scroller">
    And this is something completely else
  </div>

  <div class="section"></div>
  <div class="spacer"></div>

  <div class="section"></div>
</div>

Each site does it differently and a lot of them use the intersection observer API, but my point is, that sometimes a bit of simple CSS does the job. So it is not necessarily JS! Those are some legitimate ways to do this.


Gif #2, Gif #1

Those effects are a bit more complex.

Cross-origin restrictions are a big (but necessary) bummer. Your parent frame has very restricted access to the iframe and the other way around. This leaves us with just a couple of properties we can use.

If you inspect the iframe element, you might have noticed, that they use a data-is-safeframe attribute. I copied such an element and removed every unimportant attribute.

<iframe src="" data-is-safeframe="true" sandbox="allow-forms allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-top-navigation-by-user-activation"></iframe>

What is SafeFrame? Well, the SafeFrame API is developed by IAB (Interactive Advertising Bureau) and basically allows communication between the advertiser and the ad provider. To know exactly what they use this API for is probably impossible to find out, if you are not a Google Developer.

There is only one modern way to communicate with cross-origin iframes: postMessage(). Yes, they also use SafeFrame, but I think this is not meant for things like this. There is no way (for Google too), to break this rule. To be sure, I did some testing (which was really unnecessary if I think about it) and all of my tests failed.


But this creates a lot of jitter

This surprised me as postMessage shouldn't be that slow. I made a small example with JSFiddle, because SO made some weird stuff with my iframe.

  • Here is the page that will be displayed inside the iframe: Click or Code
  • Here is the page that will display the iframe and send the postMessage() (scroll down the page (not the iframe)): Click or Code.

If you take a look at the console, you will notice that postMessage is definetly fast enough to handle fast triggering events like scroll. You can also notice it because of the "disco iframe".

So to be honest, I think the fault is on your site.


Conclusion

I don't know the exact way Google has accomplished this. However, there is only one possible way I can think of: postMessage - an approach you already chose. This means that, if there is no other way, this must be the way Google used.

Your performance issues must be a fault on your side.


So this is just my opinion. There might be technologies and ways I didn't think of. I find this question very interesting and, if my solution isn't right, I want to know the correct answer! So feel free to correct me.


Edit #1

After a lot of time-consuming investigation, I have finally found the solution from creditable sources: in a way, from Google itself. But also from various sites that I found, which take advantage of such an effect.

Short answer

It basically is like I explained at Gif #3.

Long answer

Such an ad has different names: Interscroller, Flying carpet, sticky ad, there might be more but those are the ones I've come across.

What I've found out is that ads are often written and implemented with AMP (Accelerated Mobile Pages), published by Google.

After finally having found a site that uses a Flying Carpet Ad, I could inspect it and found the following:

This confirmed my findings. They use AMP! But not only them, I've come across different sites which use the same technique.

Let's analyze the HTML structure. The part we focus on is the <amp-fx-flying-carpet> and its first parent <div class="amp-article-ad> until the <iframe>.

The basic structure with all the unnecessary stuff removed looks like the following:

<div class="amp-article-ad">
  ::before
  <amp-fx-flying-carpet>
    <div class="i-amphtml-fx-flying-carpet-clip">
      <div class="i-amphtml-fx-flying-carpet-container">
        <amp-ad class="amp-article-ad-element i-amphtml-layout-fixed i-amphtml-layout-size-defined i-amphtml-element i-amphtml-layout">
          <div fallback></div>
          <amp-analytics></amp-analytics>
          <iframe></iframe>
        </amp-ad>
      </div>
    </div>
  </amp-fx-flying-carpet>
</div>

If we reduce this even further by removing all wrappers that are unnecessary, the structure looks like this:

<div class="amp-article-ad">
  <amp-fx-flying-carpet>
    <div class="i-amphtml-fx-flying-carpet-container">
      <amp-ad>
        <iframe></iframe>
      </amp-ad>
    </div>
  </amp-fx-flying-carpet>
</div>

Let's start at the beginning. <div class="amp-article-ad"> is just the wrapper for the ad and contains <amp-fx-flying-carpet>. The size of <div class="amp-article-ad"> and <amp-fx-flying-carpet> is the same and is the window we can see the ad through:

Now comes the important part! <div class="i-amphtml-fx-flying-carpet-container"> is the wrapper of <amp-ad> and it's size is bigger then that of it's wrapper <amp-fx-flying-carpet> (the size of the ad to be presize), which already indicates that some CSS property took it out of the flow. Examples for some properties are:

  • float: left
  • float: right
  • position: fixed
  • position: absolute
  • ...

And you probably see where I am going. The CSS properties of <div class="i-amphtml-fx-flying-carpet-container"> are:

There we have it: position: fixed!

The size of <amp-ad> is that of the ad, which makes sense, since it contains the iframe, which contains the ad.

The <iframe> obviously just contains the ad.


So we have the outer wrappers which function as the window we can see through. Inside this window are containers, which are taken out of the flow by adding position: fixed. Those fixed containers contain the <iframe>, which contains the ad.


Convince yourself! Here is the official AMP documentation with examples: https://amp.dev/documentation/examples/components/amp-fx-flying-carpet/#flying-carpet-for-ads

The structure is pretty much the same. Of course, I made a small example, using the same technique:

* {
  margin: 0;
  padding: 0;
}

body {
  text-align: center;
}

p {
  width: 100%;
  background: white;
  position: relative;
  z-index: 1;
}

.flying-carpet-wrapper {
  height: 200px;
  width: 100%;
}

.amp-ad-wrapper {
  position: fixed;
  height: 100%;
  top: 0;
  left: 0;
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}

iframe {
  border: 0;
  height: 500px;
  width: 500px;
}
<p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p>

<div class="flying-carpet-wrapper"> <!--<amp-fx-flying-carpet>-->
 <div class="amp-ad-wrapper"> <!--<div class="i-amphtml-fx-flying-carpet-container">-->
   <div class="amp-ad"> <!--<amp-ad>-->
      <iframe src="https://de.wikipedia.org/wiki/Accelerated_Mobile_Pages"></iframe>
   </div>
 </div>
</div>

<p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p><p>Content</p>
like image 191
Aaron3219 Avatar answered Apr 29 '23 12:04

Aaron3219