Как предотвратить действия iFrame от вызова внешней страницы для ее прокрутки

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

enter image description here

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

Проблема: У меня есть внутренний iFrame, содержащий сторонние объявления, которые я не могу контролировать. Если внутренний iFrame запускает любую из следующих команд Javascript, внешняя страница прокручивает все вниз, чтобы отобразить внутренний iFrame:

  • размытие и фокусировка на элементе внутри него
  • window.location.href или document.location.hash на якорь внутри него.

  • scrollIntoView для элемента внутри него

Вот демо-коллега, собранный (подождите 5 секунд, чтобы он начал)

Решения, которые не работают:

  • Если бы я контролировал внешнюю страницу, я мог бы прослушивать событие onScroll и предотвращать прокрутку страницы, если это не было из события мыши или события вверх/вниз. Однако из-за соображений безопасности мой внешний iFrame не может получить доступ к главной странице или предотвратить прокрутку главной страницы вверх или вниз.
  • Я мог бы перезаписать все эти Javascript-методы, упомянутые выше, чтобы они не работали, однако объявления в моем внутреннем iFrame фактически создают свои собственные внутренние iFrames, которые имеют эту функциональность и вызывают одну и ту же проблему.
  • События не распространяются, и я вижу, что мой iFrame может не дойти до главной страницы. Браузер, кажется, прослушивает непосредственно самую внутреннюю iFrame.

Ответ 1

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

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

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

Ответ 2

Если вы можете отказаться от поддержки IE10 и ниже, а iframes - это тот же домен, вы можете использовать комбинацию из вашего решения номер 2 (переписать методы) и события mutation. Вам нужно сначала перебрать все iframes уже в DOM и удалить ненужные функции, а также, возможно, также очистить нежелательные интервалы. И на каждом document вы добавляете mutation observer, который будет запускать те же скрипты на added nodes, если они iframes. Таким образом, вы должны иметь рекурсивную функцию, проходящую через все фреймы в начале, и функцию для удаления методов, очистки интервалов и добавления наблюдателей на каждом iFrame. И как только добавляется iFrame, вы запускаете то же самое на нем. Что-то вроде этого:

Рекурсивная функция:

var to_cancel = ["scrollIntoView","blur","focus"];
window.onload = function(){iterate_window_object(window)};
function iterate_window_object(cur_win){
    remove_methods(cur_win);
    for(var i=0;i<cur_win.frames.length;i++){
        remove_methods(cur_win.frames[i])
         if(cur_win.frames[i].length>0){
              iterate_window_object(cur_win.frames[i])
           }

      }
   }

Функция очистки:

function remove_methods(cur_win){ 
      for(var j=0;j<to_cancel.length;j++){
           cur_win.Element.prototype[to_cancel[j]] = {}
       }

       for (var k = 0; k < 999;k++){
            cur_win.clearInterval(k);
       }
       cur_win.setInterval = {};
       cur_win.observer = new MutationObserver(function(mutations) {
             mutations.forEach(function(mutation) {
                  for(var k=0;k<mutation.addedNodes.length;k++){
                       var added_node = mutation.addedNodes[k]
                       if(added_node.tagName == "IFRAME"){
                            remove_methods(added_node.contentWindow);
                            added_node.contentWindow.onload = function(){iterate_window_object(added_node.contentWindow)};
                        }
                   };
                });    
            });
            cur_win.config = { childList: true };
              cur_win.observer.observe(cur_win.document.getElementsByTagName('body')[0], cur_win.config);

        }

Вероятно, способ сделать это лучше, самая сложная часть - это возможность модифицировать методы до их вызова. И здесь он немного переборчив, потому что он выполняет итерацию через новый iframes, так что если iframe добавляется с уже добавленным iframes, они также берутся. Это, вероятно, не нужно. Таким образом, это может быть решение, но mutation события не поддерживаются IE10 и ниже.