Here I'm using an iframe to display a page-in-page that contains a Gridstack grid-based editor to reposition its elements. The inner page should consist of plain HTML and CSS. All gridstack-related CSS and any ko-related JS calls should be made from the outer page. I works on a single page.
Now the problem is that as soon as I the Gridstack grid into an iframe and bind it to a viewmodel. It doesn't size properly. The widgets are present in the HTML, but not visible (due to zero-height)
In this case the Knockout binding gets executed, but the CSS height attribute and vertical Y-position fail to get computed dynamically.
Note: the width and horizontal X-position get assigned correctly, because they are loaded from a static CSS class.
Update: Probably to fix this one needs to override this function found inside Gridstack. It dynamically creates CSS in the outer page and not the inner. This could be the cause of the problem:
createStylesheet: function(id) {
var style = document.createElement('style');
style.setAttribute('type', 'text/css');
style.setAttribute('data-gs-style-id', id);
if (style.styleSheet) {
style.styleSheet.cssText = '';
} else {
style.appendChild(document.createTextNode(''));
}
document.getElementsByTagName('head')[0].appendChild(style);//POSSIBLE CAUSE OF THE PROBLEM
return style.sheet;
},
The snippet bellow still won't execute, I recommend looking at my JSFiddle
//WORKAROUND TO SKIP THE SAME-ORIGIN POLICY
$('#editor-iframe').load(function() {
$(function() {
$('#editor-iframe').contents().find('html').html('<head><title></title><style type="text/css">.grid-stack { background: lightgoldenrodyellow; } .grid-stack-item-content { color: #2c3e50; text-align: center; background-color: #18bc9c; } </style><link rel="stylesheet" href="https://rawgit.com/troolee/gridstack.js/master/dist/gridstack.css" /></head><body><div class="grid-stack" data-bind="gridstack: widgets"><div class="grid-stack-item" data-bind="attr: {\'data-gs-x\': $data.x, \'data-gs-y\': $data.y, \'data-gs-width\': $data.width, \'data-gs-height\': $data.height, \'data-gs-auto-position\': $data.auto_position}"><div class="grid-stack-item-content"><button data-bind="click: $parent.deleteWidget">Delete me</button></div></div></div></body>');
//THE ACTUAL CODE
ko.bindingHandlers.gridstack = {
init: function(element, valueAccessor, allBindingsAccessor, data, context) {
ko.bindingHandlers.foreach.init(element, valueAccessor, allBindingsAccessor, data, context);
return {
controlsDescendantBindings: true
};
},
update: function(element, valueAccessor, allBindingsAccessor, data, context) {
var widgets = valueAccessor(),
grid = $(element).gridstack().data('gridstack'),
afterAddWidget = function(items) {
var item = _.find(items, function(i) {
return i.nodeType === 1;
});
grid.addWidget(item);
ko.utils.domNodeDisposal.addDisposeCallback(item, function() {
grid.removeWidget(item);
});
//RESIZE IFRAME TO FIT NEW CONTENT
var iframeWin = document.getElementById('editor-iframe').contentWindow;
$('#editor-iframe')
.height(Math
.max(
iframeWin.document.body.scrollHeight, iframeWin.document.body.offsetHeight,
iframeWin.document.documentElement.clientHeight,
iframeWin.document.documentElement.scrollHeight,
iframeWin.document.documentElement.offsetHeight) + 'px');
},
newVA = function() {
return {
data: widgets,
afterRender: afterAddWidget
};
};
ko.bindingHandlers.foreach.update(element, newVA, allBindingsAccessor, data, context);
}
};
var Controller = function(widgets) {
var self = this;
this.widgets = ko.observableArray(widgets);
this.addNewWidget = function() {
this.widgets.push({
x: 0,
y: 0,
width: Math.floor(1 + 3 * Math.random()),
height: Math.floor(1 + 3 * Math.random()),
auto_position: true
});
return false;
};
this.deleteWidget = function(item) {
self.widgets.remove(item);
return false;
};
};
var widgets = [{
x: 0,
y: 0,
width: 2,
height: 2
}, {
x: 2,
y: 0,
width: 4,
height: 2
}, {
x: 6,
y: 0,
width: 2,
height: 4
}, {
x: 1,
y: 2,
width: 4,
height: 2
}];
var controller = new Controller(widgets);
ko
.applyBindings(
controller,
document
.getElementById('editor-iframe').contentWindow.document.body);
});
});
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" type="text/javascript"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.0/jquery-ui.js" type="text/javascript"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js" type="text/javascript"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/3.5.0/lodash.min.js" type="text/javascript"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-debug.js" type="text/javascript"></script>
<script type="text/javascript" src="https://rawgit.com/troolee/gridstack.js/master/dist/gridstack.js"></script>
<iframe id="editor-iframe" src="http://stacksnippets.net" sandbox="allow-same-origin" style="width:100%"></iframe>