Andrus Andrus - 1 month ago 10
Javascript Question

How to force .prepend() and .append() to add DOM objects, not text

In some cases jquery

.prepend()
and
.append()
insert text instead of DOM objects.
To reproduce, run code snippet and press
Add group
link in detail band.
Inserted html appears as text.

Instead of this, bootsrtap panels should appear.

It looks like jQuery prepend and append does not add DOM objects work for unknown reason.

How to fix this so that new panels appear before and after detail panel ?



$(function() {

var startpos,
selected = $([]),
offset = {
top: 0,
left: 0
};

$('#designer-detail-addband').on('click', function() {
var $this = $(this),
$parentpanel = $this.parents(".designer-panel").get(0);

$parentpanel.prepend('<div class="panel designer-panel">' +
'<div class="panel-body designer-panel-body" style="height:1px">' +
'</div>' +
'<div class="bg-warning">' +
'<div class="panel-footer"><i class="glyphicon glyphicon-chevron-up">' +
'</i> Group heade {0}: <span id="band{0}_expr" contenteditable="true">"groupexpression"</span>' +
'</div></div></div>');

$parentpanel.append('<div class="panel designer-panel">' +
'<div class="panel-body designer-panel-body" style="height:1px"></div>' +
'<div class="bg-warning">' +
'<div class="panel-footer"><i class="glyphicon glyphicon-chevron-up">' +
'</i> Group footer {0}' +
'</div></div></div>');

});






$(".designer-panel-body").droppable({
accept: ".designer-field"
});

$(".designer-field").draggable({
start: function(event, ui) {
var $this = $(this);
if ($this.hasClass("ui-selected")) {
selected = $(".ui-selected").each(function() {
var el = $(this);
el.data("offset", el.offset());
});
} else {
selected = $([]);
$(".designer-field").removeClass("ui-selected");
}
offset = $this.offset();
},

drag: function(event, ui) {
// drag all selected elements simultaneously
var dt = ui.position.top - offset.top,
dl = ui.position.left - offset.left;
selected.not(this).each(function() {
var $this = $(this);
var elOffset = $this.data("offset");
$this.css({
top: elOffset.top + dt,
left: elOffset.left + dl
});
});
}
});



$(".panel-resizable").resizable({
minWidth: "100%",
maxWidth: "100%",
minHeight: 1
});
});

.panel-resizable {
min-height: 1px;
overflow: hidden;
margin: 0;
padding: 0;
}
.designer-field {
border: 1px solid lightgray;
white-space: pre;
overflow: hidden;
position: absolute;
}
.designer-panel-body {
min-height: 1px;
overflow: hidden;
margin: 0;
padding: 0;
}
.panel-footer {
padding: 0;
}
.designer-panel,
.panel-body {
margin: 0;
padding: 0;
}

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<link rel="stylesheet" href="http://code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css">
<script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="http://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
</head>

<body>
<div class='panel designer-panel'>
<div class='panel-body designer-panel-body panel-resizable' style='height:2cm'>

<div class='designer-field' style='left:5px;top:6px;width:180px'>field 1 in group 1 header</div>

<div class='designer-field' style='left:54px;top :36px;width:160px'>field 2 in group 1 header</div>
</div>

<div class='panel-footer'>Group 1 Header</div>
</div>

<div class='panel designer-panel'>
<div class='panel-body panel-resizable' style='height:1cm'>
<div class='designer-field' style='left:24px;top:2px;width:140px'>field in detail group</div>
</div>
<div class='panel-footer panel-primary'>Detail <a role="button" id="designer-detail-addband"> Add group</a>
</div>
</div>

<div class='panel'>
<div class='panel-body panel-resizable' style='height:1cm'>

<div class='designer-field' style='left:44px;top:2px;width:140px'>field in group 1 footer</div>
</div>
<div class='panel-footer panel-warning'>Group 1 Footer</div>
</div>




Answer

.get returns a HTMLElement. That is why, its appending string.

If you wish to select first element, you should use :first selector.

$parentpanel = $this.parents(".designer-panel:first");

$(function() {

  var startpos,
    selected = $([]),
    offset = {
      top: 0,
      left: 0
    };

  $('#designer-detail-addband').on('click', function() {
    var $this = $(this),
      $parentpanel = $this.parents(".designer-panel:first");

    $parentpanel.prepend('<div class="panel designer-panel">' +
      '<div class="panel-body designer-panel-body" style="height:1px">' +
      '</div>' +
      '<div class="bg-warning">' +
      '<div class="panel-footer"><i class="glyphicon glyphicon-chevron-up">' +
      '</i> Group heade {0}: <span id="band{0}_expr" contenteditable="true">"groupexpression"</span>' +
      '</div></div></div>');

    $parentpanel.append('<div class="panel designer-panel">' +
      '<div class="panel-body designer-panel-body" style="height:1px"></div>' +
      '<div class="bg-warning">' +
      '<div class="panel-footer"><i class="glyphicon glyphicon-chevron-up">' +
      '</i> Group footer {0}' +
      '</div></div></div>');

  });

  $(".designer-panel-body").droppable({
    accept: ".designer-field"
  });

  $(".designer-field").draggable({
    start: function(event, ui) {
      var $this = $(this);
      if ($this.hasClass("ui-selected")) {
        selected = $(".ui-selected").each(function() {
          var el = $(this);
          el.data("offset", el.offset());
        });
      } else {
        selected = $([]);
        $(".designer-field").removeClass("ui-selected");
      }
      offset = $this.offset();
    },

    drag: function(event, ui) {
      // drag all selected elements simultaneously
      var dt = ui.position.top - offset.top,
        dl = ui.position.left - offset.left;
      selected.not(this).each(function() {
        var $this = $(this);
        var elOffset = $this.data("offset");
        $this.css({
          top: elOffset.top + dt,
          left: elOffset.left + dl
        });
      });
    }
  });

  $(".panel-resizable").resizable({
    minWidth: "100%",
    maxWidth: "100%",
    minHeight: 1
  });
})
.panel-resizable {
  min-height: 1px;
  overflow: hidden;
  margin: 0;
  padding: 0;
}
.designer-field {
  border: 1px solid lightgray;
  white-space: pre;
  overflow: hidden;
  position: absolute;
}
.designer-panel-body {
  min-height: 1px;
  overflow: hidden;
  margin: 0;
  padding: 0;
}
.panel-footer {
  padding: 0;
}
.designer-panel,
.panel-body {
  margin: 0;
  padding: 0;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<link rel="stylesheet" href="http://code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css">
<script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="http://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
</head>

<body>
  <div class='panel designer-panel'>
    <div class='panel-body designer-panel-body panel-resizable' style='height:2cm'>

      <div class='designer-field' style='left:5px;top:6px;width:180px'>field 1 in group 1 header</div>

      <div class='designer-field' style='left:54px;top :36px;width:160px'>field 2 in group 1 header</div>
    </div>

    <div class='panel-footer'>Group 1 Header</div>
  </div>

  <div class='panel designer-panel'>
    <div class='panel-body panel-resizable' style='height:1cm'>
      <div class='designer-field' style='left:24px;top:2px;width:140px'>field in detail group</div>
    </div>
    <div class='panel-footer panel-primary'>Detail <a role="button" id="designer-detail-addband"> Add group</a>
    </div>
  </div>

  <div class='panel'>
    <div class='panel-body panel-resizable' style='height:1cm'>

      <div class='designer-field' style='left:44px;top:2px;width:140px'>field in group 1 footer</div>
    </div>
    <div class='panel-footer panel-warning'>Group 1 Footer</div>
  </div>

It uses ParentNode.prepend instead. Reference