Почему Greasemonkey не обнаруживает изменения некоторых страниц в Facebook?

Я пытался сделать страницу user.js на /messages в facebook, но похоже, что Greymonkey не замечает, когда навигация меняется с / на /messages. Это также происходит на других внутренних страницах. Сначала я подумал, что это было вызвано навигацией AJAX, но URL-адрес меняется (не хеш-часть), так что это нормальная навигация, верно?

Это тестовая страница, которую я использовал:

// ==UserScript==
// @name           Test
// @namespace      none
// @description    just an alert when page changes
// @include        http*://www.facebook.com/*
// ==/UserScript==

alert(location.href);

Как я могу правильно определить изменения страницы?


Версия Firefox: 6.0.2

Версия Greasemonkey: 0.9.11


person Ravan Scafi    schedule 11.09.2011    source источник
comment
Этот другой вопрос может быть актуальным. stackoverflow.com/questions/3522090/   -  person bronsoja    schedule 12.09.2011


Ответы (3)


Для браузеров, которые его поддерживают, включая Firefox 4+, Facebook использует HTML5 History API. Этот API позволяет изменить местоположение с помощью метода history.pushState(), хотя на самом деле навигации не происходит. Хотя может показаться, что страница изменилась, все, что произошло, — это закулисный вызов ajax, который изменил большую часть содержимого.

Если вы хотите зафиксировать это изменение, вам придется проксировать метод pushState() с помощью собственной функции:

(function (old) {
    window.history.pushState = function () {
        old.apply(window.history, arguments);
        alert(window.location.href);
    }
})(window.history.pushState); 

Подробнее об API History читайте на странице https://developer.mozilla.org/en/DOM/Manipulating_the_browser_history< /а>.

person Andy E    schedule 11.09.2011
comment
Я не думаю, что вам нужно использовать прокси — не могли бы вы просто обработать событие window.onpopstate? - person Coderer; 08.01.2014
comment
onpopstate не определяет, когда pushState вызывается для новых страниц, только когда вы используете кнопки «вперед» или «назад» (или вызовы API «вперед» и «назад»). - person rampion; 08.01.2014
comment
проблема с этим подходом заключается в том, что новые элементы страницы не обязательно загружаются после возврата old.apply(window.history, arguments), поэтому он может быть не готов к обработке вашим сценарием. - person rampion; 08.01.2014

Другой подход состоит в том, чтобы перехватить DOMNodeInserted для страницы и запустить, когда путь соответствует /messages после вставки:

// ==UserScript==
// ...
// @include https://www.facebook.com/*
// ...
// ==/UserScript==

var url = document.location.toString();
function scriptBody(){
   if (!url.match(/facebook.com\/messages/)) return;
   // ...
   // do stuff
   // ...
});

scriptBody(); // run on initial page load

document.querySelector('html').addEventListener('DOMNodeInserted', function(ev){
  var new_url = document.location.toString();
  if (url == new_url) return; // already checked or processed
  url = new_url;

  scriptBody(); // run when URL changes
});

Обратите внимание, что если вы, пользователи, используете кнопки «вперед/назад», вы можете получить события «DOMNodeInserted» для контента, который повторно вставляется на страницу, которую вы уже изменили с помощью своего скрипта, поэтому вам нужно убедиться, что вы проверяете, были ли какие-либо изменения которые вы обычно делаете на странице, уже были созданы, чтобы предотвратить вставку повторяющихся элементов управления или что-то еще.

person rampion    schedule 08.01.2014

+1 к предложениям @rampion. Я хотел выполнить простое перенаправление, и поиск элементов на странице был не очень полезен, так как я не хочу перенаправлять пользователя без присмотра.

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

if (document.addEventListener ){
  document.addEventListener("click", function(event) {
    var targetElement = event.target || event.srcElement;
    // TODO: support deeper search for parent element with a href attribute
    var href = targetElement.getAttribute('href') || targetElement.parentElement.getAttribute('href') ;
    if (href && videoURLRe.test(href)) {
      var target = "";
      if (href.indexOf("/") == 0) {
        target = "https://m.facebook.com" + href
      } else {
        target = href.replace("www.facebook", "m.facebook");
      }
      window.location.assign(target);
    }   
  }, true);
}

Работает довольно аккуратно. Мне пришлось вычислить правильный третий параметр addEventListener, чтобы мой слушатель выполнялся раньше всех остальных.

Полный сценарий см. на https://greasyfork.org/en/scripts/8176-switch-to-mobile-version-on-facebook-video-page

Я не смог найти способ надежно обнаружить изменения URL-адреса, независимо от метода, используемого веб-сайтом для изменения этого URL-адреса. Подход @andy-e кажется потрясающим, но по какой-то причине у меня не сработал. Возможно, я не смог правильно сделать тег script @grant.

person akostadinov    schedule 21.04.2015