Обнаружение, когда iframe получает или теряет фокус

Каков правильный способ определения, когда iframe получает или теряет фокус (т.е. будет или не будет получать события клавиатуры)? В Fx4 не работает следующее:

var iframe = /* my iframe */;
iframe.addEventListener("focus", function() { /* never gets called */ }, false);

person CAFxX    schedule 28.03.2011    source источник
comment
Пожалуйста, взгляните на stackoverflow.com/questions/1926861/iframe-onblur-event.   -  person Steven Ryssaert    schedule 28.03.2011
comment
@Uw У меня нет контроля над содержимым iframe (другое происхождение), и этот метод требует доступа к iframe.contentDocument (не разрешено для документов, поступающих из другого источника)   -  person CAFxX    schedule 28.03.2011


Ответы (9)


Вы можете опросить «document.activeElement», чтобы определить, соответствует ли он iframe. Опрос не идеален, но работает:

function checkFocus() {
  if(document.activeElement == document.getElementsByTagName("iframe")[0]) {
    console.log('iframe has focus');
  } else {
    console.log('iframe not focused');
  }
}

window.setInterval(checkFocus, 1000); 
person Ryan    schedule 19.10.2012
comment
Не работает для меня. Я использую хром на странице со встроенным видео на YouTube. Нажатие на видео не изменяет document.activeElement, который всегда указывает на ‹body› главного окна. - person Tony Lâmpada; 16.07.2013
comment
Он работает в обычном iframe; не уверен, что делает встроенное видео YouTube. Когда вы нажимаете на него, он может использовать javascript, чтобы установить фокус в другом месте. Он может даже использовать Flash. - person Ryan; 16.07.2013
comment
Позже я проверил с хромом. Действительно, я получаю различное поведение в Chrome x Chromium. В Chrome это работает, как вы говорите. В хроме это работает, как я сказал выше. - person Tony Lâmpada; 17.07.2013
comment
это был единственный способ предотвратить кражу фокуса iframe YouTube, когда player.nextVideo () назывался - person woojoo666; 06.04.2015
comment
@Райан Отлично! Он также работает с веб-сайтом с перекрестным происхождением. На мой взгляд, это нарушение CSP, потому что мы не сможем получить, если щелкнем в iframe. При этом мы можем угадать, когда происходит первый клик в iframe. - person AnonBird; 10.11.2016
comment
У меня были проблемы с Safari и document.activeElement. Вероятно, он работает должным образом только с элементами ввода, а не с iframe. - person DrLightman; 12.06.2020

Я знаю, что это старо, но у меня тоже была такая же проблема.

в итоге я использовал этот небольшой фрагмент кода:

$(document).on('focusout', function(){
       setTimeout(function(){
       // using the 'setTimout' to let the event pass the run loop
       if (document.activeElement instanceof HTMLIFrameElement) {
             // Do your logic here..
        }
    },0);
});
person EliorCohen    schedule 08.03.2015
comment
не работает для iframe YouTube. когда я вызывал player.nextVideo(), iframe украл фокус без вызова этого метода - person woojoo666; 06.04.2015
comment
Спасибо, это сработало для меня. Тайм-аут был важен, так как я пытался скрыть раскрывающийся элемент, когда пользователь щелкнул в любом месте, используя простое событие щелчка окна, которое на самом деле не срабатывает, когда вы меняете фокус на iframe (например, использует tinymce). Этот фокус и время ожидания гарантируют, что я также могу закрыть всплывающее окно, а также убедиться, что все, что я щелкнул внутри всплывающего окна, также запущено. - person Johncl; 10.10.2016
comment
Отличное решение. Спасибо! - person GivP; 10.02.2017
comment
Очень красивое решение! Вы должны отметить в своем ответе, что focusout будет запускаться для нескольких элементов DOM, а не только для фреймов (текстовое поле, входы и т. д.). - person Shlomi Hassid; 14.10.2017

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

person CAFxX    schedule 06.09.2011
comment
Возможно. Смотрите мой ответ. - person Ryan; 19.10.2012
comment
Данные ответы возможны с перекрестным происхождением, если вы реализуете HTML5 sendMessage для общения между двумя - person user3036342; 15.09.2014

Как проверить, когда был нажат iframe, а также состояние наведения.

Примечание. Я настоятельно рекомендую вам не выбирать метод опроса, а использовать метод, управляемый событиями, такой как этот.


Отказ от ответственности

Невозможно использовать события focus или blur непосредственно в iframe, но вы можете использовать их в window, чтобы обеспечить управляемый событиями метод проверки document.activeElement. Таким образом, вы можете достичь того, что вы после.

Хотя сейчас 2018 год, мой код внедряется в GTM и пытается быть кросс-браузерно совместимым с IE 11. Это означает, что существует более эффективный код, если вы используете новые функции ES/ECMAScript.


Настраивать

Я собираюсь сделать еще несколько шагов, чтобы показать, что мы также можем получить атрибут src iframe, а также определить, находится ли он в режиме зависания.

Код

В идеале вам нужно поместить это в событие готовности документа или, по крайней мере, инкапсулировать его, чтобы переменные не были глобальными [возможно, используйте IIFE]. Я не обернул его в готовый документ, потому что он обрабатывается GTM. Это также может зависеть от того, где вы размещаете это или как вы его загружаете, например, в нижнем колонтитуле.

https://jsfiddle.net/9285tbsm/9/

Я заметил в предварительном просмотре JSFiddle, что это уже iframe, иногда вам нужно сначала сфокусировать его, прежде чем события начнут захватывать. Другие проблемы могут заключаться в том, что окно вашего браузера еще не сфокусировано.

// Helpers

var iframeClickedLast;

function eventFromIframe(event) {
  var el = event.target;
  return el && el.tagName && el.tagName.toLowerCase() == 'iframe';
}

function getIframeSrc(event) {
  var el = event.target;
  return eventFromIframe(event) ? el.getAttribute('src') : '';
}

// Events

function windowBlurred(e) {
  var el = document.activeElement;
  if (el.tagName.toLowerCase() == 'iframe') {
    console.log('Blurred: iframe CLICKED ON', 'SRC:', el.getAttribute('src'), e);
    iframeClickedLast = true;
  } 
  else {
    console.log('Blurred', e);
  }
}

function windowFocussed(e) {
  if (iframeClickedLast) {
    var el = document.activeElement;
    iframeClickedLast = false;
    console.log('Focussed: iframe CLICKED OFF', 'SRC:', el.getAttribute('src'), e);
  } 
  else {
    console.log('Focussed', e);
  }
}

function iframeMouseOver(e) {
  console.log('Mouse Over', 'SRC:', getIframeSrc(e), e);
}

function iframeMouseOut(e) {
  console.log('Mouse Out', 'SRC:', getIframeSrc(e), e);
}

// Attach Events

window.addEventListener('focus', windowFocussed, true);  
window.addEventListener('blur', windowBlurred, true);

var iframes = document.getElementsByTagName("iframe");
for (var i = 0; i < iframes.length; i++) {
  iframes[i].addEventListener('mouseover', iframeMouseOver, true);
  iframes[i].addEventListener('mouseout', iframeMouseOut, true);
}
person CTS_AE    schedule 14.06.2018
comment
Спасибо. Только одно: обнаружение фокуса внутри функции windowBlurred работало для меня только в браузерах WebKit и Chromium, но не в Firefox, потому что Firefox, кажется, обновляет активный элемент только после запуска события размытия. Мое решение состоит в том, что прослушиватель размытия не выполняет функцию windowBlurred напрямую, а вместо этого использует window.setTimeout(windowBlurred, 0). - person immo; 11.01.2021
comment
Ваш код не работал в Firefox. Расследование показало, что события фокуса и размытия запускаются только во внешнем окне. mouseover и mouseout во внешнем окне работают, хотя само по себе это не говорит о том, был ли сфокусирован iframe. Я пытался использовать mousedown во внешнем окне, но это тоже срабатывает только для событий во внешнем окне. - person Allasso; 28.06.2021

Решение состоит в том, чтобы внедрить событие javascript на родительскую страницу следующим образом:

var script = document.createElement('script');
script.type = 'text/javascript';

script.innerHTML = 
"document.addEventListener('click', function()" + 
"{ if(document.getElementById('iframe')) {" +
    // What you want
"}});"; 

head.appendChild(script);
person Sindar    schedule 28.03.2011
comment
Как я писал выше в своем ответе на Uw, я не контролирую содержимое iframe (другое происхождение). Это, очевидно, означает, что я не могу ничего вводить ни от родителя к детям, ни от детей к родителю. - person CAFxX; 28.03.2011
comment
Извини, чувак, так что это будет сложнее, может тебе нужно обойти проблему междоменного доступа, чувак... Существует множество решений, но они не очень чистые... - person Sindar; 28.03.2011

Вот код для определения того, когда iframe получает или теряет фокус

 // This code can be used to verify Iframe gets focus/loses.

      function CheckFocus(){

       if (document.activeElement.id == $(':focus').context.activeElement.id) {
                // here do something
            }
     else{
      //do something
    }
}
person Sheo Dayal Singh    schedule 20.09.2017
comment
Что вы имеете в виду, что этот код срабатывает? Нет обработчиков событий, это просто если... Поскольку я не могу представить, как это должно работать, пожалуйста, предоставьте код или что-то подобное, которое показывает, что это работает. - person CAFxX; 21.09.2017
comment
этот код срабатывает означает: этот код можно использовать внутри любого события или функции для проверки того, что элемент сфокусирован или размыт. Это просто. Мы изменили код для лучшего понимания. - person Sheo Dayal Singh; 21.09.2017
comment
Конечно спасибо, но вопрос был не об этом. Вопрос спрашивает, как обнаружить, т.е. получить уведомление. Ваше решение не отправляет уведомление, оно требует опроса состояния. - person CAFxX; 21.09.2017

Это может сработать

document.addEventListener('click', function(event) {
  var frame= document.getElementById("yourFrameID");

  var isClickInsideFrame = frame.contains(event.target);

  if (!isClickInsideFrame ) {
    //exec code
  }

});
person Justine M.    schedule 19.10.2017
comment
к сожалению, он не работает с разными доменами (CORS). например: jsfiddle.net/liadmagen/bxhwv9v1/1 - person Liad Magen; 22.12.2017

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

/* eslint-disable no-unused-vars */
export default function watchIframeFocus(onFocus, onBlur) {
  let iframeClickedLast;

  function windowBlurred(e) {
    const el = document.activeElement;
    if (el.tagName.toLowerCase() == 'iframe') {
      iframeClickedLast = true;
      onFocus();
    } 
  }
  function windowFocussed(e) {
    if (iframeClickedLast) {
      iframeClickedLast = false;
      onBlur();
    } 
  }
  window.addEventListener('focus', windowFocussed, true);  
  window.addEventListener('blur', windowBlurred, true);
}
person Adam Jagosz    schedule 03.10.2019

Это решение работает для меня как на мобильных устройствах, так и на настольных компьютерах:

;(function pollForIframe() {
  var myIframe = document.querySelector('#my_iframe');
  if (!myIframe) return setTimeout(pollForIframe, 50);

  window.addEventListener('blur', function () {
    if (document.activeElement == myIframe) {
      console.log('myIframe clicked!');
    }
  });
})();
person Brian Norman    schedule 12.03.2020