Pull to refresh

Парочка приемов работы с iframe

Reading time 5 min
Views 17K
Доброго времени суток, друзья!

Задача


Сделать нечто похожее на Dashboard на Codepen.

Результат должен быть примерно следующим:


Возможное решение


Разметка одной секции может выглядеть так:
<section>
    <h3>Title</h3>
    <div class="viewport">
        <iframe src="index.html" seamless scrolling="no"></iframe>
    </div>
    <div class="buttons">
        <button onclick="window.open('index.html', '_blank')" title="fullscreen">demo</button>
        <button onclick="document.location='code.7z'" title="download">code</button>
    </div>
</section>

Что здесь интересного?

Поскольку каждый блок имеет заголовок, мы может обернуть его в section (согласно спецификации section и article должны иметь заголовки).

.viewport — блок, содержащий фрейм (далее мы будет называть его просто блоком).

Атрибут src ссылается на содержимое фрейма, которое заменено «кавером» с помощью JS (об этом далее).

Seamless определяет, что содержимое фрейма должно отображаться так, словно оно является частью документа (в настоящее время не поддерживается).

Scrolling=«no» запрещает отображение полос прокрутки во фрейме.

Window.open — один из способов открыть содержимое фрейма ("_blank" — в новой вкладке).

Document.location — один из способов скачать файл.

Весь HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width: device-width">
    <title>Iframe</title>
    <link rel="stylesheet" href="style.css">
    <script src="script.js" defer></script>
</head>
<body>
    <header>
        <h2>Title</h2>
    </header>
    <main>
        <section>
            <h3>Title</h3>
            <div class="viewport">
                <iframe src="index.html" seamless scrolling="no"></iframe>
            </div>
            <div class="buttons">
                <button onclick="window.open('index.html', '_blank')" title="fullscreen">demo</button>
                <button onclick="document.location='code.7z'" title="download">code</button>
            </div>
        </section>
        
        <section>
            <h3>Title</h3>
            <div class="viewport">
                <iframe src="index.html" seamless scrolling="no"></iframe>
            </div>
            <div class="buttons">
                <button onclick="window.open('index.html', '_blank')" title="fullscreen">demo</button>
                <button onclick="document.location='code.7z'" title="download">code</button>
            </div>
        </section>
        
        <section>
            <h3>Title</h3>
            <div class="viewport">
                <iframe src="index.html" seamless scrolling="no"></iframe>
            </div>
            <div class="buttons">
                <button onclick="window.open('index.html', '_blank')" title="fullscreen">demo</button>
                <button onclick="document.location='code.7z'" title="download">code</button>
            </div>
        </section>
        
        <section>
            <h3>Title</h3>
            <div class="viewport">
                <iframe src="index.html" seamless scrolling="no"></iframe>
            </div>
            <div class="buttons">
                <button onclick="window.open('index.html', '_blank')" title="fullscreen">demo</button>
                <button onclick="document.location='code.7z'" title="download">code</button>
            </div>
        </section>
    </main>
    <footer>
        <p>© All rights reserved.</p>
    </footer>
</body></html>


Проблема № 1. Вписать фрейм в блок


По умолчанию размер iframe в Chrome составляет 304х154px.

Применим к section следующие стили:
section {
    margin: 1em;
    width: 300px;
    background: rgba(0, 0, 0, 0.15);
    border-radius: 5px;
    box-shadow: inset 0 0 50px rgba(0, 0, 0, 0.3);
}

Установка ширины section в 300px (+meta name=«viewport» и display: flex у родительского элемента) обеспечивает одинаковое отображение фрейма на экранах с различным разрешением.

Получается так:


А должно быть так:


Как видим, проблема заключается в том, что содержимое отображается во фрейме в натуральную величину и сам фрейм немного выходит за границы блока.

Одним из способов решения данной проблемы является масштабирование iframe:
/*
для сохранения пропорций берем одно из "популярных" разрешений экрана, например, 1024х768px,
уменьшаем масштаб фрейма до 25%,
указываем координаты точки, относительно которой будет происходить трансформация - 0 0 (верхний левый угол)
*/
iframe {
    width: 1024px;
    height: 768px;
    transform: scale(.25);
    transform-origin: 0 0;
}

Далее определяем размеры блока. Ширина: 1024 * 0.25 = 256px, высота: 768 * 0.25 = 192px.

Весь CSS
@import url("https://fonts.googleapis.com/css?family=Playfair+Display|Roboto&display=swap");

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    min-height: 100vh;
    background: radial-gradient(circle, skyblue, steelblue) fixed;
    display: flex;
    flex-direction: column;
    font-family: "Playfair Display", serif;
    text-align: center;
    color: #222;
}

h2 {
    font-size: 2em;
    text-transform: uppercase;
    text-shadow: 1px 1px #ddd;
    user-select: none;
}

main {
    flex-grow: 1;
    display: inherit;
    flex-wrap: wrap;
    justify-content: space-evenly;
    align-content: space-evenly;
}

section {
    margin: 1em;
    width: 300px;
    background: rgba(0, 0, 0, 0.15);
    border-radius: 5px;
    box-shadow: inset 0 0 50px rgba(0, 0, 0, 0.3);
}

.viewport {
    margin: auto;
    width: 256px;
    height: 192px;
    overflow: hidden;
}

iframe {
    width: 1024px;
    height: 768px;
    transform: scale(.25);
    transform-origin: 0 0;
}

h3 {
    padding: 0.5em 0;
    font-size: 1em;
    letter-spacing: 2px;
    text-transform: uppercase;
    text-align: center;
    color: #ddd;
    text-shadow: 1px 1px 0 #222;
    user-select: none;
}

button {
    border: none;
    outline: none;
    margin: 0.75em 0;
    padding: 0.75em;
    width: 100px;
    background: linear-gradient(to bottom, skyblue, steelblue);
    font-family: inherit;
    font-weight: bold;
    letter-spacing: 2px;
    color: inherit;
    text-transform: uppercase;
    border-radius: 10px;
    box-shadow: 0 0 2px rgba(0, 0, 0, 0.4);
    cursor: pointer;
    transition: .2s;
}

button + button {
    margin-left: 1em;
}

button:active,
button:checked,
button:focus,
button:hover {
    background: radial-gradient(steelblue, skyblue);
    color: #ddd;
    box-shadow: inset 0 0 2px rgba(0, 0, 0, .4);
}


Проблема № 2. Отображение содержимого при наведении курсора


При значительном количестве фреймов на странице, особенно с динамическим содержимым, мы получим очень долгую загрузку/перезагрузку и постоянные лагания при скроллинге.

Для того, чтобы решить данную проблему, мы отключаем содержимое фреймов и заменяем его картинкой. Картинка должна иметь размер 1024х768px. Если быть более точным, то мы меняем адреса файлов — значение src (на Codepen эта проблема решается с помощью короткой анимации содержимого фрейма). При наведении курсора на определенный фрейм должно отображаться его содержимое.

Решение может быть таким:
// находим все фреймы в документе
let iframes = document.querySelectorAll("iframe")

// перебираем массив (на самом деле здесь мы имеем дело с объектом Nodelist, но это неважно)
for (let i = 0; i < iframes.length; i++){
    let iframe = iframes[i],
    
    // сохраняем оригинальное значение srс
    originalSrc = iframe.src
    
    // заменяем содержимое фрейма картинкой
    iframe.src = "cover.jpg"
    
    // при наведении курсора отображаем содержимое фрейма
    iframe.addEventListener("mouseover", () => iframe.src = originalSrc)
    
    // возвращаем "кавер"
    iframe.addEventListener("mouseout", () => iframe.src = "cover.jpg")
}

Результат можно посмотреть здесь.

Благодарю за внимание.
Tags:
Hubs:
+2
Comments 10
Comments Comments 10

Articles