【问题标题】:Mithril: render one DOM element bases on anotherMithril:基于另一个渲染一个 DOM 元素
【发布时间】:2016-12-20 19:53:13
【问题描述】:

我有一个固定高度的 div(主体),其中包含两个子项、标题和内容。单击按钮时标题的高度会发生变化,并且内容的高度应自动调整以填充正文的其余部分。现在的问题是,新标题的高度是在标题和内容 div 渲染后计算的,因此内容 div 的高度不会在按钮单击时更新。这是缩短的代码:

return m('.body', {
  style: {
    height: '312px'
  }
}, [
  m('.header', /* header contents */),
  m('.content', {
    style: {
      height: (312 - this._viewModel._headerHeight()) + 'px'
    }
  }, /* some contents */)
])

headerHeight 函数计算标题的高度并对其应用更改。但是,新高度是在渲染后计算的,因此不会立即应用于内容高度的计算 - 总会有延迟。

有解决办法吗?

【问题讨论】:

    标签: javascript css mithril.js


    【解决方案1】:

    在处理动态 DOM 布局时,这是一个常见的问题,其中一些可写的 DOM 属性是从其他可读的 DOM 属性派生的。这在像 Mithril 这样的声明性虚拟 DOM 习惯用法中尤其难以推理,因为它们基于这样一个前提,即每个视图函数都应该是 UI 状态的自完成快照——在这种情况下这是不可能的。

    您有 3 个选择:您可以通过直接在 Mithril 的视图之外操作 DOM 来打破虚拟 DOM 惯用语来实现此功能,或者您可以对组件建模以在“2 pass draw”上操作,其中每个对 header 元素的潜在更改会导致 1 次绘制来更新标题,而第二次绘制来相应地更新内容。或者,您也许可以使用纯 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 相关的属性时,这种方法会变得更加有用,因为您可以在渲染视图时检查整个视图模型 — 但它也更加复杂和低效,尤其是对于您的场景:

    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——是使用 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,标题应该增长以适应其内容并且永远不会缩小,并且内容应该缩小但永远不会增长。

    【讨论】:

    • 这是非常有用的见解!我的直观解决方案是 flexbox,但由于我们希望确保支持所有浏览器而被拒绝。我实施了第一个选项,就像一个魅力!非常感谢@Barney !!
    猜你喜欢
    • 2021-11-28
    • 1970-01-01
    • 2023-03-11
    • 1970-01-01
    • 2013-07-20
    • 1970-01-01
    • 1970-01-01
    • 2018-05-30
    • 1970-01-01
    相关资源
    最近更新 更多