web-dev-qa-db-ja.com

間違った結果を返すgetBoundingClientRect

DOM内の要素の現在の位置とサイズを判断しようと少し苦労しています。画面の右側にあるカードベースのシステムを説明するために断片をまとめました。

私が構築しようとしている動作は、それらのカードのいずれかをクリックすると、別のカードが追加され(最終的には下になりますが、現在は上にあります)、塗りつぶす前に画面の左上隅に飛び出します利用可能なスペース。

d3.selectAll("attribute-card").on("click", function (d) {

   var rect = this.getBoundingClientRect();
   var card = d3.select("body")
          .append("div")
            .attr("class", "card")
            .style("background", "transparent")
            .style("border", "thin solid red")
            .style("left", rect.left + "px")
            .style("top", rect.top + "px")
            .style("width", (rect.right - rect.left) + "px")
            .style("height", (rect.bottom - rect.top) + "px")
            .style("position", "absolute");
});
html {
  height: 100%;
  margin: 0;
  font-family: Arial;
  overflow: hidden;
}
body {
  height: 100%;
}
svg {
  background: #2c272b;
  width: 100%;
  height: 100%;
}
.radial-menu .segment {
  fill: #3b3944;
}
.radial-menu .segment:hover {
  fill: #535060;
}
.radial-menu .symbol {
  pointer-events: none;
  fill: white;
}
.radial-menu .symbol.icon {
  font-family: 'FontAwesome';
}
.beam {
  stroke: #fff;
}
.planet circle {
  fill: #399745;
  stroke: #3b3944;
  stroke-width: 0;
  stroke-dasharray: 33,11;
}
.planet .related {
  fill: none;
  stroke: #3b3944;
  stroke-dasharray: none;
  stroke-width: 25px;
}
.planet text {
  fill: #000;
  opacity: 0.4;
  text-anchor: middle;
  pointer-events: none;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
.planet .name {
  font-size: 2.5em;
  width: 94%;
  margin: 125px 0px 0px 10px;
}
.planet.selected text {
  fill: white;
  opacity: 1;
}
.planet.focused text {
  fill: white;
  opacity: 1;
}
.moon circle {
  fill: #3b3944;
}
.moon:hover {
  fill: #535060;
}
.moon text {
  fill: white;
  text-anchor: middle;
  pointer-events: none;
}
.gravity {
  stroke: #3b3944;
  fill: #3b3944;
  stroke-linecap: round;
  stroke-width: 2px;
}
.card-list {
  background: #2c272b;
  position: absolute;
  top: 0;
  right: 0;
  width: 200px;
  min-height: 100%;
  opacity: 1;
}
.card {
  background: #dedede;
  border: 2px solid #ebebeb;
  margin: 5px 5px 5px 5px;
  border-radius: 8px;
  padding: 5px 15px 5px 15px;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
.card .title {
  font-weight: bold;
}
.card .summary {
  color: #cc8b11;
  font-weight: bold;
  font-size: 12px;
}
.card .summary .summary-item {
  margin: 0;
}
/*# sourceMappingURL=style.css.map */
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<html><head>
    <meta charset="utf-8">
    <meta name="msapplication-tap-highlight" content="no">
    <title name="Business Landscape Explorer Prototype"></title>
    <link href="bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
    <link rel="stylesheet" type="text/css" href="styles/style.css">
    <script src="d3.v3.js" charset="utf-8"></script><style type="text/css"></style>
</head>
<body>
    
    <div id="card-list" class="card-list">
        <div id="attributes" class="attribute-list" data-bind="foreach: attributes">
            <attribute-card params="value: $data"><div class="card attribute-card">                    <p class="title" data-bind="text: name">Name</p>                    <div class="summary" data-bind="foreach: summaries"></div>                </div></attribute-card>
        
            <attribute-card params="value: $data"><div class="card attribute-card">                    <p class="title" data-bind="text: name">Cost</p>                    <div class="summary" data-bind="foreach: summaries">                        <p class="summary-item" data-bind="text: $data">Average: £9 million</p>                                            <p class="summary-item" data-bind="text: $data">Total: £2,700 million</p>                    </div>                </div></attribute-card>
        
            <attribute-card params="value: $data"><div class="card attribute-card">                    <p class="title" data-bind="text: name">Start Date</p>                    <div class="summary" data-bind="foreach: summaries">                        <p class="summary-item" data-bind="text: $data">Earliest: 31st Jan 2007</p>                                            <p class="summary-item" data-bind="text: $data">Latest: 27th Nov 2019</p>                    </div>                </div></attribute-card>
        
            <attribute-card params="value: $data"><div class="card attribute-card">                    <p class="title" data-bind="text: name">Enabled</p>                    <div class="summary" data-bind="foreach: summaries">                        <p class="summary-item" data-bind="text: $data">True: 71%</p>                                            <p class="summary-item" data-bind="text: $data">False: 29%</p>                    </div>                </div></attribute-card>
        
            <attribute-card params="value: $data"><div class="card attribute-card">                    <p class="title" data-bind="text: name">Status</p>                    <div class="summary" data-bind="foreach: summaries">                        <p class="summary-item" data-bind="text: $data">Red: 11%</p>                                            <p class="summary-item" data-bind="text: $data">Amber: 36%</p>                                            <p class="summary-item" data-bind="text: $data">Green: 41%</p>                    </div>                </div></attribute-card>
        </div>
    </div>

    </body></html>

私がやっていることはかなり基本的で、クリックされた要素をつかみ、その境界矩形を測定し、同じサイズと位置でbodyに新しい要素を追加します。

d3.selectAll("attribute-card").on("click", function (d) {

   var rect = this.getBoundingClientRect();
   var card = d3.select("body")
          .append("div")
            .attr("class", "card")
            .style("background", "transparent")
            .style("border", "thin solid red")
            .style("left", rect.left + "px")
            .style("top", rect.top + "px")
            .style("width", (rect.right - rect.left) + "px")
            .style("height", (rect.bottom - rect.top) + "px")
            .style("position", "absolute");
});

私は getBoundingClientRect() について読んでいますが、仕様に従って欲しいことをしているようです。左を正しく取得することさえできません。この関数は単純に壊れていますか(驚かされるでしょうか)、またはCSSの一部がこのネイティブ関数を何らかの形で壊していますか?

ここに追加する必要があるのは、異なるブラウザーでオフになっている結果のスクリーンショットです。 IEは最も近いが、それでも下/右の値と格闘しているようだ。

enter image description here

16
Ian

まあ、私は非常に混乱していますが、私が望むように物事を機能させることができました。少しの推測作業に基づいてパディング、マージン、境界線を考慮に入れて計算を変更し、一部のスタイルを変更して、すべてが機能することを確認しました。これにより、次の計算が行われました。

    var rect = element.getBoundingClientRect();
    rect = {
        left: rect.left - margin.left,
        right: rect.right - margin.right - padding.left - padding.right,
        top: rect.top - margin.top,
        bottom: rect.bottom - margin.bottom - padding.top - padding.bottom - border.bottom  
    };
    rect.width = rect.right - rect.left;
    rect.height = rect.bottom - rect.top;
    return rect;

奇妙なことに、これをアプリケーションにプラグインしようとしてもまったく機能しませんでした。パディングの一部を取り出して、最終的には:

rect = {
        left: rect.left - margin.left,
        right: rect.right - border.right,
        top: rect.top - margin.top,
        bottom: rect.bottom - border.bottom - border.top
    };
    rect.height = rect.bottom - rect.top;
    rect.width = rect.right - rect.left;
    return rect;
function getBoundingRect(element) {

    var style = window.getComputedStyle(element); 
    var margin = {
        left: parseInt(style["margin-left"]),
        right: parseInt(style["margin-right"]),
        top: parseInt(style["margin-top"]),
        bottom: parseInt(style["margin-bottom"])
    };
    var padding = {
        left: parseInt(style["padding-left"]),
        right: parseInt(style["padding-right"]),
        top: parseInt(style["padding-top"]),
        bottom: parseInt(style["padding-bottom"])
    };
    var border = {
        left: parseInt(style["border-left"]),
        right: parseInt(style["border-right"]),
        top: parseInt(style["border-top"]),
        bottom: parseInt(style["border-bottom"])
    };
    
    
    var rect = element.getBoundingClientRect();
    rect = {
        left: rect.left - margin.left,
        right: rect.right - margin.right - padding.left - padding.right,
        top: rect.top - margin.top,
        bottom: rect.bottom - margin.bottom - padding.top - padding.bottom - border.bottom  
    };
    rect.width = rect.right - rect.left;
    rect.height = rect.bottom - rect.top;
    return rect;
    
};

d3.selectAll(".card").on("click", function (d) {

   var rect = getBoundingRect(this);
    
   var card = d3.select("body")
          .append("div")
            .attr("class", "card")
            .style("background", "transparent")
            .style("border", "thin solid red")
            .style("left", rect.left + "px")
            .style("top", rect.top + "px")
            .style("width", rect.width + "px")
            .style("height", rect.height + "px")
            .style("position", "absolute");
});
html {
  height: 100%;
  margin: 0;
  font-family: Arial;
  overflow: hidden;
}
body {
  height: 100%;
}
svg {
  background: #2c272b;
  width: 100%;
  height: 100%;
}
.radial-menu .segment {
  fill: #3b3944;
}
.radial-menu .segment:hover {
  fill: #535060;
}
.radial-menu .symbol {
  pointer-events: none;
  fill: white;
}
.radial-menu .symbol.icon {
  font-family: 'FontAwesome';
}
.beam {
  stroke: #fff;
}
.planet circle {
  fill: #399745;
  stroke: #3b3944;
  stroke-width: 0;
  stroke-dasharray: 33,11;
}
.planet .related {
  fill: none;
  stroke: #3b3944;
  stroke-dasharray: none;
  stroke-width: 25px;
}
.planet text {
  fill: #000;
  opacity: 0.4;
  text-anchor: middle;
  pointer-events: none;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
.planet .name {
  font-size: 2.5em;
  width: 94%;
  margin: 125px 0px 0px 10px;
}
.planet.selected text {
  fill: white;
  opacity: 1;
}
.planet.focused text {
  fill: white;
  opacity: 1;
}
.moon circle {
  fill: #3b3944;
}
.moon:hover {
  fill: #535060;
}
.moon text {
  fill: white;
  text-anchor: middle;
  pointer-events: none;
}
.gravity {
  stroke: #3b3944;
  fill: #3b3944;
  stroke-linecap: round;
  stroke-width: 2px;
}
.card-list {
  background: #2c272b;
  position: absolute;
  top: 0;
  right: 0;
  width: 200px;
  min-height: 100%;
  opacity: 1;
}
.card {
  background: #dedede;
  border: 2px solid #ebebeb;
  margin: 5px 5px 5px 5px;
  border-radius: 8px;
  padding: 5px 15px 5px 15px;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
.card .title {
  font-weight: bold;
}
.card .summary {
  color: #cc8b11;
  font-weight: bold;
  font-size: 12px;
}
.card .summary .summary-item {
  margin: 0;
}
/*# sourceMappingURL=style.css.map */
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<html><head>
    <meta charset="utf-8">
    <meta name="msapplication-tap-highlight" content="no">
    <title name="Business Landscape Explorer Prototype"></title>
    <link href="bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
    <link rel="stylesheet" type="text/css" href="styles/style.css">
    <script src="d3.v3.js" charset="utf-8"></script><style type="text/css"></style>
</head>
<body>
    
    <div id="card-list" class="card-list">
        <div id="attributes" class="attribute-list" data-bind="foreach: attributes">
            <attribute-card params="value: $data"><div class="card attribute-card">                    <p class="title" data-bind="text: name">Name</p>                    <div class="summary" data-bind="foreach: summaries"></div>                </div></attribute-card>
        
            <attribute-card params="value: $data"><div class="card attribute-card">                    <p class="title" data-bind="text: name">Cost</p>                    <div class="summary" data-bind="foreach: summaries">                        <p class="summary-item" data-bind="text: $data">Average: £9 million</p>                                            <p class="summary-item" data-bind="text: $data">Total: £2,700 million</p>                    </div>                </div></attribute-card>
        
            <attribute-card params="value: $data"><div class="card attribute-card">                    <p class="title" data-bind="text: name">Start Date</p>                    <div class="summary" data-bind="foreach: summaries">                        <p class="summary-item" data-bind="text: $data">Earliest: 31st Jan 2007</p>                                            <p class="summary-item" data-bind="text: $data">Latest: 27th Nov 2019</p>                    </div>                </div></attribute-card>
        
            <attribute-card params="value: $data"><div class="card attribute-card">                    <p class="title" data-bind="text: name">Enabled</p>                    <div class="summary" data-bind="foreach: summaries">                        <p class="summary-item" data-bind="text: $data">True: 71%</p>                                            <p class="summary-item" data-bind="text: $data">False: 29%</p>                    </div>                </div></attribute-card>
        
            <attribute-card params="value: $data"><div class="card attribute-card">                    <p class="title" data-bind="text: name">Status</p>                    <div class="summary" data-bind="foreach: summaries">                        <p class="summary-item" data-bind="text: $data">Red: 11%</p>                                            <p class="summary-item" data-bind="text: $data">Amber: 36%</p>                                            <p class="summary-item" data-bind="text: $data">Green: 41%</p>                    </div>                </div></attribute-card>
        </div>
    </div>

    </body></html>
9
Ian