2017-09-01 80 views
5

Chciałbym uruchomić trochę javascript w kontekście okna elementu iframe. Teraz jedynym sposobem mogę myśleć, aby zrobić to wstrzyknąć tag skryptu:Kod uruchamiania w kontekście okna ramki

myIframe = document.createElement('iframe'); 
myIframe.setAttribute('name', 'xyz123'); 
document.body.appendChild(myIframe); 

myIframe.contentWindow.document.write(` 
    <script> 
     console.log('The current window name is:', window.name); 
    </script> 
`); 

Uwaga: To jest iframe tej samej domeny, bez src, więc mam pełny dostęp do contentWindow.

Dla mojego przypadku użycia ważne jest, aby kod działał z poprawnymi globaliami; window, document itd. Wszystkie powinny być ograniczone do samego elementu iframe.

Czy mogę to zrobić w inny sposób? Powyższe działa, ale skrypt powinien działać na różnych domenach wszystko z różnymi zasadami CSP, co oznacza dodanie wsparcia dla nonces/skrótów itp

Czy można zrobić coś takiego:

myIframe.contentWindow.run(function() { 
    console.log('The current window name is:' window.name); 
}); 

I ve wypróbowany myIframe.contentWindow.setTimeout, ale nadal wydaje się uruchomić kod w kontekście okna nadrzędnego.

Odpowiedz

2

Może podzielić javascript na część z głównego okna (nazwijmy to: main.js) i iframe (nazwijmy to iframe.js). Następnie w elemencie iframe src wstaw link do iframe.js lub iframe.html, który ładuje plik js (nie jestem pewien, czy możesz dołączyć javascript prosto z atrybutu src).

Jeśli załadujesz js do iframe, użyj rozwiązania pod numerem Calling a function inside an iframe from outside the iframe.

+0

Dzięki za sugestię - ale dla mojego przypadku użycia, muszę być w stanie włożyć dowolne javascript do ramki, która niekoniecznie jest hostowana w dowolnym miejscu - stąd tag skryptu. – bluepnume

2

Można w rzeczywistości utworzyć tę funkcję run, a następnie zastosować funkcję zwrotną do this, która oczywiście będzie kontekstem iframe. Następnie można uzyskać dostęp do elementów iframe za pomocą this: wyjście

myIframe.contentWindow.run = function(fn) { 
    fn.apply(this); 
}; 

myIframe.contentWindow.run(function() { 
    console.log('(run) The current window name is:', this.window.name); 
}); 

Konsola

(run) The current window name is: xyz123 

Można sprawdzić mój przykład tutaj: http://zikro.gr/dbg/html/con-frame/

EDIT

Jeśli chcesz po prostu użyć window zamiast this.window, można utworzyć parametr do funkcji inline ze on wymienić window, a potem po prostu przejść this.window do tej funkcji takich jak to:

myIframe.contentWindow.run = function(fn) { 
    fn.call(this, this.window); 
}; 

myIframe.contentWindow.run(function(window) { 
    console.log('(run) The current window name is:', window.name); 
}); 

i nadal działa zgodnie z oczekiwaniami.

+0

To jest świetny przykład - tylko problem polega na tym, że zmienia semantykę wszystkich funkcji, które będą istnieć w elemencie iframe, aby odwoływać się do 'this.window', a nie tylko' window' - ale jest to zdecydowanie najlepsza odpowiedź, więc daleko. Dzięki! – bluepnume

+0

@bluepnume nie ma za co. Zaktualizowałem swoją odpowiedź, aby mieć wbudowaną funkcję, która ma parametr o nazwie 'okno', którego możesz użyć; eliminuje potrzebę używania 'this.window'. To zadziała, chyba że będziesz potrzebował jawnie zastosować całą funkcję inline z określonego źródła, co nie będzie możliwe, aby ten konkretny parametr był możliwy. –

1
window.name='main window'; // just for debugging 
var myIframe = document.createElement('iframe'), frameScript = document.createElement('script'); 
document.body.appendChild(myIframe); 
frameScript.textContent = "window.top.name='topWindow';window.name = 'xyz123';function WhereAmI(){return(window.name);} window.parent.postMessage('frame_initialized', '*'); " 
myIframe.contentWindow.document.documentElement.appendChild(frameScript); 
function OnMessage(event) { 
    if(typeof event.data == 'string') switch(event.data) { 
    case 'frame_initialized': 
    myIframe.contentWindow.document.body.appendChild(document.createTextNode(myIframe.contentWindow.WhereAmI())); 
    console.log('Now running in', myIframe.contentWindow.WhereAmI()); 
    break; 
    } 
} 
window.addEventListener('message', OnMessage, false); 

Testowane z Firefoksem i Chromium.

Zamiast .textContent można zastosować .src do frameScript, aby skrypt mógł być ładowany asynchronicznie. Następnie możesz zadzwonić do postMessage, jak pokazano powyżej, lub zadzwonić do funkcji zwrotnej, aby powiadomić okno główne.

Należy pamiętać, że w oryginalnym kodzie został zainicjowany window.frameElement.name.Twój skrypt prosi wtedy o window.name. FireFox automatycznie kopiuje wartość do okna, powodując pewne zamieszanie.

+0

Aby poprawić tę odpowiedź, muszę lepiej poznać Twój scenariusz. Czy masz dostęp do kodu HTML w obu domenach? Czy powinien działać tylko dla ciebie lokalnie lub dla wszystkich? Czy możliwe jest użycie menedżera skryptów, takiego jak GreaseMonkey? – gnblizz

+0

Dzięki za sugestię, ale nie jestem pewien, czy window.postMessage szczególnie pomoże mi uruchomić dowolny kod w ramce - Potrzebowałbym jakiegoś sposobu dodania słuchacza wiadomości w ramce, aby to działało, co przynosi mi z powrotem do pierwotnego problemu. – bluepnume

+0

Aby odpowiedzieć na twoje pytanie, musi to zadziałać dla wszystkich, aby nie zadziałał klucz do aplikacji. Nie ma drugiej domeny - element iframe jest ładowany bez 'src' – bluepnume

1

Musisz załadować skrypt asynchronicznie (tj. $.getScript()), a następnie wywołać go na .contentWindow. Nie testowałem, ale to powinno działać.

(function() { 

    var myIframe = document.createElement('iframe#iframe'): 

    var jsSnippet; 
    var iframeScript = function(url) { 
     script = document.createElement('script'), 
     scripts = document.getElementsByTagName('script')[0]; 
     script.src = url; 
     return jsSnippet = scripts.parentNode.insertBefore(script, scripts); 
    }; 

    myIframe.setAttribute('name', 'xyz123'): 
    myIframe.appendChild(jsSnippet); 
    document.body.appendChild(myIframe); 
    return document.getElementById('#iframe').contentWindow.iframeScript(); 
}) 

Oba te zasoby będą pomocne, jeśli to rozwiązanie nie działa: https://plainjs.com/javascript/ajax/load-a-script-file-asynchronously-49/

Invoking JavaScript code in an iframe from the parent page

Powiązane problemy