iFrame src thay đổi phát hiện sự kiện?


181

Giả sử tôi không có quyền kiểm soát nội dung trong iframe, có cách nào để tôi có thể phát hiện thay đổi src trong đó thông qua trang mẹ không? Một số loại tải có thể?

Phương pháp cuối cùng của tôi là thực hiện kiểm tra khoảng thời gian 1 giây nếu src iframe giống như trước đây, nhưng thực hiện giải pháp hacky này sẽ rất tệ.

Tôi đang sử dụng thư viện jQuery nếu nó giúp.


3
Liệu srcsự thay đổi sở hữu khi một liên kết trong iframe được nhấp? Tôi không chắc về điều đó - nếu tôi phải đoán, nếu sẽ nói "không". Có nhiều cách để giám sát các thuộc tính (trong Firefox ít nhất là AFAIK) nhưng tôi không chắc liệu nó có được sử dụng trong trường hợp này hay không.
Pekka

Tôi đang cố gắng thực hiện một cái gì đó như thế này và có thể xác nhận rằng srcthuộc tính trong DOM không thay đổi trong các phiên bản mới nhất của FireFox hoặc Safari. Câu trả lời của @ Michiel dưới đây là tốt như tôi đã có thể nhận được cho đến nay.
Kaelin Colclasure

Câu trả lời:


201

Bạn có thể muốn sử dụng onLoadsự kiện này, như trong ví dụ sau:

<iframe src="http://www.google.com/" onLoad="alert('Test');"></iframe>

Cảnh báo sẽ bật lên bất cứ khi nào vị trí trong iframe thay đổi. Nó hoạt động trong tất cả các trình duyệt hiện đại, nhưng có thể không hoạt động trong một số trình duyệt cũ hơn như IE5 và Opera sớm. ( Nguồn )

Nếu iframe hiển thị một trang trong cùng một miền của cha mẹ , bạn sẽ có thể truy cập vị trí với contentWindow.location, như trong ví dụ sau:

<iframe src="/test.html" onLoad="alert(this.contentWindow.location);"></iframe>

13
Ý bạn là this.contentWindow.location.href
Nikolai

@Daniel Vassallo giả sử bạn lấy aproach này khi bạn muốn xử lý vấn đề bảo mật, không ai có thể ghi đè sự kiện onLoad và sau đó thay đổi nội dung iframe?
Noam Shaish

15
Khi tôi this.contentWindow.location.hrefnhận đượcPermission denied to access property 'href'
Mazatec

4
this.contentWindow.location.href sẽ không hoạt động vì bạn đang thực hiện một yêu cầu nguồn gốc chéo. :( Tất cả các trình duyệt hạn chế điều đó ngay bây giờ.
Naveen

2
Sửa chữa: this.contentWindow.locationcó sẵn cho các tên miền khác. Thậm chí this.contentWindow.location.hrefcó sẵn cho các tên miền khác, nhưng chỉ để viết. Tuy nhiên việc đọc this.contentWindow.location.hrefbị giới hạn trong cùng một tên miền.
wadim

57

Trả lời dựa trên JQuery <3

$('#iframeid').load(function(){
    alert('frame has (re)loaded');
});

Như được đề cập bởi subharb, như từ JQuery 3.0, điều này cần được thay đổi thành:

$('#iframe').on('load', function() {
    alert('frame has (re)loaded ');
});

https://jquery.com/upTHER-guide/3.0/#breaking-change-load-unload-and-error-remond


2
Không hoạt động chính xác như kế hoạch. Nó kích hoạt cảnh báo về tải trang ban đầu. Tùy thuộc vào kế hoạch của bạn dành cho mã này (đối với tôi, tôi đang kích hoạt hộp thoại tắt trang trên biểu mẫu gửi) giải pháp này sẽ không hoạt động.
stacigh 30/03/2015

@stracigh, đó là chính xác, sự kiện sẽ kích hoạt mỗi lần tải khung hình, do đó cũng là lần đầu tiên trên tải trang ban đầu. Nếu bạn không muốn mã của mình được kích hoạt lần đầu tiên, bạn phải phát hiện ra đây là lần tải và trả lại khung đầu tiên của bạn.
Michiel

@FooBar, Có, nó hoạt động trên nhiều miền, trên thực tế đó là những gì tôi đã sử dụng nó cho. Nhưng bạn không thể truy cập trang trong iFrame bằng javascript khi trang đó nằm trên một tên miền khác.
Michiel

Điều này hoạt động và phát hiện khi nội dung được tải, tôi cần một cách để phát hiện khi src thay đổi. Một cái gì đó giống như trước khi tải (để kích hoạt trình tải)
Andrea_86

Như được chỉ định bởi @stacigh, nó sẽ kích hoạt sự kiện một lần khi tải trang mẹ. Nếu bạn khai báo một bộ đếm bên ngoài chức năng đó và đọc nó bên trong, thì bạn có thể phát hiện các thay đổi sau đó. Bạn sẽ tăng bộ đếm bên trong bộ xử lý onload plus if (counter > 1) { // do something on iframe actual change }(giả sử bộ đếm được đặt thành 0 khi bắt đầu)
Alex

38

Nếu bạn không có quyền kiểm soát trang và muốn xem một số thay đổi thì phương pháp hiện đại là sử dụng MutationObserver

Một ví dụ về việc sử dụng nó, xem srcthuộc tính để thay đổi mộtiframe

new MutationObserver(function(mutations) {
  mutations.some(function(mutation) {
    if (mutation.type === 'attributes' && mutation.attributeName === 'src') {
      console.log(mutation);
      console.log('Old src: ', mutation.oldValue);
      console.log('New src: ', mutation.target.src);
      return true;
    }

    return false;
  });
}).observe(document.body, {
  attributes: true,
  attributeFilter: ['src'],
  attributeOldValue: true,
  characterData: false,
  characterDataOldValue: false,
  childList: false,
  subtree: true
});

setTimeout(function() {
  document.getElementsByTagName('iframe')[0].src = 'http://jsfiddle.net/';
}, 3000);
<iframe src="http://www.google.com"></iframe>

Đầu ra sau 3 giây

MutationRecord {oldValue: "http://www.google.com", attributeNamespace: null, attributeName: "src", nextSibling: null, previousSibling: null…}
Old src:  http://www.google.com
New src:  http://jsfiddle.net/ 

Trên jsFiddle

Câu trả lời được đăng ở đây vì câu hỏi ban đầu đã được đóng lại như một bản sao của câu hỏi này.


Điều gì xảy ra nếu tôi đang sử dụng Thành phần web và một trong các thành phần tùy chỉnh của tôi có thuộc tính src? Tôi nghĩ rằng điều này sẽ vô tình nhặt nó lên.
Lu-ca

Sau đó, bạn thay đổi các tùy chọn trên observe. Đây chỉ là một ví dụ.
Xotic750

@PaulRoub đã xóa liên kết jsfiddle (phải là lỗi sao chép / dán) và bao gồm mã dưới dạng đoạn trích.
Xotic750

4
Nhưng trong trường hợp của tôi, frame.contentDocument.URL thay đổi (từ các liên kết bên trong khung) chứ không phải src. Tôi có thể xem nó không?
httpete

Không chắc chắn, tốt nhất để viết một câu hỏi tôi nghĩ.
Xotic750

11

Lưu ý: Đoạn mã sẽ chỉ hoạt động nếu iframe có cùng nguồn gốc.

Các câu trả lời khác đã đề xuất loadsự kiện này, nhưng nó sẽ kích hoạt sau khi trang mới trong iframe được tải. Bạn có thể cần được thông báo ngay sau khi URL thay đổi , không phải sau khi trang mới được tải.

Đây là một giải pháp JavaScript đơn giản:

function iframeURLChange(iframe, callback) {
    var unloadHandler = function () {
        // Timeout needed because the URL changes immediately after
        // the `unload` event is dispatched.
        setTimeout(function () {
            callback(iframe.contentWindow.location.href);
        }, 0);
    };

    function attachUnload() {
        // Remove the unloadHandler in case it was already attached.
        // Otherwise, the change will be dispatched twice.
        iframe.contentWindow.removeEventListener("unload", unloadHandler);
        iframe.contentWindow.addEventListener("unload", unloadHandler);
    }

    iframe.addEventListener("load", attachUnload);
    attachUnload();
}

iframeURLChange(document.getElementById("mainframe"), function (newURL) {
    console.log("URL changed:", newURL);
});
<iframe id="mainframe" src=""></iframe>

Điều này sẽ theo dõi thành công các srcthay đổi thuộc tính, cũng như mọi thay đổi URL được thực hiện từ bên trong iframe.

Đã thử nghiệm trong tất cả các trình duyệt hiện đại.

Tôi đã thực hiện một ý chính với mã này là tốt. Bạn có thể kiểm tra câu trả lời khác của tôi quá. Nó đi sâu một chút vào cách thức hoạt động của nó.


6

Khung nội tuyến luôn giữ trang mẹ, bạn nên sử dụng trang này để phát hiện bạn đang ở trang nào trong iframe:

Mã HTML:

<iframe id="iframe" frameborder="0" scrolling="no" onload="resizeIframe(this)" width="100%" src="www.google.com"></iframe>

Js:

    function resizeIframe(obj) {
        alert(obj.contentWindow.location.pathname);
    }

2

Kể từ phiên bản 3.0 của Jquery, bạn có thể gặp lỗi

LoạiError: url.indexOf không phải là một chức năng

Mà có thể dễ dàng sửa chữa bằng cách làm

$('#iframe').on('load', function() {
    alert('frame has (re)loaded ');
});

1

Đây là phương thức được sử dụng trong Commerce SagePay và trong các mô-đun Drupal Commerce Paypoint về cơ bản so sánh document.location.hrefvới giá trị cũ bằng cách tải iframe của chính nó, sau đó là bên ngoài.

Vì vậy, về cơ bản, ý tưởng là tải trang trống dưới dạng giữ chỗ với mã JS riêng và biểu mẫu ẩn. Sau đó, mã JS mẹ sẽ gửi biểu mẫu ẩn đó trong đó #actionđiểm của nó tới iframe bên ngoài. Khi chuyển hướng / gửi xảy ra, mã JS vẫn đang chạy trên trang đó có thể theo dõi các document.location.hrefthay đổi giá trị của bạn .

Dưới đây là ví dụ JS được sử dụng trong iframe:

;(function($) {
  Drupal.behaviors.commercePayPointIFrame = {
    attach: function (context, settings) {
      if (top.location != location) {
        $('html').hide();
        top.location.href = document.location.href;
      }
    }
  }
})(jQuery);

Và đây là JS được sử dụng trong trang mẹ:

;(function($) {
  /**
   * Automatically submit the hidden form that points to the iframe.
   */
  Drupal.behaviors.commercePayPoint = {
    attach: function (context, settings) {
      $('div.payment-redirect-form form', context).submit();
      $('div.payment-redirect-form #edit-submit', context).hide();
      $('div.payment-redirect-form .checkout-help', context).hide();
    }
  }
})(jQuery);

Sau đó, trong trang đích trống tạm thời, bạn cần bao gồm biểu mẫu sẽ chuyển hướng đến trang bên ngoài.


Đây có thể là một hack nhưng chi phí thấp hơn đáng kể so với người quan sát đột biến. Khi được sử dụng trong một ứng dụng trang duy nhất, hãy chú ý không tạo bất kỳ tài liệu tham khảo nào sẽ ngăn chặn phần còn lại từ bản hack này khỏi rác được thu thập.
Jeff
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.