【问题标题】:Get incorrect offsetWidth and offsetHeight values获取不正确的 offsetWidth 和 offsetHeight 值
【发布时间】:2017-05-31 03:52:26
【问题描述】:

这是我的 angular2 代码。

模板

<div #picker class="slider">
  <div class="slider-track">
    <div #sliderSelectionEl class="slider-selection"></div>
    <div #sliderHandle1 class="slider-handle"></div>
    <div #sliderHandle2 class="slider-handle"></div>
  </div>
  <div #tooltipEl class="tooltip">
    <div class="tooltip-arrow"></div>
    <div #tooltipInner class="tooltip-inner"></div>
  </div>
  <input type="text" class="span2" value="" id="sl2"><br/>
</div>


组件

从“@angular/core”导入 {Component、OnInit、Input、ViewChild、ElementRef、Renderer}; 导出类 SliderComponent 实现 OnInit { @ViewChild('picker') 选择器:ElementRef; 构造函数(私有渲染器:渲染器,私有 el:ElementRef){ } ngAfterViewInit() { this.renderer.setElementClass(this.picker.nativeElement, 'slider-horizo​​ntal', true); console.log(this.picker.nativeElement.offsetWidth); console.log(this.picker.nativeElement.offsetHeight); } }

.slider-horizontal {
  width: 210px;
  height: 20px;
}

问题是每次加载时打印的值都不同。我猜这个问题是由于浏览器没有完成加载div。你知道有什么解决办法吗?

【问题讨论】:

  • 在中间有任何动态元素之前都是一样的。你有吗?
  • 你用的是什么浏览器?
  • @micronyks 是的,我在 div 中添加了一些类,但在 2 条打印线之前。这个有影响吗?
  • @yurzui 我用 Chrome
  • 每次加载都会显示相同的结果

标签: html angular


【解决方案1】:

为了获得正确的偏移值,您可以使用:ngAfterContentChecked 和 AfterContentChecked 接口。

每次更改检测运行后都会调用此方法。因此,在此方法中使用标志(或计数器)和 setTimeOut:

if (this.counter <= 10) {
      // this print offsetwidth of my element
      console.log('mm ' + this.container.nativeElement.offsetWidth);


      // setTimeOut allow to run another changedetection 
      // so ngAfterContentChecked will run again
      setTimeout(() => { }, 0);

      //you could use a counter or a flag in order to stop getting the right width
      this.counter++;
    }

希望对您有所帮助!欢迎发表评论

【讨论】:

    【解决方案2】:

    您可以使用

    检测大小变化

    MutationObserver

    这个新 API 的最大受众可能是那些 编写 JS 框架,[...] 另一个用例是您使用框架来操纵 DOM 并需要对这些框架做出反应的情况 有效地修改(并且没有 setTimeout hacks!)。

    您可以使用它来检测元素的变化:

    // select the target node
    var target = document.querySelector('#some-id'); // or 
    
    // create an observer instance
    var observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            console.log(mutation.type);
        });
    });
    
    // configuration of the observer:
    var config = { attributes: true, childList: true, characterData: true }
    
    // pass in the target node, as well as the observer options
    observer.observe(target, config);
    
    // later, you can stop observing
    observer.disconnect();
    

    对于您的情况,您可以在 ngAfterViewInit 中使用它并刷新您的偏移量大小。您可以更具体,只检测一些突变,然后提取您的偏移量。

    更多信息:

    文档:https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver

    兼容性:https://caniuse.com/#feat=mutationobserver

    演示

    var observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
           console.log(mutation);
           if(mutation.attributeName == 'class') // detect class change
              /*
              or if(mutation.target.clientWidth == myWidth)
              */
              showOffset(mutation.target);
              
              observer.disconnect();
        });
    });
    
    var config = { attributes: true}
    var demoDiv = document.getElementById('demoDiv');
    var logs = document.getElementById('logs');
    
    // wait for document state to be complete
    if (document.readyState === "complete") {
        ngAfterViewInit();
      }
      
    document.onreadystatechange = function () {
      if (document.readyState === "complete") {
        ngAfterViewInit();
      }
    }
    
    // observe changes that effects demoDiv + add class
    function ngAfterViewInit(){
    	observer.observe(demoDiv, config);
      demoDiv.classList.add('slider-horizontal');
    }
    
    // show offsetWidth + height. 
    // N.B offset width and height will be bigger than clientWidth because I added a border. If you remove the border you'll see 220px,20px
    
    function showOffset(element){
      offsetMessage = "offsetWidth:" + demoDiv.offsetWidth + " offsetHeight: " + demoDiv.offsetHeight;
    	console.log(offsetMessage);
      logs.innerHTML = offsetMessage;
    }
    .slider-horizontal {
      border: 2px solid red;
      width: 210px;
      height: 20px;
      background: grey;
    }
    <div id='demoDiv'> I am a demo div </div>
    <div style="margin-top: 20px;"> logs : <span id='logs' style=" padding: 5px; border: 1px solid black"></span></div>

    【讨论】:

    • 请您包括他应该寻找的适当类型的突变,我将奖励赏金,谢谢 Mium。编辑:无论如何我都授予它,但请包括在内。
    • 对不起,setTimeout 对我有用,这不起作用,为什么?我使用 Angular 7。为什么要显式调用 document.readyState === "complete"?
    【解决方案3】:

    您必须在渲染周期后安排对“offsetWidth”的调用,角度执行绘制在微任务队列的末尾,因此您可以尝试setTimeout(..., 0) 或在zonejs 之外运行Promise.resolve().then(...)。希望对您有所帮助。

    【讨论】:

    • 感谢您的回答,您知道您可以在没有第二个参数的情况下执行 setTimeout(() => {}) 吗?
    • 不知道它是可选的,但我不介意明确声明:)
    • 我完全支持你,我忽略它的原因是因为如果我设置一个计时器,我大脑的一部分会认为我在等待 10 毫秒,所以对我来说它表明零延迟。我见过人们为此目的使用 setTimeout(..., 10) ,即使它没有引起任何问题,10ms 下落不明的错误我......至少它是客户端。 :)
    • 这似乎不太可靠,我已经测试了很多。
    猜你喜欢
    • 2017-06-01
    • 2011-11-16
    • 1970-01-01
    • 2014-03-29
    • 2011-04-01
    • 1970-01-01
    • 2011-11-08
    • 1970-01-01
    • 2020-08-12
    相关资源
    最近更新 更多