Can I intercept the loading of an iframe and modify the contentWindow? - javascript

I am trying to provide a property to an iframe via its contentWindow and would like to do it in a synchronous manner.
I know I can access and set properties on the contentWindow of the iframe but the modification will occur after the iframe has loaded.
Parent
<body>
<script>
const frame = document.createElement('iframe')
document.body.appendChild(frame)
frame.src = 'frame.html'
frame.contentWindow.foobar = 'foobar'
</script>
</body>
iframe
<body>
<script>
console.log(window.foobar) // undefined
</script>
</body>
Is it possible to achieve or do I have to do this asynchronously by dispatching an event from the parent notifying the iframe that the property has been set?

I was able to find a solution to this. My goal was to create a "mock console" where any calls to console.log in the iframe document would be intercepted by the parent window. The problem was similar to OP's in that even though I overrode frame.contentWindow.console, it seemed to get re-set when the window loaded.
I'm not sure how exactly I solved it (I tried copying the replit previewer, luckily they had sourcemaps enabled), but nonetheless here is a plunkr showing a working example:
https://plnkr.co/edit/OLQ0wm8sZVUlMT0p?open=lib%2Fscript.js
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<p>Preview frame</p>
<div id="userCodePreviewWrapper"></div>
<p>Mock Console</p>
<div id="mockConsole"></div>
<script>
let contentWindow;
const consoleDiv = document.querySelector('#mockConsole');
function createConsole(frame) {
// Override the console object in the frame
contentWindow = frame.contentWindow;
frame.contentWindow.__nativeConsole = frame.contentWindow.console;
frame.contentWindow.console = {
log: function(msg) {
console.log("Logging console message", msg);
consoleDiv.innerHTML += `<p>${msg}</p>`;
}
}
}
document.addEventListener('DOMContentLoaded', () => {
const previewFrameDiv = document.querySelector('#userCodePreviewWrapper');
const previewFrame = document.createElement('iframe');
previewFrame.src = 'user-code.html';
previewFrame.setAttribute('id', 'userCodePreview');
previewFrame.setAttribute(
'sandbox',
'allow-forms allow-pointer-lock allow-popups ' +
'allow-same-origin allow-scripts allow-modals',
);
previewFrame.setAttribute('frameborder', 0);
previewFrameDiv.appendChild(previewFrame);
createConsole(previewFrame);
});
</script>
</body>
</html>

Related

JS: Display certain div if browser is IE

I have this react project that doesn't work in Internet Explorer, and we don't intend it to work in IE.
So when rendering the index.html there is the usual root div that renders react.
I want not to render that root div when browser is IE but a different div, with a message warning that the app doesn't work in IE
I can know if the browser is IE like so:
const isIE = !!window.MSInputMethodContext && !!document.documentMode
So I'm trying to change the html div output depending of isIE and I'm not quite sure how.
Logic:
if isIE - true render <div id="root"></div>
if !isIE - false render <div>Browser not supported</div>
Tried something like this:
<html>
<head>
<meta charset="utf-8" />
<% _.map(css, (item) => { %><link href="<%= item %>" rel="stylesheet"><% }) %>
</head>
<body>
<script>
const isIE = !!window.MSInputMethodContext && !!document.documentMode;
if (isIE) { document.getElementById('root').innerHTML += '<p>IE!!</p>' } // or something similar
</script>
<div id="root"></div>
<script>window.__ENVIRONMENT__ = Object.freeze(<%= JSON.stringify(environment) %>)
</script>
<% _.map(js, (item) => { %><script src="<%= item %>"></script><% }) %>
</body>
</html>
Also tried a logic returning the html as a string like so:
<script>
if (isIE) { return '<div>Browser not supported</div>'; }
else { return '<div id="root"></div> }
</script>
And this
const IEdiv = '<div>This is IE</div>';
const rootDiv = '<div id="root"></div>';
if (isIE) { IEdiv.append('body'); }
else { rootDiv.append('body'); }
None of this works
React shouldn't work out of the box with any version of IE as far as I can tell. It requires (or at least used to require) react-app-polyfill to get anywhere.
You're also going to have problems with const as it's only supported by IE11. If the only version of IE you're concerned with is IE11 you should be fine for this.
UPDATED ANSWER
I created a sample myself to see what I could do. I first experimented with loading the <div> into the page with JS, and hit a nasty IE error in being unable to add elements to the document.
So I changed my approach back to yours - editing the existing <div>. The problem isn't your code, the problem is that your script is being ran before the <div> is present in the DOM (to be accessed). Put the <div> above the script (or move the JS to an external file and load it in) and it should work as you intend it to.
Here's my sample for reference:
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<div id="root"></div>
<script>
var isIE = !!window.MSInputMethodContext && !!document.documentMode;
console.warn(isIE);
if (isIE) {
var root = document.getElementById('root');
root.innerHTML += 'IE!!';
}
else {
var root = document.getElementById('root');
root.innerHTML += 'hello hello hello';
// you may also need to defer React being loaded in
}
</script>
</body>
</html>

hCaptcha JavaScript API render: invalid container 'null'

I'm getting this error <[hCaptcha] render: invalid container 'null'.> on opera developer console.
I replaced my site key and it returned invalid container null.
Here is my source code.
<html>
<head>
<script src="https://hcaptcha.com/1/api.js?onload=yourFunction&render=explicit" async defer></script>
<script type="text/javascript">
var yourFunction = function () {
console.log('hCaptcha is ready.');
var widgetID = hcaptcha.render('captcha-1', { sitekey: 'MY_SITE_KEY' });
};
</script>
</head>
<body>
<div class="h-captcha" data-sitekey="MY_SITE_KEY" data-theme="dark"></div>
</body>
</html>
When render is called implicitly, the default containerId is h-captcha, as defined in the class of the target div.
When you call hcaptcha.render() explicitly, the first parameter is the containerId.
Due to this behavior, your problem can be resolved by either changing your first parameter of your render() call to h-captcha rather than captcha-1, or changing the class value to captcha-1.

Javascript Mediator In Browser Can't Find Instance Variables

Am working on a HTML/JS Mediator that filters a data_model when a user enters text to a field. Have used window.onload = init, and spent four hours trying to find why 'this' in the browser makes it print the calling object, and thus I can't get a class instance to refer to itself using 'this'
console.log(this.text_field)
in setup_list_view() works fine, seemingly because it's within the constructors scope. Running it outside the constructor, as per below, gives:
Cannot set property 'innerHTML' of undefined at HTMLInputElement.handle_text_field_changed
...
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
function init() {
var text_field = document.getElementById("text-field");
var list_view = document.getElementById("list-view")
form_mediator = new FormMediator(text_field, list_view)
}
class FormMediator {
constructor(text_field, list_view) {
this.setup_text_field(text_field)
this.setup_list_view(list_view)
}
setup_text_field(text_field) {
this.text_field = text_field;
this.text_field.onchange = this.handle_text_field_changed
}
setup_list_view(list_view) {
this.data_model = ['England', 'Estonia', 'France', 'Germany']
this.list_view = list_view
this.list_view.innerHTML = this.data_model
}
does_string_start_with_text_field_text(text) {
return false;
return text.startsWith('E')
}
handle_text_field_changed(){
this.list_view.innerHTML = 'new content' //this.data_model.filter(this.does_string_start_with_text_field_text)
}
}
window.onload = init
</script>
<input id="text-field"><button>Search</button>
<span id="list-view"></span>
</body>
</html>
Any help much appreciated.
The problem in your code occurs in this line:
this.text_field.onchange = this.handle_text_field_changed
A method, by default, won't carry its original binding context if assigned to a variable or another object's property. You need to bind this handle_text_field_changed method first, this way:
this.text_field.onchange = this.handle_text_field_changed.bind(this)

Defining Polymer element after importing ES6 code via System.js

I'm creating an HTML element using Polymer, and I want it to be able to work with an ES6 class I've written. Therefore, I need to import the class first and then register the element, which is what I do:
(function() {
System.import('/js/FoobarModel.js').then(function(m) {
window.FoobarModel = m.default;
window.FoobarItem = Polymer({
is: 'foobar-item',
properties: {
model: Object // instanceof FoobarModel === true
},
// ... methods using model and FoobarModel
});
});
})();
And it works well. But now I want to write a test HTML page to display my component with some dummy data:
<!DOCTYPE html>
<html lang="en">
<head>
<script src="/bower_components/webcomponentsjs/webcomponents.js"></script>
<script src="/bower_components/system.js/dist/system.js"></script>
<script>
System.config({
map:{
traceur: '/bower_components/traceur/traceur.min.js'
}
});
</script>
<link rel="import" href="/html/foobar-item.html">
</head>
<body>
<script>
(function() {
var data = window.data = [
{
city: {
name: 'Foobar City'
},
date: new Date('2012-02-25')
}
];
var view;
for (var i = 0; i < data.length; i++) {
view = new FoobarItem();
view.model = data[i];
document.body.appendChild(view);
}
})();
</script>
</body>
</html>
Which isn't working for one simple reason: the code in the <script> tag is executed before Polymer registers the element.
Thus I'd like to know if there's a way to load the ES6 module synchronously using System.js or even better, if it's possible to listen to a JavaScript event for the element registration (something like PolymerElementsRegistered)?
I've tried the following without success:
window.addEventListener('HTMLImportsLoaded', ...)
window.addEventListener('WebComponentsReady', ...)
HTMLImports.whenReady(...)
In the app/scripts/app.js script from the polymer starter kit, they use auto-binding template and dom-change event
// Grab a reference to our auto-binding template
var app = document.querySelector('#app');
// Listen for template bound event to know when bindings
// have resolved and content has been stamped to the page
app.addEventListener('dom-change', function() {
console.log('Our app is ready to rock!');
});
Also check this thread gives alternatives to the polymer-ready event.

How to add google remarketing code for meteor.js?

The seo company I work closely with told me I needed to add this code inbetween the body tags of my meteor project.
<script type="text/javascript">
/* <![CDATA[ */
var google_conversion_id = 123456789;
var google_custom_params = window.google_tag_params;
var google_remarketing_only = true;
/* ]]> */
</script>
<script type="text/javascript" src="//www.googleadservices.com/pagead/conversion.js">
</script>
<noscript>
<div style="display:inline;">
<img height="1" width="1" style="border-style:none;" alt=""src="//googleads.g.doubleclick.net/pagead/viewthroughconversion/949352235 /?value=0&guid=ON&script=0"/>
</div>
</noscript>
However as we know script tags don't get executed properly inside body tags.
So I searched on google to find an answer and I found this code that supposedly works but it's in REACT. How can I convert this react code into normal javascript that I can refer from a template or something. I'm thinking in a onCreated and or onRendered function.
GoogleTag = React.createClass({
displayName : 'GoogleTag',
render(){
var src = '';
if(this.props.type === 'conversion'){
src = _.template('//www.googleadservices.com/pagead/conversion/<%=id%>/?label=<%=label%>&guid=ON&script=0'),
src = src({id : this.props.id, label : this.props.label})
}
if (this.props.type === 'remarketing') {
src = _.template('//googleads.g.doubleclick.net/pagead/viewthroughconversion/<%=id%>/?value=0&guid=ON&script=0'),
src = src({id: this.props.id})
}
var style = {
display : "inline"
},
imgStyle = {
borderStyle : "none"
}
return (
<div style={style}>
<img height="1" width="1" style={imgStyle} alt="" src={src}/>
</div>
)
}
})
You can use Google Remarketing library for async actions and write your meta-data via direct call tracking function:
/* remarketingTrack utility */
export default function remarketingTrack(data = {}) {
try {
trackConversion({
google_conversion_id: SOME_ID,
google_custom_params: data,
google_remarketing_only: true
});
} catch (e) {
// error
}
}
/** somewhere in ReactJS component **/
componentDidMount() {
remarketingTrack({
flight_originid: XXX,
flight_destid: XXX,
flight_startdate: date,
flight_pagetype: 'home',
});
}
I think that it's more flexible and neat solution
Place
<script type="text/javascript">
var google_conversion_id = 123456789;
var google_custom_params = window.google_tag_params;
var google_remarketing_only = true;
</script>
<script type="text/javascript" src="//www.googleadservices.com/pagead/conversion.js"></script>
in your head and you will be fine.
In Meteor you can only have one <head> defined in your project, just create a file called head.html on the client and place the code above inside a body tag in the file.
First, remove any code that you have regarding this. A standard implementation won't work with Meteor without a custom written implementation.
Second, take the code block that Google gives you. Throw out all of the JavaScript, and all of the script tags. You don't need them.
We'll be running a 'non-standard implementation', but it work's the same as far as we're concerned. Take the code within the noscript tags (the div with the img tag), and we need to place it in every place our pages render. So, place it in your Blaze or React layout template, and you should be done.
Google will detect the noscript implementation (which they create so it'll work for visitors who don't have JavaScript enabled, but we are hacking it for use with our implementation), all Google really needs to see is the url call from the img tag :)

Categories

Resources