getBoundingClientRect gibt falsche Ergebnisse zurück

Lesezeit: 12 Minuten

Ich kämpfe ein wenig damit, die aktuelle Position und Größe eines Elements innerhalb des DOM zu bestimmen. Ich habe ein Fragment zusammengestellt, um ein kartenbasiertes System auf der rechten Seite des Bildschirms zu veranschaulichen.

Das Verhalten, das ich zu erstellen versuche, ist, dass, wenn Sie auf eine dieser Karten klicken, eine weitere Karte hinzugefügt wird (letztendlich darunter, aber vorerst oben), die in die obere linke Ecke des Bildschirms fliegt, bevor sie gefüllt wird verfügbarer Platz.

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="https://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>

Was ich tue, ist ziemlich einfach, greife das angeklickte Element, messe sein Begrenzungsrechteck und füge ein neues Element hinzu body mit gleicher Größe und Position:

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");
});

Ich habe darüber gelesen getBoundingClientRect() und es scheint zu tun, was ich gemäß der Spezifikation will, es tut einfach nicht das, was ich hier erwarte, da die Breite/Höhe alle ausgeschaltet sind und Firefox nicht einmal die linke Seite richtig einstellen kann. Ist diese Funktion einfach kaputt (was mich überraschen würde) oder macht ein Teil meines CSS diese native Funktion irgendwie kaputt?

Ich sollte hier einen Screenshot der Ergebnisse hinzufügen, die in verschiedenen Browsern ausgeschaltet sind. IE ist bei weitem am nächsten, scheint aber immer noch mit den unteren/rechten Werten zu kämpfen.

Geben Sie hier die Bildbeschreibung ein

  • getBoundingClientRect() gibt die Koordinaten von zurück Elementkann es Rand (5), Rand (2) und Polsterung (15, 5) ignorieren, die auf Element gesetzt sind (Werte in Klammern sind das, was Sie in der Karte “Name” verwenden).

    – Teemu

    10. März 2015 um 14:56 Uhr


  • @Teemu: Das ist hilfreich und ich hatte das nicht gelesen – wo sind Sie auf diese Ausschlüsse gestoßen?

    – Jan

    10. März 2015 um 15:00 Uhr

  • Du meinst die Werte? Habe gerade Firebug geöffnet und das Element aus Stacksnippet ausgewählt und auf das „Layout“-Tool geklickt, um die Layout-Positionierung anzuzeigen.

    – Teemu

    10. März 2015 um 15:01 Uhr

  • @Teemu – nein, wo hast du gelesen, dass Rand, Polsterung, Rand vom Rechteck ausgeschlossen sind?

    – Jan

    10. März 2015 um 15:02 Uhr

  • @Teemu – Ich denke auch, kennen Sie eine browserübergreifende Lösung, um dies zu berechnen? Oder muss ich selbst schreiben?

    – Jan

    10. März 2015 um 15:05 Uhr

Benutzeravatar von Ian
Jan

Nun, ich bin mächtig verwirrt, habe es aber geschafft, das Ding so zum Laufen zu bringen, wie ich es wollte. Ich habe die Berechnung geändert, um Polsterung, Rand und Ränder basierend auf ein wenig Vermutung zu berücksichtigen, und einige Stile geändert, um sicherzustellen, dass alles noch funktioniert. Das brachte mir folgende Rechnung:

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;

Seltsamerweise funktionierte es überhaupt nicht, als ich versuchte, dies in meine Anwendung einzufügen. Etwas von der Polsterung herausgenommen und am Ende:

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="https://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>

  • Beachten Sie, dass einige Browser ein schreibgeschütztes Rect-Objekt zurückgeben! Ziemlich nervig, oder? 🙁

    – Oriadam

    28. April 2016 um 17:17 Uhr

  • Woher hast du den Füllwert?

    – Anders Lindén

    31. Januar 2017 um 7:55 Uhr


  • @AndersLindén gute Frage, ich kann mich nicht genau erinnern. Ich glaube, ich habe einfach verwendet d3.select(element).style("padding") zum Beispiel und analysiert dann die resultierende Zeichenfolge.

    – Jan

    31. Januar 2017 um 9:15 Uhr

Ich bin auf das gleiche Problem gestoßen, aber in meinem Fall waren die Rechtecke manchmal alle gleichmäßig um eine konstante Anzahl von Pixeln versetzt. Ich habe festgestellt, dass der Körperknoten selbst einen gewissen Versatz relativ zum Ansichtsfenster haben kann, den Sie anpassen sollten, wenn Sie ein Element an den Körper anhängen. Siehe folgenden Code:

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

   var bodyRect = document.body.getBoundingClientRect(); // Get potential offset of the page's body node
   var rect = this.getBoundingClientRect(); // This gives coordinates relative to the viewport, not relative to the body's origin
   var card = d3.select("body")
          .append("div")
            .attr("class", "card")
            .style("background", "transparent")
            .style("border", "thin solid red")
            .style("left", (rect.left - bodyRect.left) + "px") // Correct for the body's offset
            .style("top", (rect.top - bodyRect.top) + "px") // Correct for the body's offset
            .style("width", (rect.right - rect.left) + "px")
            .style("height", (rect.bottom - rect.top) + "px")
            .style("position", "absolute");
});

Sie haben erwähnt, dass das gemessene Element animiert wird (Fliegen Sie in die obere linke Ecke des Bildschirms, bevor Sie den verfügbaren Platz ausfüllen.)

Wenn Sie eine Skalierungsanimation durchführen, vergewissern Sie sich, dass Sie das Element messen, nachdem die Animation abgeschlossen ist

1441540cookie-checkgetBoundingClientRect gibt falsche Ergebnisse zurück

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy