Mithril:渲染一个 DOM 元素基于另一个
Mithril: render one DOM element bases on another
我有一个固定高度 div (body) 包含两个 children、header 和内容。 header 的高度在单击按钮时发生变化,内容的高度应自动调整以填充 body 的其余部分。现在的问题是,新的 header 的高度是在 header 和内容 div 渲染之后计算的,所以内容 div 的高度不会单击按钮后更新。这是缩短的代码:
return m('.body', {
style: {
height: '312px'
}
}, [
m('.header', /* header contents */),
m('.content', {
style: {
height: (312 - this._viewModel._headerHeight()) + 'px'
}
}, /* some contents */)
])
headerHeight 函数计算 header 的高度并对其应用更改。然而,新高度是在呈现后计算的,因此不会立即应用于内容高度的计算 - 总是存在滞后。
有解决办法吗?
这是处理动态 DOM 布局时的常见问题,其中一些可写 DOM 属性派生自其他可读 DOM 属性。这在像 Mithril 这样的声明性虚拟 DOM 习语中尤其难以推理,因为它们基于这样的前提,即每个视图函数都应该是 UI 状态的 self-complete 快照——在这种情况下不可能。
您有 3 个选择:您可以通过直接在 Mithril 视图之外操作 DOM 来突破虚拟 DOM 习语来实现此功能,或者您可以对组件进行建模以进行操作在“2 pass draw”上,header 元素的每个潜在更改都会导致 1 次绘制以更新 header 和第二次绘制以相应地更新内容。或者,您可以使用纯粹的 CSS 解决方案。
因为您只需要更新一个 属性,您几乎可以肯定选择第一个选项会更好。通过使用 config
函数,您可以编写在每次绘制视图后执行的自定义功能。
return m('.body', {
style: {
height: '312px'
},
config : function( el ){
el.lastChild.style.height = ( 312 - el.firstChild.offsetHeight ) + 'px'
}
}, [
m('.header', /* header contents */),
m('.content', /* some contents */)
])
第二个选项在虚拟 DOM 哲学方面更为惯用,因为它避免了直接 DOM 操作并将所有有状态数据保存在视图读取和应用的模型中。当您拥有大量动态 DOM-related 属性时,这种方法会变得更有用,因为您可以在呈现视图时检查整个视图模型——但它也更加复杂和低效,尤其是对于您的场景:
controller : function(){
this.headerHeight = 0
},
view : function( ctrl ){
return m('.body', {
style: {
height: '312px'
}
}, [
m('.header', {
config : function( el ){
if( el.offsetHeight != ctrl.headerHeight ){
ctrl.headerHeight = el.offsetHeight
window.requestAnimationFrame( m.redraw )
}
}, /* header contents */),
m('.content', {
style : {
height : ( 312 - ctrl.headerHeight ) + 'px'
}
}, /* some contents */)
])
}
第三个选项——depending on which browsers you need to support — would be to use the CSS flexbox module.
return m('.body', {
style: {
height: '312px',
display: 'flex',
flexDirection: 'column'
}
}, [
m('.header', {
style : {
flexGrow: 1,
flexShrink: 0
}
}, /* header contents */),
m('.content', {
style : {
flexGrow: 0,
flexShrink: 1
}
}, /* some contents */)
])
这样,您可以简单地声明容器是一个 flexbox,header 应该增长以适应其内容并且永不收缩,并且内容应该收缩但永不增长。
我有一个固定高度 div (body) 包含两个 children、header 和内容。 header 的高度在单击按钮时发生变化,内容的高度应自动调整以填充 body 的其余部分。现在的问题是,新的 header 的高度是在 header 和内容 div 渲染之后计算的,所以内容 div 的高度不会单击按钮后更新。这是缩短的代码:
return m('.body', {
style: {
height: '312px'
}
}, [
m('.header', /* header contents */),
m('.content', {
style: {
height: (312 - this._viewModel._headerHeight()) + 'px'
}
}, /* some contents */)
])
headerHeight 函数计算 header 的高度并对其应用更改。然而,新高度是在呈现后计算的,因此不会立即应用于内容高度的计算 - 总是存在滞后。
有解决办法吗?
这是处理动态 DOM 布局时的常见问题,其中一些可写 DOM 属性派生自其他可读 DOM 属性。这在像 Mithril 这样的声明性虚拟 DOM 习语中尤其难以推理,因为它们基于这样的前提,即每个视图函数都应该是 UI 状态的 self-complete 快照——在这种情况下不可能。
您有 3 个选择:您可以通过直接在 Mithril 视图之外操作 DOM 来突破虚拟 DOM 习语来实现此功能,或者您可以对组件进行建模以进行操作在“2 pass draw”上,header 元素的每个潜在更改都会导致 1 次绘制以更新 header 和第二次绘制以相应地更新内容。或者,您可以使用纯粹的 CSS 解决方案。
因为您只需要更新一个 属性,您几乎可以肯定选择第一个选项会更好。通过使用 config
函数,您可以编写在每次绘制视图后执行的自定义功能。
return m('.body', {
style: {
height: '312px'
},
config : function( el ){
el.lastChild.style.height = ( 312 - el.firstChild.offsetHeight ) + 'px'
}
}, [
m('.header', /* header contents */),
m('.content', /* some contents */)
])
第二个选项在虚拟 DOM 哲学方面更为惯用,因为它避免了直接 DOM 操作并将所有有状态数据保存在视图读取和应用的模型中。当您拥有大量动态 DOM-related 属性时,这种方法会变得更有用,因为您可以在呈现视图时检查整个视图模型——但它也更加复杂和低效,尤其是对于您的场景:
controller : function(){
this.headerHeight = 0
},
view : function( ctrl ){
return m('.body', {
style: {
height: '312px'
}
}, [
m('.header', {
config : function( el ){
if( el.offsetHeight != ctrl.headerHeight ){
ctrl.headerHeight = el.offsetHeight
window.requestAnimationFrame( m.redraw )
}
}, /* header contents */),
m('.content', {
style : {
height : ( 312 - ctrl.headerHeight ) + 'px'
}
}, /* some contents */)
])
}
第三个选项——depending on which browsers you need to support — would be to use the CSS flexbox module.
return m('.body', {
style: {
height: '312px',
display: 'flex',
flexDirection: 'column'
}
}, [
m('.header', {
style : {
flexGrow: 1,
flexShrink: 0
}
}, /* header contents */),
m('.content', {
style : {
flexGrow: 0,
flexShrink: 1
}
}, /* some contents */)
])
这样,您可以简单地声明容器是一个 flexbox,header 应该增长以适应其内容并且永不收缩,并且内容应该收缩但永不增长。