So i have a small react component which renders an iframe. I need to determine when all the content (html etc) has fully loaded.
The iframe is loading an orbeon form which can takes a few seconds to fully load.
I have tried hooking into the 'load' event form the iframe which works but triggers the even the second the iframe is loaded and not when the iframes content has loaded.
I have read some posts about listening to the 'DOMContentLoaded' even but cannot seem to get that to work.
here is the react component which renders the Iframe.
basically I need to trigger the documentDrawerLoaded function once all of the iframe content has been rendered.
return React.createClass({
displayName: 'FilePickerColourSelector',
getInitialState: function() {
return {
listVisible: false
};
},
componentWillMount: function () {
console.log('loading...')
},
componentDidMount: function () {
this.refs.documentDrawer.getDOMNode().addEventListener('load', this.documentDrawerLoaded);
},
documentDrawerLoaded: function () {
console.log('drawer has been loaded');
document.getElementById('js-document-drawer-overlay').classList.toggle('active');
document.getElementById('js-document-drawer').classList.toggle('active');
},
render: function() {
var documentDrawerStyles = {
height: '100%',
width: '100%',
border: 'none'
}
return <iFrame
src={this.props.url}
style={documentDrawerStyles}
ref="documentDrawer"
scrolling="no"
>
</iFrame>;
},
});
If you are controlling the content of the iframe you may use cross-origin communication. Or in other words the site in your iframe knows when its content is fully loaded and broadcast that information to the parent page.
You can use Jquery for that:
$(function(){
$('#MainPopupIframe').on('load', function(){
$(this).show();
console.log('load the iframe')
});
$('#click').on('click', function(){
$('#MainPopupIframe').attr('src', 'http://heera.it');
});
});
as see in the example:
How can I detect whether an iframe is loaded?
Related
Is there a way to know if parent window has loaded from within iframe?
I want to run a function which is in iFrame but I need to run it after all the parent windows are loaded and the event listener will be inside the iframe.
I tried the following but it runs before parent windows are loaded.
window.addEventListener('load', function () {
alert("It's loaded!")
});
One way would be to add the iframe dynamically:
parent:
window.addEventListener('load', function () {
var iframe = document.createElement('iframe');
iframe.src = "https://www.example.com";
document.body.appendChild(iframe);
});
iframe:
window.addEventListener('load', function () {
alert('hello world!');
//doSomethingUseful();
}
This way, you could be certain that they will load in a specific order. However, as they'd be loading in series, the increase in total page load time could become noticeable.
Alternatively, you could use this approach. This one may not work as is, if one page happens to finish loading before the other. If you do opt for this approach, it may be necessary to communicate in both directions so that the first page to load finds out when the second page has loaded. That may look like this:
parent:
newEvent();
window.document.addEventListener('myCustomEventI', newEvent, false);
function newEvent() {
var data = { loaded: true }
var event = new CustomEvent('myCustomEventP', { detail: data })
window.parent.document.dispatchEvent(event);
}
iframe:
newEvent();
window.document.addEventListener('myCustomEventP', handleEvent, false);
function newEvent() {
var data = { loaded: true }
var event = new CustomEvent('myCustomEventI', { detail: data })
window.parent.document.dispatchEvent(event);
}
function handleEvent(e) {
alert('both loaded!');
//doSomethingUseful();
}
Brainf... with iframes.
So, current page structure:
page -> main_iframe -> iframe1 -> iframe2
The scripts are executed in "main_iframe".
This "main_iframe" has "iframe1" and "iframe1" has "iframe2" inside:
I want to change width=100% of iframe2 which is inside iframe1, which is inside main_iframe
SO i am executing following js at "main_iframe" and it works:
document.querySelector('#iframe1').onload = function() {
setTimeout(function(){
document.querySelector('#iframe1').contentDocument.querySelectorAll("iframe")[0].style.width = '100%';
}, 3000);
}
But how to remove timeout timer so it will render as soon as iframe2 has loaded?
I've tried following:
document.querySelector('#iframe1').onload = function() {
document.querySelector('#iframe1').contentDocument.querySelectorAll("iframe")[0].onload = function () {
document.querySelector('#iframe1').contentDocument.querySelectorAll("iframe")[0].style.width = '100%';
}
};
But without luck, nothing is happening after onload fires.
You can reference the outer iframe with this inside the load event handler, which makes it a bit tidier. This should do what you need...
document.querySelector("#iframe1").addEventListener("load", function() {
this.contentDocument.querySelector("iframe").style.width = "100%";
});
I have a react component rendering on page load. The content includes lots of rich media that I want to lazy load only when the content is on screen and subsequently unload it when it's not. More content is loaded as the user scrolls.
I'm using a combination of techniques to handle lazy loading iframes, videos, and images and it works well outside of content rendered via React. Mostly custom jQuery and the Lazy Load Anything library.
My main issue is that I can't get my lazy load function to trigger on content just placed into the dom. It works once the user resizes/scrolls (I have a events for this that are triggered appropriately). How do I get it to trigger when the content is available?
I've tried triggering it from componentDidMount but this doesn't seem to work as the content has yet to be placed into the DOM.
I suppose I could just check for content every n seconds but I'd like to avoid this for performance reasons.
Here's my simplified code:
var EntriesList = React.createClass({
render: function() {
var entries = this.props.items.map(function(entry) {
return (
<div className="entry list-group-item" key={entry.id}>
// lazy items video, image, iframe...
<img src="1px.gif" className="lazy" datasource="/path/to/original" />
<video poster="1px.gif" data-poster-orig="/path/to/original" preload="none">{entry.sources}</video>
</div>
);
});
return(<div>{entries}</div>);
}
});
var App = React.createClass({
componentDidMount: function() {
$.get('/path/to/json', function(data) {
this.setState({entryItems: data.entries});
}.bind(this));
// What do I put here to trigger lazy load? for the rendered content?
myLazyLoad(); // does not work on the new content.
},
getInitialState: function() {
return ({
entryItems: []
});
},
render: function() {
return (<div><EntriesList items={this.state.entryItems} /></div>);
}
});
React.render(<App />, document.getElementById('entries'));
With the npm package react-lazy-load-image-component, you just have to wrap the components that you want to lazy load with <LazyLoadComponent> and it will work without any other configuration.
import { LazyLoadComponent } from 'react-lazy-load-image-component';
var EntriesList = React.createClass({
render: function() {
var entries = this.props.items.map(function(entry) {
return (
<LazyLoadComponent>
<div className="entry list-group-item" key={entry.id}>
// lazy items video, image, iframe...
<img src="1px.gif" className="lazy" />
<video poster="1px.gif" data-poster-orig="/path/to/original" preload="none">{entry.sources}</video>
</div>
</LazyLoadComponent>
);
});
return(<div>{entries}</div>);
}
});
var App = React.createClass({
componentDidMount: function() {
$.get('/path/to/json', function(data) {
this.setState({entryItems: data.entries});
}.bind(this));
},
getInitialState: function() {
return ({
entryItems: []
});
},
render: function() {
return (<div><EntriesList items={this.state.entryItems} /></div>);
}
});
React.render(<App />, document.getElementById('entries'));
Disclaimer: I'm the author of the package.
If you are trying to use the jquery plugin you may end with a DOM out of sync with that rendered by React. Also in your case the lazy load function should be called in the EntriesList component, not from its parent.
You could use a very simple component as react-lazy-load:
https://github.com/loktar00/react-lazy-load
or just take inspiration from its source code to implement your own.
var EntriesList = React.createClass({
render: function() {
var entries = this.props.items.map(function(entry) {
return (
<div className="entry list-group-item" key={entry.id}>
// lazy items video, image, iframe...
<LazyLoad>
<img src="1px.gif" datasource="/path/to/original" />
<video poster="1px.gif" data-poster-orig="/path/to/original" preload="none">{entry.sources}</video>
</LazyLoad>
</div>
);
});
return(<div>{entries}</div>);
}
});
Try to check on scroll event to your div parent container (the div that you render on App class:
var App = React.createClass({
componentDidMount: function() {
$.get('/path/to/json', function(data) {
this.setState({entryItems: data.entries});
}.bind(this));
},
myLazyLoad: function(e) {
// here do actions that you need: load new content, do ajax request, ...
// example: check you scrolling and load new content from page 1, 2, 3 ... N
var self = this;
$.get('path/to/json/?page=N', function(data) {
self.setState({entryItems: data.entries});
});
},
getInitialState: function() {
return ({
entryItems: []
});
},
render: function() {
return (<div onScroll={this.myLazyLoad}><EntriesList items={this.state.entryItems} /></div>);
}
});
Lazy Loading React
A component can lazily load dependencies without its consumer knowing using higher order functions, or a consumer can lazily load its children without its children knowing using a component that takes a function and collection of modules, or some combination of both.
https://webpack.js.org/guides/lazy-load-react/
You can add JS events in SugarCRM 7.2 by creating a custom record.js.
The problem I'm having is that they fire before the page is loaded so elements I'm trying to affect don't exist.
I have tried the following:
$(document).ready(function() { alert(0); }) // fires before page is loaded
$(document).on('load', function() { alert(1); }) // doesn't fire at all
$(window).load(function() { alert(2); }) // doesn't fire at all
Any help in resolving this would be much appreciated.
record.js
({
extendsFrom: 'RecordView',
initialize: function (options) {
this._super('initialize', [options]);
SUGAR.util.ajaxCallInProgress = function () {
alert(0);
$('[name="duplicate_button"]').hide();
},
})
The way I got this to work was to use the following code in custom/modules//clients/base/views/record/record.js
({
extendsFrom: 'AccountsRecordView',
initialize: function (options) {
this._super('initialize', [options]);
this.on("render", this.SetHomeButtons, this); //calls SetHomeButtons
},
SetHomeButtons: function () {
some code ....
},
})
The function SetHomeButtons is called once the page is loaded
Another way of doing it is to overwrite the render function to call your custom code
That doesn't work because of AJAX.
Edit: in Sugar 7 you have the function SUGAR.util.ajaxCallInProgress() it retruns false when every Request is done (all Content Elements have been loaded)
Is there a way I can use Mootools selectors to select element(s) within iframe content?
$('#myIframe input').addEvent('focus', function() {
// Do something
});
Thank you in advance!
If the iFrame is in the same domain and you are using Mootools inside it you can try:
$('myIframe').contentDocument.getElements('input').addEvent(
Example
Otherwise try this:
$('myIframe').contentDocument.querySelector('input').addEventListener(
Example
Edit:
If you want to catch the load event and then add the focus listener you could use:
var iframe = new IFrame({
src: '/RN95f/3/show',
styles: {
border: '2px solid #ccf'
},
events: {
load: function () {
alert('The iframe has finished loading.');
this.contentDocument.getElements('input').addEvent('focus', function () {
// Do something
alert('focus on input detected!');
console.log(this);
});
}
}
});
$(document.body).adopt(iframe);
Example
Sergio's answer is correct, thank you Sergio for your reply! However to get that to work, I needed to ensure iframe content was loaded before it would find input elements...
$('myIframe').onload = function() {
$('myIframe').contentDocument.getElements('input').addEvent('focus', function() {
// Do something
});
};