Polymer 聚合物纸对话框:如何知道何时可以切换注入对话框?
在我的应用程序中,为了响应用户输入,我将一个包含可滚动区域(Polymer 聚合物纸对话框:如何知道何时可以切换注入对话框?,polymer,Polymer,在我的应用程序中,为了响应用户输入,我将一个包含可滚动区域(paper dialog scrollable)的纸质对话框作为主体的最后一个子对象注入DOM。我会在需要时注入它,因为出于一些不同的原因,我发现在页面中包含对话框是不切实际的,以防用户决定激活它。我可以深入探讨这些原因,但我认为这不会有什么成效 我按如下方式插入对话框: var fragment = "<paper-dialog id='mydialog' ...><paper-dialog-scrollable .
paper dialog scrollable
)的纸质对话框作为主体的最后一个子对象注入DOM。我会在需要时注入它,因为出于一些不同的原因,我发现在页面中包含对话框是不切实际的,以防用户决定激活它。我可以深入探讨这些原因,但我认为这不会有什么成效
我按如下方式插入对话框:
var fragment = "<paper-dialog id='mydialog' ...><paper-dialog-scrollable ...>...";
$('body').append(fragment);
var dialog = $('#mydialog').get(0);
Polymer.Base.async(function() { dialog.toggle(); }, 100);
我相信这种差异与Firefox比Chrome需要更多的填充物有关。我尝试的下一件事是用以下代码激活对话框:
Polymer.Base.async(function(){ dialog.toggle(); }, 1);
通过此更改,当我尝试调用它时,toggle()
方法就出现了,并且出现了对话框
在Chrome上测试时,我遇到的下一个问题是,如果纸张对话框
包含一个可滚动部分(纸张对话框可滚动
),如果我在注入对话框后“过快”激活该对话框,则可滚动部分的高度将为零。这是因为“scrollable”div上有一个“fit”类,它是纸张对话框scrollable
元素的唯一子元素。我通过手动删除ChromeDeveloperTools中的“fit”类来验证这一点,并确保对话框正确显示
在可滚动的纸张对话框
的代码中,我发现:
attached: function()
{
this.classList.add('no-padding');
// Set itself to the overlay sizing target
this.dialogElement.sizingTarget = this.scrollTarget;
// If the host is sized, fit the scrollable area to the container.
// Otherwise let it be its natural size.
requestAnimationFrame(function() {
if (this.offsetHeight > 0) {
// this happens when I toggle "too quickly"
this.$.scrollable.classList.add('fit');
}
this._scroll();
}.bind(this));
}
如果在切换对话框之前等待更长时间:
Polymer.Base.async(function(){ dialog.toggle(); }, 100);
。。然后“fit”类不存在&对话框的可滚动部分被正确显示。但是,这不是解决方案,因为可能需要等待更长的时间(或没有那么长),这取决于机器的速度、当前负载等。我需要对话框可靠地工作,而无需在切换之前等待超过必要的时间。是否有一些我可以监听的事件会在安全切换对话框时触发?另外,有人知道应用“fit”类的可滚动的纸张对话框
代码吗?也许有某种方法可以阻止在一开始就应用这个类名(除了让用户等待的时间超过真正需要的时间)?在附加可滚动的纸张对话框之前等待事件
在chrome中有本机html导入和自定义元素,因此您无需等待此事件,因为此过程是同步的,但在firefox中html导入不是本机的,而是使用polyfills添加的
window.addEventListener('WebComponentsReady', function(e) {
var dialogScrollable = Polymer.Base.create('paper-dialog-scrollable');
var dialog = Polymer.Base.create('paper-dialog', { id: 'mydialog', });
dialog.appendChild(dialogScrollable);
document.body.appendChild(dialog);
});
或者与你举的例子更相似
window.addEventListener('WebComponentsReady', function(e) {
var fragment = "<paper-dialog id='mydialog' ...><paper-dialog-scrollable ...>...";
$('body').append(fragment);
var dialog = $('#mydialog').get(0);
});
window.addEventListener('WebComponentsReady',函数(e){
var fragment=“…”;
$('body')。追加(片段);
var dialog=$('#mydialog').get(0);
});
这里有一个指向的链接,显示WebComponentsReady事件已触发,对话框已切换。
这里也显示了dialogScrollable的问题,但这可能是一个关于堆栈溢出的单独问题,因为它与“如何知道何时可以切换插入的对话框”没有直接关系。关于“WebComponentsReady”事件
Ryan White建议的WebComponentsReady
事件听起来可能会有所帮助,但在最初加载的页面上的所有导入都已加载并且页面上的所有自定义元素都已升级之后,它会在页面上只触发一次。在我的测试中,我发现在那之后它不会再开火了。由于Web组件polyfill(例如Firefox上需要)加载导入并异步执行元素升级,因此在使用初始页面中存在的任何组件或自定义元素之前,需要等待WebComponentsReady
。如果初始页面确实导入web组件,但不包含自定义元素,则WebComponentsReady
事件仍会发出导入加载已完成且已准备好使用的信号
在我的情况下,最初加载的页面不导入web组件,也不包含自定义元素。我不想加载所有可能需要的组件,也不想实例化用户可能要求的所有对话框。相反,我希望加载web组件并根据需要创建自定义元素。在接下来的部分中,我将分享我所学到的关于动态注入web组件(以及使用它们的自定义元素)的知识
注入Web组件并等待其加载
这很简单
var util = {};
///////////////////////////////////////////////////////////////////////////////////////////////////
// util.listenOnce(elem, type, listener, useCapture)
// Return a promise for an event of type <type> raised on <elem>.
///////////////////////////////////////////////////////////////////////////////////////////////////
util.listenOnce = function(elem, type, useCapture)
{
var deferred = $.Deferred();
var promise = deferred.promise();
var callback = function()
{
deferred.resolve.apply(deferred, arguments);
elem.removeEventListener(type, callback, useCapture);
};
elem.addEventListener(type, callback, useCapture);
return promise;
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// util.urlNormalize(url)
// If <url> is a site-local URL, return a full URL version of it, otherwise return <url> as-is.
///////////////////////////////////////////////////////////////////////////////////////////////////
util.urlNormalize = function(url)
{
// already a full URL -> return as-is
if ((url.indexOf('http:') == 0) || (url.indexOf('https:') == 0)) return url;
var path;
if (url[0] == '/')
{
path = url;
}
else
{
path = window.location.pathname;
if (path.charAt(path.length - 1) != '/') path += '/';
path += url;
}
return window.location.protocol + '//' + window.location.hostname + path;
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// util.addImport(url)
// Add an HTML import to the DOM, returning a promise.
// It's OK to call this multiple times with the same url.
///////////////////////////////////////////////////////////////////////////////////////////////////
{
var completeUrls = [];
util.addImport = function(url)
{
// already loaded this import?
if (completeUrls.indexOf(url) >= 0)
{
return $.Deferred().resolve().promise();
}
// find the link element for this import
var urlFull = util.urlNormalize(url);
var links = $('head > link');
var link;
links.each(function(){
if ((this.rel == 'import') && (this.href == urlFull))
{
link = this;
return false;
}
});
// create the <link> element if necessary, and watch for the 'load' event
var loaded;
if (link)
{
loaded = util.listenOnce(link, 'load');
}
else
{
// create a <link> element
link = document.createElement('link');
link.rel = 'import';
link.href = url;
// on load, update completeUrls
loaded = util.listenOnce(link, 'load');
loaded.then(function() { completeUrls.push(url); });
// append the <link> element to the head
var head = document.getElementsByTagName('head')[0];
head.appendChild(link);
}
return loaded;
};
}
例如
注入自定义元素并等待与之交互
如果所有必需的组件导入都已加载,并且您像我一样注入了一个元素
var fragment = "<paper-dialog id='mydialog' ...><paper-dialog-scrollable ...>...";
$('body').append(fragment);
var dialog = $('#mydialog').get(0);
正如我发现的,如果我只是给polyfill一个工作的机会,那么我就有了一个升级的元素可以与之交互:
Polymer.Base.async(function() { dialog.toggle(); }, 1);
在大多数情况下,只要在与元素交互之前等待升级就可以了。但是,对于可滚动的纸张对话框
,它已升级并不意味着可以继续切换其父对话框。原因实际上是在我的问题中包含的纸张对话框scrollable.attached()
的代码中
这又是:
attached: function()
{
this.classList.add('no-padding');
// Set itself to the overlay sizing target
this.dialogElement.sizingTarget = this.scrollTarget;
// If the host is sized, fit the scrollable area to the container.
// Otherwise let it be its natural size.
requestAnimationFrame(function() {
if (this.offsetHeight > 0) {
this.$.scrollable.classList.add('fit');
}
this._scroll();
}.bind(this));
}
升级后不久,我尝试toggle()
父纸张对话框
时,“fit”类将应用于div#scrollable
容器,这导致可滚动区域被折叠。正如我们在纸张对话框scrollable.attached()
中看到的,它不会立即测试this.offsetHeight>0
,但实际上它使用requestAnimationFrame()
等待,直到下一次重新绘制之前执行此测试。当我在升级后仅~1ms调用dialog.toggle()
时,这会导致对话框可见,因此可滚动区域的内容高度非零。但是,当我等待100毫秒后才这样切换时:
var fragment = "<paper-dialog id='mydialog' ...><paper-dialog-scrollable ...>...";
$('body').append(fragment);
var dialog = $('#mydialog').get(0);
Polymer.Base.async(function() { dialog.toggle(); }, 100);
。。然后
attached: function()
{
this.classList.add('no-padding');
// Set itself to the overlay sizing target
this.dialogElement.sizingTarget = this.scrollTarget;
// If the host is sized, fit the scrollable area to the container.
// Otherwise let it be its natural size.
requestAnimationFrame(function() {
if (this.offsetHeight > 0) {
this.$.scrollable.classList.add('fit');
}
this._scroll();
}.bind(this));
}
Polymer.Base.async(function() { dialog.toggle(); }, 100);
requestAnimationFrame(function() { dialog.toggle(); });
Polymer.Base.async(function(){
requestAnimationFrame(function(){
dialog.toggle();
});
}, 1);
util.dialog = function(options)
{
// provide default options
var defaults =
{
imports: [],
id: 'cms-dialog',
classes: '',
title: '',
content: '',
scrollable: false,
dismissButtonLabel: 'Cancel',
dismissButtonFn: null,
confirmButtonLabel: 'OK',
confirmButtonFn: null
};
options = $.extend({}, defaults, options);
options.classes += ' cms-dialog';
// make a list of required components
var imports = options.imports;
var polymerRoot = '//cdn.rawgit.com/download/polymer-cdn/1.2.3/lib/';
imports.push(polymerRoot + 'neon-animation/animations/scale-up-animation.html');
imports.push(polymerRoot + 'neon-animation/animations/fade-out-animation.html');
imports.push(polymerRoot + 'paper-dialog/paper-dialog.html');
imports.push(polymerRoot + 'paper-dialog-scrollable/paper-dialog-scrollable.html');
imports.push(polymerRoot + 'paper-button/paper-button.html');
// load required imports, then create the dialog
util.addImports(imports).then(function(){
// nuke any existing dialog
$('.cms-dialog').remove();
// create paper-dialog
var dialogProps = {
id: options.id,
modal: true,
className: options.classes,
entryAnimation: 'scale-up-animation',
exitAnimation: 'fade-out-animation'
};
var dialog = Polymer.Base.create('paper-dialog', dialogProps);
// add title
if (options.title)
{
$(dialog).append("<h2 class='title'>" + options.title + '</h2>');
}
// add content
var content;
if (options.scrollable)
{
var scrollableProps = {
className: 'content'
};
content = Polymer.Base.create('paper-dialog-scrollable', scrollableProps);
content.dialogElement = dialog;
$(content.scrollTarget).append(options.content);
}
else
{
content = $("<div class='content'>" + options.content + "</div>").get(0);
}
$(dialog).append(content);
// add buttons
var dismissButton = '';
if (options.dismissButtonLabel)
{
dismissButton =
"<paper-button id='dialog-dismiss-button' class='cms-button' dialog-dismiss>" +
options.dismissButtonLabel +
"</paper-button>";
}
var confirmButton = '';
if (options.confirmButtonLabel)
{
confirmButton =
"<paper-button id='dialog-confirm-button' class='cms-button' dialog-confirm>" +
options.confirmButtonLabel +
"</paper-button>";
}
$(dialog).append(
"<div class='buttons'>" +
dismissButton +
confirmButton +
"</div>");
// activate the dialog
var toggle = function(){
// install on-click event handlers
if (options.dismissButtonFn)
{
$('#dialog-dismiss-button').on('click', options.dismissButtonFn);
}
if (options.confirmButtonFn)
{
$('#dialog-confirm-button').on('click', options.confirmButtonFn);
}
// run on-ready callback (if given)
if (options.onReady) options.onReady();
// bring up the dialog
dialog.toggle();
};
// toggle when it's safe
var attachedTarget = options.scrollable ? content : dialog;
var attachedOrig = attachedTarget.attached;
attachedTarget.attached = function() {
if (attachedOrig) attachedOrig.apply(attachedTarget, arguments);
requestAnimationFrame(toggle);
};
// toggle when it's safe (this also appears to work)
//Polymer.Base.async(function() { requestAnimationFrame(toggle); }, 1);
// add the dialog to the document
document.body.appendChild(dialog);
});
};