Отслеживаем «onresize» на элементе

  • Tutorial
Достаточно древняя задача отслеживания события изменения размеров произвольного элемента на странице. Актуально при создании модульно-независимой структуры для корректировки размеров и иных сss-атрибутов нужного элемента на странице, в частности блоков с периодической Аякс-подгрузкой (например: новостных). Типично решается через таймирование и периодический опрос размеров элемента. Новый вариант, — не использующий таймирующий опрос.

Трюк: Внутрь элемента вставляем пустой фрейм с position:absolute и 100%-(ыми)размерами, придаём элементу position:relative;. И отслеживаем frame.onresize:

Тест-Код:

<div  id="Test" style="position:relative;border:red solid 1px;width:200px;height:100px;">
<iframe name="frame" width=100% height=100% style="position:absolute;z-index:-1"></iframe>
Тут контент ...
</div>

<script type="text/javascript">
frame.onresize = function(){
  alert('Размеры div #Test изменены.');
}

setTimeout(function(){  //Тестовое изменение размера через 3сек. 
  document.getElementById("Test").style.width='100px';
},3000);
</script>


Под спойлером
С учётом пожеланий, — более развёрнутый код:
Расширенный код

Код:

<div  id="Test" style="position:relative;border:red solid 1px;width:200px;height:100px;">
<!--//    Для каждого нового элемента  name= - свое!    //-->
<iframe name="frame" width=100% height=100% style="position:absolute;z-index:-1"></iframe>
Тут контент ...
</div>

<script type="text/javascript">
setTimeout(function(){ // Отработка задержки фрейма (для FF и ИЕ)
   var timerResize='first';
   frame.onresize = function(){ // frame,  - Имя фрейма (name=frame)  - cм начало Кода;
     if(timerResize!=='first')clearTimeout(timerResize);
     timerResize=setTimeout( function(){  //  Задержка для очистки  чрезмерных срабатываний;
         alert('Размеры div #Test изменены.'); // Тело обработки события «onresize»;

     },20)  // Параметр 20(ms) , - зависит от нужной скорости реагирования на повторные события;
            // актуально при плавных изменениях размера элемента,   
            // либо почти одновременное изменением размера несколькими разными процессами.
  }
},200);


setTimeout(function(){  //Тестовое изменение размера. 
  document.getElementById("Test").style.width='100px';
},3000);

setTimeout(function(){  //Тестовое изменение размера. 
  document.getElementById("Test").style.width='200px';
},7000);
</script>



AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 23

    +5
    скажите, пожалуйста, а чем этот метод хорош? я, правда не копал глубоко, но мои знакомые, когда делали для своего проекта фреймворк, руководствовались следующим образом: событие onresize может не всегда вызываться, а может вызываться сотню раз даже тогда, когда ничего не меняется. И еще, это поведение нужно учитывать во всех браузерах.

    Так вот, если вызывается метод, который может изменить состояние DOMa, они сравнивали значения новых размеров со старыми. Если было различие, то собственное событие onresize вызывалось, иначе — нет.

    я описал этот подход очеь грубо, так как в дебри не лез. Но, в принципе, это позволяло снять нагрузку с обработчика события (например, просчет размеров родителей или детей, если используется свой механизм ресайзинга).

    просто мне кажется, что добавление айфрейма без источника в каждый нужный элеменет, это как-то накладно.
      –4
      ))) — У кого-то уже работает Ccыль
      –3
      Vest сказал(а): просто мне кажется, что добавление айфрейма без источника в каждый нужный элеменет, это как-то накладно.
      Во первых: вряд ли есть необходимость cтавить на каждый элемент, правильнее тогда уж на весь window однократно. Во вторых — типичное решение: это таймирование в реальном масштабе, это интервал опроса размеров не более 200ms. Однократная установка iframe или же бесконечный опрос по таймеру? (считаем пока для единичного экземпляра)
      По таймеру накапливается и какой то мусор в процессе таймирования и, если смотреть загрузку процессора, — таймирование её существенно повышает.

      Собственно пустой фрейм достаточно неплохое решение для контроля размеров на нескольких элементах.
      Зы: Пустой фрейм даже не требует типичной загрузки и ставиться в DOM сразу.
        +1
        я не говорил про таймер. в том-то и дело, вы если контроллируете страницу можете узнать когда вам необходимо проверить кусок ДОМа на ресайз. Предположим, вы загрузили контент динамически, проверили размер элемента и выстрелили бы событие один раз.

        например, по вашей ссылке: я сейчас пишу с андроида, так вот бета Файерфокс выстрелил алерт два раза, а вебкиты (хром и стандартный) всего по разу. Смахивает на инконсистенс :-)
          –1
          так вот бета Файерфокс выстрелил алерт два раза,

          у FF(не только бета ) процесс установки размеров фрейма проходит два этапа — дефолтный размер, только затем размер указанный в теге, поэтому можно либо установить frame.onresize с небольшой задержкой, либо запретить срабатывание на время 10-20ms
          Второе: — Ширина/высота может изменяться медленно и динамически, поэтому применяется такой приём:

          <div  id="Test" style="position:relative;border:red solid 1px;width:200px;height:100px;">
          <iframe name="frame" width=100% height=100% style="position:absolute;z-index:-1"></iframe>
          Тут контент ...
          </div>
          
          
          <script type="text/javascript">
          var timerResize='first';
          if(timerResize=='first')setTimeout('clearTimeout(timerResize)',20);
          
          frame.onresize = function(){
            clearTimeout(timerResize);
            timerResize=setTimeout( function(){ alert('Размеры div #Test изменены.')},20)
          }
          
          setTimeout(function(){  //Тестовое изменение размера через 3сек. 
            document.getElementById("Test").style.width='100px';
          },3000);
          </script>
          

          Что убирает слишком частые срабатывания при долгих и плавных или рывковых изменениях размера;

          Пример в статье дан как основа метода, не углубляясь в стандартную методологию и тонкости кроссбраузерной работы с onresize ( посколь тонкости приёмов работы с window.onresize достаточно хорошо описаны и для FF и ИЕ, — остальные браузеры вроде вопросов не вызывали. У FF, до недавнего времени, применение onresize c овместно alert () без тайминга блокировки быстрых срабатываний вызывало фатальную ошибку), — ксать и onscroll точно так же)
            0
            «Магические константы» — большое зло, у вас эта задержка 20мс, а у кого-то на нетбуке — 200мс, однозначно плохой «костыль»…
              0
              Задержка выбрана из минимального времени срабатываний setTimeout браузеров: — для настольных ПК она порядка 10-12ms, для мобильных устройств порядка 20ms.
              Первичной задержка по загрузке iframe 20ms или 200ms -не критична, посколь столь быстрое «onresize» вряд ли отследить взглядом.
              Задержка для устранения быстрых срабатываний, наверно болеее критична, (исследовалась только для браузеров на ПК), в большинстве случаев приведённая цифра — устраивала.
        –2
        Vest сказал(а): Предположим, вы загрузили контент динамически, проверили размер элемента и выстрелили бы событие один раз.
        Это тоже можно сделать двояко:
        1. После первого срабатывания — мы обычно проводим модификацию расположения контента, к примеру отрезаем лишнее и переносим в соседнюю колонку, туда нужно перенести (или создать новый, удалить старый iframe и заново настроить событие (так что второго срабатывания не будет).
        2. Вариант два: отключать проверку на «ресайс» во время загрузки контента, в течении данного Aякс-запроса и включать по окончании.
          –2
          Собственно резюме:
          Основной( и единственный) метод ранее, — периодическое тестирование размеров элемента по таймеру, через краткий промежуток времени, и достаточно ресурсоёмкий, и оставляет достаточно много «мусора» в памяти, вследствии постоянного воспроизведения своих функций и параметров.
          Предложен новый вариант, позволяющий привязать событие «onresize» к произвольному элементу, чего ранее не было.
          Данный код короче и, по своей идее привязки к конкретному элементу, — более прозрачен и менее ресурсоёмок для малого количества обследуемых элементов.
            0
            Вместо ставить минусы — отпишитесь и предложите свой, иной вариант, кроме стандартного периодического тестировани по таймеру.
              0
              Легко — я процитирую комментарий Vest, который был отправлен за два часа до Вашего предложения представить свой, иной вариант:
              я не говорил про таймер. в том-то и дело, вы если контроллируете страницу можете узнать когда вам необходимо проверить кусок ДОМа на ресайз. Предположим, вы загрузили контент динамически, проверили размер элемента и выстрелили бы событие один раз.


              Это и есть иной вариант. И там нет периодического тестирования по таймеру.
                –1
                Approved_by_Biolanteнаписал(а):
                Легко — я процитирую комментарий:… вы если контроллируете страницу можете узнать когда вам необходимо проверить кусок ДОМа на ресайз. Предположим, вы загрузили контент динамически, проверили размер элемента и выстрелили бы событие один раз.
                Это отличный вариант при заранее известном ограниченном кол-ве Аякс-подгрузок.
                Представьте подгрузку RSS ленты на Инфо-сайт из пяти-шести источников одновременно короткими месагами. Они могут грузиться в один общий блок своими подблоками и делать проверку из каждой Аякс-подгрузки не айс, и она мало что даёт к общей картине. Однократное срабатывание «ресайза» айфреймом на общем блоке, позволяет общаться для правки лишь с последним подблоком.
              0
              Подскажите кто-нибудь какой нибудь практический пример, где нужно контролировать «onresize» на элементе?
                –1
                demimurych написал(а): практический пример, где нужно контролировать «onresize» на элементе

                Расползся элемент верстки из-за длинной строки,
                При вставке Аяксом убежала колонка, не расчитанная на длину контента,
                При анимации нужен контроль уменьшения/увеличения элемента более предельных значений( ограничиваем min-height/min-widh у фрейма(или max)
                Движущиеся блоки выходят за границу контейнера, (*ставим фрейм на контейнер
                  +1
                  Простите, но среди описанных Вами задач, я не увидел ни одной которую нельзя было бы решить другим способом, нежели эмулированием события онресайз. И по моему мнению, эти способы правильнее чем применение такого костыля.

                  Хотя, признаюсь, всегда испытывал нежные чувства к таким «необычным» решениям.

                    0
                    А как насчет такого примера:
                    Требуется кастомный скролл, и, как порядочный скролл, он должен менять размер ползунка в зависимости от размеров scrollable-элемента. Элемент с этим скроллом может менять размеры не только вследствие вставки/удаления  child-элементов, но и вследствие изменения размеров соседних элементов (т.е DOM Mutation события не подходят).

                    с удовольствием послушаю альтернативные варианты решения.  Вариант «послать всех нафиг» и использовать стандартный скролл — не предлагать (-:

                    P.S.
                    автору огромное спасибо! Хак оказался очень кстати)
                      0
                      mank_devно: и вследствие изменения размеров
                      Тут с подобной необходимостью можно столкнуться и при масштабировании окна браузера колёсиком мыши, хотя масштаб можно отследить и по window.onresize окна браузера.
                        0
                        Сразу же напрашивается вопрос — а изменения child элементов с неба падают? Очевидно что их инициирует Ваш же код.
                        И как вывод, должен быть какой-то обьект который содержит данные о элементах, чей размер Вам нужно контролировать.
                        Любой же код инициирующий какие-либо изменения на странице должен иметь какой — нибудь «хук» дающий команду обсерверу проверить состояние обьектов чей размер Вам нужно контролировать.

                        Это всего лишь вопрос архитектуры Вашего веб приложения.

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

                          0
                          demimurych Собствено споры лишь о том — Что экономичней — делать проверки в каждой операции добавки-изъятия или по событию? -Если операций много и разных — возможно работа по единственному событию «onresize» — удачнее многочисленных проверок. Применение может быть обосновано и при употреблении чужих скриптов и библиотек, разбираться в которых нет времени.
                            0
                            ну очень спорно: кому-то больше подходит одно модульное решение, которое достаточно универсально и хорошо решает проблему, позволяя сделать и забыть (пускай даже такой хак).
                            Альтернатива, которую предлагаете мне вы: каждый раз при наращивании функционала приложения впихивать во все необходимые места вызов хука для пересчета (ну или файрить какие-то кастомные события).

                            Я за первый вариант, т.к. считаю, что незачем создавать лишние связи и заставлять соседние модули «помнить», что нужно изменить какой-то левый блок.
                    +1
                    demimurych, Спасибо за чувства. Но имхо суть топика: Ранее методов отслеживания близких к реальному «onresize» на произвольном элементе — не было. Использовали и используют таймер, несмотря на некое недоумение неявоскриптёров.

                    Тут предлагается практически равноценный аналог «onresize» на произвольном элементе. Нужность — не нужность оценится уже практикой применения. Ранее jQuery — не было, сейчас многие без оной не представляют верстку. т.е.- Есть вариант новой стамески, а «пользоваться — не пользоваться?» — решать Вам. Возможно круглая с фаской Вам не подходит(но для кого-то востребована.)
                      0
                      Коллеги, для не-совсем-верстальщиков:

                      Если я на сегодняшний день вызову в скрипте без jQuery
                      window.onresize = func; // при изменении размеров браузера
                      window.onload = func; // при загрузке страницы


                      Функция сработает? Или по-прежнему с таймером?
                        0
                        Спасибо за статью, мне помогло

                        Only users with full accounts can post comments. Log in, please.