En boy oranı varsayımı olmadan iframe'i nasıl duyarlı hale getirebilirim?


14

En boy oranını varsaymadan iframe'i nasıl duyarlı hale getirebilirim? Örneğin, içeriğin oluşturulmadan önce bilinmeyen herhangi bir genişliği veya yüksekliği olabilir.

Not: Javascript kullanabilirsiniz.

Misal:

<div id="iframe-container">
    <iframe/>
</div>

Boyut Bu iframe-containeryüzden o zorlukla uygun içinde fazladan boşluk bırakmadan, diğer bir deyişle, kaydırma yapmadan gösterilebilir böylece içerik için yeterli alan, ancak hiçbir fazlalık boşluk olduğundan bunun içeriği. Konteyner iframe'i mükemmel bir şekilde sarar.

Bu , içeriğin en boy oranının 16: 9 olduğu varsayılarak iframe'in nasıl duyarlı hale getirileceğini gösterir. Ancak bu soruda, en boy oranı değişkendir.


1
Let @ bir değişiklik ile @TravisJ
ferit

2
İç çerçevedeki içeriğin boyutlarını hesaplamak mümkündür, ancak tam olarak nasıl yapılması gerektiği, içeriğin kendisine ve bir çok kullanılan CSS'ye bağlıdır (kesinlikle konumlandırılmış içeriğin ölçülmesi son derece zordur). Bir css sıfırlama dosyası kullanıyorsanız, sonuç sıfırlama dosyasının kullanılmadığı sayfadan farklıdır ve tarayıcılar arasında da farklılık gösterir. İframe ve üst öğesinin boyutunu ayarlamak önemsizdir. Bu nedenle, sayfanın yapısı, özellikle kullanılan CSS sıfırlama dosyalarının adları (veya ortak dosya (lar) kullanmıyorsanız gerçek CSS) hakkında daha fazla bilgiye ihtiyacımız var.
Teemu

2
İframe'e yüklenen belge üzerinde kontrolünüz var mı? Yani, eğer yapmazsan, yapabileceğin hiçbir şey yok. Her şey iframe belgesi içinde (veya bu belgeye erişerek) yapılmalıdır, aksi takdirde bu mümkün değildir.
Teemu

1
Hayır, o zaman mümkün değildir, bkz. Developer.mozilla.org/en-US/docs/Web/Security/… . Ana sayfa, güvenlik nedeniyle iframe'deki bir etki alanları arası belgeye erişemez (ve tersi de geçerlidir), bu ancak iframe'de gösterilen belge üzerinde kontrolünüz varsa önlenebilir.
Teemu

1
... 15 yıllık internetin imkansız olduğunu söylemek yeterli değil mi? Bu konuda gerçekten yeni bir soruya ihtiyacımız var mı?
Kaiido

Yanıtlar:


8

Boyutunu elde etmek için Javascript kullanarak farklı bir orijinli iFrame ile etkileşim kurmak mümkün değildir; Bunu yapmanın tek yolu kullanmaktır window.postMessageile targetOriginetki alanınıza seti veya wildchar *iFrame kaynaktan. Farklı kaynak sitelerin içeriğini ve kullanımını proxy yapabilirsiniz srcdoc, ancak bu bir hack olarak kabul edilir ve SPA'lar ve daha birçok dinamik sayfa ile çalışmaz.

Aynı başlangıç ​​iFrame boyutu

Varsayalım ki, aynı yüksekliğe ve sabit genişliğe sahip iki aynı kökenli iFramesimiz var:

<!-- iframe-short.html -->
<head>
  <style type="text/css">
    html, body { margin: 0 }
    body {
      width: 300px;
    }
  </style>
</head>
<body>
  <div>This is an iFrame</div>
  <span id="val">(val)</span>
</body>

ve uzun yükseklik iFrame:

<!-- iframe-long.html -->
<head>
  <style type="text/css">
    html, body { margin: 0 }
    #expander {
      height: 1200px; 
    }
  </style>
</head>
<body>
  <div>This is a long height iFrame Start</div>
  <span id="val">(val)</span>
  <div id="expander"></div>
  <div>This is a long height iFrame End</div>
  <span id="val">(val)</span>
</body>

loadOlayı kullanarak iframe.contentWindow.documentana pencereye göndereceğimizi kullanarak iFrame boyutunu alabiliriz postMessage:

<div>
  <iframe id="iframe-local" src="iframe-short.html"></iframe>
</div>
<div>
  <iframe id="iframe-long" src="iframe-long.html"></iframe>
</div>

<script>

function iframeLoad() {
  window.top.postMessage({
    iframeWidth: this.contentWindow.document.body.scrollWidth,
    iframeHeight: this.contentWindow.document.body.scrollHeight,
    params: {
      id: this.getAttribute('id')
    }
  });
}

window.addEventListener('message', ({
  data: {
    iframeWidth,
    iframeHeight,
    params: {
      id
    } = {}
  }
}) => {
  // We add 6 pixels because we have "border-width: 3px" for all the iframes

  if (iframeWidth) {
    document.getElementById(id).style.width = `${iframeWidth + 6}px`;
  }

  if (iframeHeight) {
    document.getElementById(id).style.height = `${iframeHeight + 6}px`;
  }

}, false);

document.getElementById('iframe-local').addEventListener('load', iframeLoad);
document.getElementById('iframe-long').addEventListener('load', iframeLoad);

</script>

Her iki iFrame için de uygun genişlik ve yükseklik elde edeceğiz; buradan çevrimiçi kontrol edebilir ve burada ekran görüntüsünü görebilirsiniz .

Farklı kökenli iFrame boyutu kesmek ( önerilmez )

Burada açıklanan yöntem bir hack'tir ve kesinlikle gerekliyse ve başka bir yol yoksa kullanılmalıdır; dinamik olarak oluşturulan sayfaların ve SPA'ların çoğunda çalışmaz . Yöntem, sayfa HTML kaynak kodunu CORS politikasını atlamak için bir proxy kullanarak getirir ( cors-anywherebasit bir CORS proxy sunucusu oluşturmanın kolay bir yoludur ve çevrimiçi bir demosu vardırhttps://cors-anywhere.herokuapp.com ), daha sonra kullanmak postMessageve boyutunu göndermek için bu HTML'ye JS kodu enjekte eder . Üst belgeye iFrame. Hatta iFrame resize( iFrame ile birliktewidth: 100% ) olayını işler ve iFrame boyutunu üst öğeye geri gönderir.

patchIframeHtml:

İFrame HTML kodunu yamalamak ve postMessageiFrame boyutunu üste loadve açık olarak göndermek için kullanılacak özel Javascript enjekte etmek için bir işlev resize. originParametre için bir değer varsa, <base/>o başlangıç ​​URL'sini kullanarak başlığa bir HTML öğesi eklenecektir, bu nedenle, gibi HTML URI'leri /some/resource/file.extiFrame içindeki başlangıç ​​URL'si tarafından düzgün bir şekilde getirilecektir.

function patchIframeHtml(html, origin, params = {}) {
  // Create a DOM parser
  const parser = new DOMParser();

  // Create a document parsing the HTML as "text/html"
  const doc = parser.parseFromString(html, 'text/html');

  // Create the script element that will be injected to the iFrame
  const script = doc.createElement('script');

  // Set the script code
  script.textContent = `
    window.addEventListener('load', () => {
      // Set iFrame document "height: auto" and "overlow-y: auto",
      // so to get auto height. We set "overlow-y: auto" for demontration
      // and in usage it should be "overlow-y: hidden"
      document.body.style.height = 'auto';
      document.body.style.overflowY = 'auto';

      poseResizeMessage();
    });

    window.addEventListener('resize', poseResizeMessage);

    function poseResizeMessage() {
      window.top.postMessage({
        // iframeWidth: document.body.scrollWidth,
        iframeHeight: document.body.scrollHeight,
        // pass the params as encoded URI JSON string
        // and decode them back inside iFrame
        params: JSON.parse(decodeURIComponent('${encodeURIComponent(JSON.stringify(params))}'))
      }, '*');
    }
  `;

  // Append the custom script element to the iFrame body
  doc.body.appendChild(script);

  // If we have an origin URL,
  // create a base tag using that origin
  // and prepend it to the head
  if (origin) {
    const base = doc.createElement('base');
    base.setAttribute('href', origin);

    doc.head.prepend(base);
  }

  // Return the document altered HTML that contains the injected script
  return doc.documentElement.outerHTML;
}

getIframeHtml:

useProxyParam ayarlanmışsa, bir proxy kullanarak CORS'leri atlayarak sayfa HTML'si alma işlevi . postMessageBoyut verileri gönderilirken iletilecek ek parametreler olabilir .

function getIframeHtml(url, useProxy = false, params = {}) {
  return new Promise(resolve => {
    const xhr = new XMLHttpRequest();

    xhr.onreadystatechange = function() {
      if (xhr.readyState == XMLHttpRequest.DONE) {
        // If we use a proxy,
        // set the origin so it will be placed on a base tag inside iFrame head
        let origin = useProxy && (new URL(url)).origin;

        const patchedHtml = patchIframeHtml(xhr.responseText, origin, params);
        resolve(patchedHtml);
      }
    }

    // Use cors-anywhere proxy if useProxy is set
    xhr.open('GET', useProxy ? `https://cors-anywhere.herokuapp.com/${url}` : url, true);
    xhr.send();
  });
}

Message event handler işlevi, "Aynı başlangıç ​​iFrame boyutu" ile tamamen aynıdır .

Artık özel JS kodumuzu enjekte ederek bir iFrame'in içine çapraz kökenli bir alan yükleyebiliriz:

<!-- It's important that the iFrame must have a 100% width 
     for the resize event to work -->
<iframe id="iframe-cross" style="width: 100%"></iframe>

<script>
window.addEventListener('DOMContentLoaded', async () => {
  const crossDomainHtml = await getIframeHtml(
    'https://en.wikipedia.org/wiki/HTML', true /* useProxy */, { id: 'iframe-cross' }
  );

  // We use srcdoc attribute to set the iFrame HTML instead of a src URL
  document.getElementById('iframe-cross').setAttribute('srcdoc', crossDomainHtml);
});
</script>

Ve iFrame'i overflow-y: auto, iFrame gövdesi için bile kullanmadan dikey kaydırma yapmadan içeriğinin tam yüksekliğine göre boyutlandıracağız ( bu overflow-y: hidden, yeniden boyutlandırmada kaydırma çubuğunun titrememesini sağlamalıyız ).

Online olarak buradan kontrol edebilirsiniz .

Yine bunun bir hack olduğunu ve bundan kaçınılması gerektiğini fark etmek için ; biz yapamaz erişim Çapraz Kökeni iFrame belgesi ne de şeylerin her türlü enjekte edin.


1
Teşekkürler! Mükemmel cevap.
ferit

1
Teşekkür ederim. Maalesef cors-anywhereşu anda kapalı olduğundan demo sayfasını göremiyorsunuz . Telif hakkı nedeniyle harici sitenin ekran görüntüsünü eklemek istemiyorum. Uzun süre kalırsa başka bir CORS proxy'si ekleyeceğim.
Christos Lytras

2

Esas olarak, içerik boyutunu nasıl ölçtüğünüzü bozabilecek şeyleri yapmanıza olanak tanıyan CSS nedeniyle, iframe'deki içeriğin boyutunu çalışma ile ilgili birçok sorun vardır.

Tüm bu şeylerle ilgilenen ve aynı zamanda web alanları arası çalışan bir kütüphane yazdım, yararlı bulabilirsiniz.

https://github.com/davidjbradshaw/iframe-resizer


1
Güzel! Kontrol edeceğim!
ferit

0

Benim çözüm üzerinde GitHub'dan ve JSFiddle .

En boy oranı varsayımı olmadan duyarlı iframe için bir çözüm sunmak.

Amaç, iframe'i gerektiğinde, başka bir deyişle pencere yeniden boyutlandırıldığında yeniden boyutlandırmaktır. Bu, yeni pencere boyutunu almak ve sonuç olarak iframe'i yeniden boyutlandırmak için JavaScript ile gerçekleştirilir.

Not: Sayfa yüklendikten sonra pencere tek başına yeniden boyutlandırılmadığından sayfa yüklendikten sonra yeniden boyutlandırma işlevini çağırmayı unutmayın.

Kod

index.html

<!DOCTYPE html>
<!-- Onyr for StackOverflow -->

<html>

<head> 
    <meta http-equiv="content-type" content="text/html;charset=utf-8" />
    <title>Responsive Iframe</title>
    <link rel="stylesheet" type="text/css" href="./style.css">
</head>

<body id="page_body">
    <h1>Responsive iframe</h1>

    <div id="video_wrapper">
        <iframe id="iframe" src="https://fr.wikipedia.org/wiki/Main_Page"></iframe>
    </div>

    <p> 
        Presenting a solution for responsive iframe without aspect
        ratio assumption.<br><br>
    </p>

    <script src="./main.js"></script>
</body>

</html>

style.css

html {
    height: 100%;
    max-height: 1000px;
}

body {
    background-color: #44474a;
    color: white;
    margin: 0;
    padding: 0;
}

#videoWrapper {
    position: relative;
    padding-top: 25px;
    padding-bottom: 100px;
    height: 0;
    margin: 10;
}
#iframe {
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}

main.js

let videoWrapper = document.getElementById("video_wrapper");

let w;
let h;
let bodyWidth;
let bodyHeight;

// get window size and resize the iframe
function resizeIframeWrapper() {
    w = window.innerWidth;
    h = window.innerHeight;

    videoWrapper.style["width"] = `${w}px`;
    videoWrapper.style["height"] = `${h - 200}px`;
}

// call the resize function when windows is resized and after load
window.onload = resizeIframeWrapper;
window.onresize = resizeIframeWrapper;

Bunun için biraz zaman geçirdim. Umarım beğenirsiniz =)

Düzenle Bu muhtemelen en iyi genel çözümdür. Ancak, çok küçük yapıldığında, iframe'e uygun boyut verilmez. Bu, kullandığınız iframe'e özgüdür. İframe koduna sahip olmadığınız sürece bu fenomen için uygun bir cevabı kodlamak mümkün değildir, çünkü kodunuzun iframe tarafından hangi boyutun düzgün görüntülenmesini tercih ettiğini bilmenin bir yolu yoktur.

Sadece @Christos Lytras tarafından sunulan gibi bazı hackler hile yapabilir, ancak her iframe için asla çalışmayacaktır. Sadece belirli bir durumda.


Denediğiniz için teşekkürler! Ancak, beklendiği gibi çalışmıyor. İframe içeriği küçük olduğunda, kap yine de fazladan alana sahiptir. Bkz. Jsfiddle.net/6p2suv07/3 iframe'in src'sini değiştirdim.
ferit

"Ekstra alan" ile ilgili sorununuzu anlayamıyorum. Pencere minimum boyutu 100 piksel genişliğindedir. İframe konteynere uyar. İframe'inizdeki düzen benim kontrolümde değil. Sorunu cevapladım. Daha fazla yardım istiyorsanız lütfen kendinizinkini düzenleyin
Onyr

Bir resim koyun ve açıklayın
Onyr

iframe, kabın kendisine verdiği tüm alanı alır, şey, iframe içeriği küçük olduğunda, alabileceği tüm alanı gerektirmez. Bu nedenle, konteyner iframe'e yeterli alan bırakmalı, daha az değil.
ferit

İframe'in yüksekliği için bunun nedeni, alanımın yüksekliğinin örneğimdeki pencere ile sınırlı olmasıdır. Bu mu? Örneğiniz kullandığınız iframe'e özgüdür, kabul edilmesi gereken genel bir cevap verdim. Tabii ki, iframe'inize güzel bir şekilde uyması için kodu değiştirmek mümkündür, ancak bu iframe'in arkasındaki kodu bilmenizi gerektirir
Onyr

-1

Bir şey yap Iframe kapsayıcısı bazı genişlik ve yükseklik kümesi CSS özelliği

.iframe-container{
     width:100%;
     min-height:600px;
     max-height:100vh;
}

.iframe-container iframe{
     width:100%;
     height:100%;
     object-fit:contain;
}

İçerik 600 pikselden küçükse ne olur?
ferit
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.