1

Arrow keys should move selected elements pixel by pixel in addition to moving by mouse.

I tried code below but keydown event does not occur. To reproduce, select elements usong laoos and press in arrow keys. Nothing happens. How to fix this so that arrows can used to move elements ?

Fiddle: https://jsfiddle.net/bgx8gpwc/7/

<!DOCTYPE html>
<html>

<head>
  <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">
  <style>
    .designer-panel-body {
      min-height: 1px;
      overflow: hidden;
      margin: 0;
      padding: 0;
    }
    .panel-footer {
      background-color: inherit;
    }
    .designer-panel,
    .designer-resetmargins {
      margin: 0;
      padding: 0;
    }
    .designer-verticalline,
    .designer-horizontalline,
    .designer-rectangle {
      font-size: 1pt;
      border: 1px solid #000000;
    }
    .designer-field {
      border: 1px solid lightgray;
      white-space: pre;
      overflow: hidden;
    }
    .ui-selecting {
      background-color: lightskyblue;
      color: white;
    }
    .ui-selected {
      background-color: lightskyblue;
      border-color: darkblue;
      color: white;
    }
    .designer-label {
      white-space: pre;
      /*overflow: hidden;*/
    }
    .designer-field,
    .designer-label {
      font-family: "Times New Roman";
      font-size: 10pt;
      z-index: 2;
    }
    .designer-verticalline,
    .designer-horizontalline,
    .designer-rectangle,
    .designer-field,
    .designer-image,
    .designer-label {
      position: absolute;
    }
  </style>
  <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>

  <script>
    function getpos(e) {
      return {
        X: e.pageX,
        Y: e.pageY
      };
    }

    function Rect(start, stop) {
      this.left = Math.min(start.X, stop.X);
      this.top = Math.min(start.Y, stop.Y);
      this.width = Math.abs(stop.X - start.X);
      this.height = Math.abs(stop.Y - start.Y);
    }

    $(function() {
      var startpos;
      var selected = $([]),
        offset = {
          top: 0,
          left: 0
        };
      $(".designer-verticalline, .designer-rectangle, .designer-field, .designer-image").resizable();

      // http://stackoverflow.com/questions/705250/is-there-a-jquery-plugin-which-combines-draggable-and-selectable#8643716
      // teha: seal on ka mousedown mis andis viga, kaseda kasutada
      var $liigutatavad = $(".designer-verticalline, .designer-horizontalline, .designer-rectangle, .designer-field, .designer-image, .designer-label");
      $liigutatavad.draggable({
        start: function(event, ui) {
          var $this = $(this);

          if ($this.hasClass("ui-selected")) {
            // if this is selected, attach current offset
            // of each selected element to that element
            selected = $(".ui-selected").each(function() {
              var el = $(this);
              el.data("offset", el.offset());
            });
          } else {
            // if this is not selected, clear current selection
            selected = $([]);
            $liigutatavad.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
            });
          });

          // this does not fix the issue:
          //$(".designer-verticalline, .designer-rectangle, .designer-field, .designer-image").resizable();
        }
      });

      // ...but manually implement selection to prevent interference from draggable()
      $(".designer-panel-body").on("click", "div", function(e) {
        if ( /*!e.metaKey &&*/ !e.shiftKey && !e.ctrlKey) {
          // deselect other elements if meta/shift not held down
          $(".designer-panel-body").removeClass("ui-selected");
          $(this).addClass("ui-selected");
        } else {
          if ($(this).hasClass("ui-selected")) {
            $(this).removeClass("ui-selected");
          } else {
            $(this).addClass("ui-selected");
          }
        }

        //var selectable = $("#container").data("selectable");
        //selectable.refresh();
        //$( ".designer-panel-body" ).data("selectable")._mouseStop(null);
      });

      $(".designer-panel-body").selectable({});


$(".designer-panel-body").keydown(function(e) {
switch(e.which) {
    case 37: // left
       $(".ui-selected").each(function() {
         var $this = $(this);
            $this.css({
              left: $this.left -2
            });
          });
        break;

    case 38: // up
       $(".ui-selected").each(function() {
         var $this = $(this);
            $this.css({
              top: $this.top -2
            });
          });
    break;

    case 39: // right
       $(".ui-selected").each(function() {
         var $this = $(this);
            $this.css({
              left: $this.left +2
            });
          });
    break;

    case 40: // down
        $(".ui-selected").each(function() {
         var $this = $(this);
            $this.css({
              top: $this.top +2
            });
          });
    break;

    default: return; // exit this handler for other keys
}
e.preventDefault(); // prevent the default action (scroll / move caret)
});


    });
  </script>

</head>

<body>
  <div class='panel designer-panel'>
    <div class='panel-body designer-panel-body panel-warning' style='height:9.37cm'>

      <div class='designer-field' contenteditable='true' style='top:2.30cm;left:5.84cm;width:10.24cm;height:0.63cm;font-family:Arial;font-size:14pt;font-weight:bold;'>vnimi+&#39; &#39;+dok.tasudok</div>
      <div class='designer-field' contenteditable='true' style='top:2.30cm;left:16.37cm;width:2.68cm;height:0.61cm;font-size:14pt;'>DOK.kuupaev</div>
      <div class='rectangle' style='border-width: 1px;background-color:#FFFFFF;top:2.99cm;left:1.34cm;width:18.05cm;height:5.29cm'></div>
      <div class='designer-field' contenteditable='true' style='top:3.01cm;left:1.53cm;width:9.71cm;height:0.55cm;font-size:12pt;'>m.FIRMA</div>
      <div class='designer-field' contenteditable='true' style='top:3.01cm;left:12.13cm;width:3.13cm;height:0.53cm;font-size:12pt;'>ise.telefon</div>
      <div class='designer-field' contenteditable='true' style='top:3.01cm;left:17.11cm;width:1.89cm;height:0.55cm;font-size:12pt;text-align:right;'>ise.regnr</div>
      <div class='designer-label' contenteditable='true' style='top:3.04cm;left:11.39cm;text-align:right;font-size:12pt;'>Tel.</div>
      <div class='designer-label' contenteditable='true' style='top:3.04cm;left:15.71cm;font-size:12pt;'>Reg.Nr</div>
      <div class='designer-field' contenteditable='true' style='top:3.62cm;left:1.55cm;width:9.45cm;height:0.55cm;font-size:12pt;'>ise.tanav</div>
      <div class='designer-field' contenteditable='true' style='top:3.70cm;left:15.16cm;width:3.37cm;height:0.55cm;font-size:12pt;'>ise.vatpayno</div>
      <div class='designer-label' contenteditable='true' style='top:3.72cm;left:12.89cm;text-align:right;font-size:12pt;'>KMKR nr</div>
      <div class='designer-field' contenteditable='true' style='top:4.30cm;left:1.58cm;width:9.08cm;height:0.55cm;font-size:12pt;'>rtri(ise.postiindek)+&#39; &#39;+rtri(ise.piirkond)</div>
      <div class='designer-field' contenteditable='true' style='top:4.30cm;left:14.66cm;width:4.34cm;height:0.55cm;font-size:12pt;text-align:right;'>aarve(dok.arvekonto, &#39;konto.arveldusar&#39;)</div>
      <div class='designer-label' contenteditable='true' style='top:4.33cm;left:13.89cm;font-size:12pt;'>A/A</div>
      <div class='designer-horizontalline' style='border-width: 1px;top:4.96cm;left:1.34cm;width:18.03cm;height:0.00cm'></div>
      <div class='designer-field' contenteditable='true' style='top:5.04cm;left:17.13cm;width:1.89cm;height:0.55cm;font-size:12pt;text-align:right;'>klient.regnr</div>
      <div class='designer-field' contenteditable='true' style='top:5.06cm;left:4.18cm;width:12.71cm;height:0.55cm;font-size:12pt;'>klient.nimi</div>
      <div class='designer-label' contenteditable='true' style='top:5.06cm;left:15.74cm;font-size:12pt;'>Reg.Nr</div>
      <div class='designer-label' contenteditable='true' style='top:5.09cm;left:1.63cm;font-size:12pt;'>Maksja</div>
      <div class='designer-field' contenteditable='true' style='top:5.72cm;left:1.53cm;width:11.68cm;height:0.55cm;font-size:12pt;'>klient.tanav</div>
      <div class='designer-field' contenteditable='true' style='top:5.72cm;left:15.18cm;width:3.37cm;height:0.55cm;font-size:12pt;'>klient.vatpayno</div>
      <div class='designer-label' contenteditable='true' style='top:5.75cm;left:12.92cm;text-align:right;font-size:12pt;'>KMKR nr</div>
      <div class='designer-field' contenteditable='true' style='top:6.38cm;left:1.53cm;width:11.84cm;height:0.55cm;font-size:12pt;'>rtri(klient.postiindek)+&#39; &#39; +rtri(klient.piirkond)</div>
      <div class='designer-field' contenteditable='true' style='top:6.38cm;left:13.47cm;width:3.37cm;height:0.55cm;font-size:12pt;'>sql(&quot;sele transfld(&#39;nimetus&#39;, &#39;riik&#39;, rapopref()) from riik where kood=klient.riik2&quot;, &#39;&#39; )</div>
      <div class='designer-field' contenteditable='true' style='top:6.99cm;left:3.71cm;width:12.16cm;height:1.16cm;font-size:12pt;'>klient.aadress</div>
      <div class='designer-label' contenteditable='true' style='top:7.01cm;left:1.45cm;text-align:right;font-size:12pt;'>Postiaadress</div>
      <div class='designer-field' contenteditable='true' style='top:8.33cm;left:3.95cm;width:2.11cm;height:0.55cm;font-size:12pt;'>dok.tasukuup</div>
      <div class='designer-field' contenteditable='true' style='top:8.33cm;left:6.08cm;width:8.05cm;height:0.55cm;font-size:12pt;'>eval( &#39;maksetin.&#39; +left(rapopref()+&#39;tingimus&#39;,10))</div>
      <div class='designer-label' contenteditable='true' style='top:8.35cm;left:1.45cm;font-size:12pt;'>Makset&#228;htaeg</div>
      <div class='designer-field' contenteditable='true' style='top:8.91cm;left:1.45cm;width:13.66cm;height:0.45cm;'>iif(!empty(dok.saaja), IR(&quot;Saaja: &quot;)+sql(&#39;sele rtri(nimi)+&quot; &quot;+rtri(tanav)+&quot; &quot;+rtri(piirkond)+&quot; &quot;+rtri(postiindek) from klient where kood=dok.saaja&#39;,&#39;&#39;),&#39;&#39;)</div>
    </div>
    <div class='bg-warning'>
      <div class='panel-footer'><i class='glyphicon glyphicon-chevron-up'></i> GroupHeader 1: str(dokumnr)+str(koopia,2)</div>
    </div>
  </div>
</body>

</html>
Andrus
  • 26,339
  • 60
  • 204
  • 378

1 Answers1

1

If you use $this.position() for getting position, your code will work just fine.

function getpos(e) {
  return {
    X: e.pageX,
    Y: e.pageY
  };
}

function Rect(start, stop) {
  this.left = Math.min(start.X, stop.X);
  this.top = Math.min(start.Y, stop.Y);
  this.width = Math.abs(stop.X - start.X);
  this.height = Math.abs(stop.Y - start.Y);
}

$(function() {
  var startpos;
  var selected = $([]),
    offset = {
      top: 0,
      left: 0
    };
  $(".designer-verticalline, .designer-rectangle, .designer-field, .designer-image").resizable();

  // http://stackoverflow.com/questions/705250/is-there-a-jquery-plugin-which-combines-draggable-and-selectable#8643716
  // teha: seal on ka mousedown mis andis viga, kaseda kasutada
  var $liigutatavad = $(".designer-verticalline, .designer-horizontalline, .designer-rectangle, .designer-field, .designer-image, .designer-label");
  $liigutatavad.draggable({
    start: function(event, ui) {
      var $this = $(this);

      if ($this.hasClass("ui-selected")) {
        // if this is selected, attach current offset
        // of each selected element to that element
        selected = $(".ui-selected").each(function() {
          var el = $(this);
          el.data("offset", el.offset());
        });
      } else {
        // if this is not selected, clear current selection
        selected = $([]);
        $liigutatavad.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
        });
      });

      // this does not fix the issue:
      //$(".designer-verticalline, .designer-rectangle, .designer-field, .designer-image").resizable();
    }
  });

  // ...but manually implement selection to prevent interference from draggable()
  $(".designer-panel-body").on("click", "div", function(e) {
    if ( /*!e.metaKey &&*/ !e.shiftKey && !e.ctrlKey) {
      // deselect other elements if meta/shift not held down
      $(".designer-panel-body").removeClass("ui-selected");
      $(this).addClass("ui-selected");
    } else {
      if ($(this).hasClass("ui-selected")) {
        $(this).removeClass("ui-selected");
      } else {
        $(this).addClass("ui-selected");
      }
    }

    //var selectable = $("#container").data("selectable");
    //selectable.refresh();
    //$( ".designer-panel-body" ).data("selectable")._mouseStop(null);
  });

  $(".designer-panel-body").selectable({
      selected : function() {
          $(".ui-selected").first().focus();
      }
  });


  $(".designer-panel-body").keydown(function(e) {
    switch (e.which) {
      case 37: // left
        $(".ui-selected").each(function() {
          var $this = $(this);
          var $position = $this.position();
          $this.css({
            left: $position.left - 2
          });
        });
        break;

      case 38: // up
        $(".ui-selected").each(function() {
          var $this = $(this);
          var $position = $this.position();
          $this.css({
            top: $position.top - 2
          });
        });
        break;

      case 39: // right
        $(".ui-selected").each(function() {
          var $this = $(this);
          var $position = $this.position();
          $this.css({
            left: $position.left + 2
          });
        });
        break;

      case 40: // down
        $(".ui-selected").each(function() {
          var $this = $(this);
          var $position = $this.position();
          $this.css({
            top: $position.top + 2
          });
        });
        break;

      default:
        return; // exit this handler for other keys
    }
    e.preventDefault(); // prevent the default action (scroll / move caret)
  });


});
.designer-panel-body {
  min-height: 1px;
  overflow: hidden;
  margin: 0;
  padding: 0;
}
.panel-footer {
  background-color: inherit;
}
.designer-panel,
.designer-resetmargins {
  margin: 0;
  padding: 0;
}
.designer-verticalline,
.designer-horizontalline,
.designer-rectangle {
  font-size: 1pt;
  border: 1px solid #000000;
}
.designer-field {
  border: 1px solid lightgray;
  white-space: pre;
  overflow: hidden;
}
.ui-selecting {
  background-color: lightskyblue;
  color: white;
}
.ui-selected {
  background-color: lightskyblue;
  border-color: darkblue;
  color: white;
}
.designer-label {
  white-space: pre;
  /*overflow: hidden;*/
}
.designer-field,
.designer-label {
  font-family: "Times New Roman";
  font-size: 10pt;
  z-index: 2;
}
.designer-verticalline,
.designer-horizontalline,
.designer-rectangle,
.designer-field,
.designer-image,
.designer-label {
  position: absolute;
}
<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>
<div class='panel designer-panel'>
  <div class='panel-body designer-panel-body panel-warning' style='height:9.37cm'>

    <div class='designer-field' contenteditable='true' style='top:2.30cm;left:5.84cm;width:10.24cm;height:0.63cm;font-family:Arial;font-size:14pt;font-weight:bold;'>vnimi+&#39; &#39;+dok.tasudok</div>
    <div class='designer-field' contenteditable='true' style='top:2.30cm;left:16.37cm;width:2.68cm;height:0.61cm;font-size:14pt;'>DOK.kuupaev</div>
    <div class='rectangle' style='border-width: 1px;background-color:#FFFFFF;top:2.99cm;left:1.34cm;width:18.05cm;height:5.29cm'></div>
    <div class='designer-field' contenteditable='true' style='top:3.01cm;left:1.53cm;width:9.71cm;height:0.55cm;font-size:12pt;'>m.FIRMA</div>
    <div class='designer-field' contenteditable='true' style='top:3.01cm;left:12.13cm;width:3.13cm;height:0.53cm;font-size:12pt;'>ise.telefon</div>
    <div class='designer-field' contenteditable='true' style='top:3.01cm;left:17.11cm;width:1.89cm;height:0.55cm;font-size:12pt;text-align:right;'>ise.regnr</div>
    <div class='designer-label' contenteditable='true' style='top:3.04cm;left:11.39cm;text-align:right;font-size:12pt;'>Tel.</div>
    <div class='designer-label' contenteditable='true' style='top:3.04cm;left:15.71cm;font-size:12pt;'>Reg.Nr</div>
    <div class='designer-field' contenteditable='true' style='top:3.62cm;left:1.55cm;width:9.45cm;height:0.55cm;font-size:12pt;'>ise.tanav</div>
    <div class='designer-field' contenteditable='true' style='top:3.70cm;left:15.16cm;width:3.37cm;height:0.55cm;font-size:12pt;'>ise.vatpayno</div>
    <div class='designer-label' contenteditable='true' style='top:3.72cm;left:12.89cm;text-align:right;font-size:12pt;'>KMKR nr</div>
    <div class='designer-field' contenteditable='true' style='top:4.30cm;left:1.58cm;width:9.08cm;height:0.55cm;font-size:12pt;'>rtri(ise.postiindek)+&#39; &#39;+rtri(ise.piirkond)</div>
    <div class='designer-field' contenteditable='true' style='top:4.30cm;left:14.66cm;width:4.34cm;height:0.55cm;font-size:12pt;text-align:right;'>aarve(dok.arvekonto, &#39;konto.arveldusar&#39;)</div>
    <div class='designer-label' contenteditable='true' style='top:4.33cm;left:13.89cm;font-size:12pt;'>A/A</div>
    <div class='designer-horizontalline' style='border-width: 1px;top:4.96cm;left:1.34cm;width:18.03cm;height:0.00cm'></div>
    <div class='designer-field' contenteditable='true' style='top:5.04cm;left:17.13cm;width:1.89cm;height:0.55cm;font-size:12pt;text-align:right;'>klient.regnr</div>
    <div class='designer-field' contenteditable='true' style='top:5.06cm;left:4.18cm;width:12.71cm;height:0.55cm;font-size:12pt;'>klient.nimi</div>
    <div class='designer-label' contenteditable='true' style='top:5.06cm;left:15.74cm;font-size:12pt;'>Reg.Nr</div>
    <div class='designer-label' contenteditable='true' style='top:5.09cm;left:1.63cm;font-size:12pt;'>Maksja</div>
    <div class='designer-field' contenteditable='true' style='top:5.72cm;left:1.53cm;width:11.68cm;height:0.55cm;font-size:12pt;'>klient.tanav</div>
    <div class='designer-field' contenteditable='true' style='top:5.72cm;left:15.18cm;width:3.37cm;height:0.55cm;font-size:12pt;'>klient.vatpayno</div>
    <div class='designer-label' contenteditable='true' style='top:5.75cm;left:12.92cm;text-align:right;font-size:12pt;'>KMKR nr</div>
    <div class='designer-field' contenteditable='true' style='top:6.38cm;left:1.53cm;width:11.84cm;height:0.55cm;font-size:12pt;'>rtri(klient.postiindek)+&#39; &#39; +rtri(klient.piirkond)</div>
    <div class='designer-field' contenteditable='true' style='top:6.38cm;left:13.47cm;width:3.37cm;height:0.55cm;font-size:12pt;'>sql(&quot;sele transfld(&#39;nimetus&#39;, &#39;riik&#39;, rapopref()) from riik where kood=klient.riik2&quot;, &#39;&#39; )</div>
    <div class='designer-field' contenteditable='true' style='top:6.99cm;left:3.71cm;width:12.16cm;height:1.16cm;font-size:12pt;'>klient.aadress</div>
    <div class='designer-label' contenteditable='true' style='top:7.01cm;left:1.45cm;text-align:right;font-size:12pt;'>Postiaadress</div>
    <div class='designer-field' contenteditable='true' style='top:8.33cm;left:3.95cm;width:2.11cm;height:0.55cm;font-size:12pt;'>dok.tasukuup</div>
    <div class='designer-field' contenteditable='true' style='top:8.33cm;left:6.08cm;width:8.05cm;height:0.55cm;font-size:12pt;'>eval( &#39;maksetin.&#39; +left(rapopref()+&#39;tingimus&#39;,10))</div>
    <div class='designer-label' contenteditable='true' style='top:8.35cm;left:1.45cm;font-size:12pt;'>Makset&#228;htaeg</div>
    <div class='designer-field' contenteditable='true' style='top:8.91cm;left:1.45cm;width:13.66cm;height:0.45cm;'>iif(!empty(dok.saaja), IR(&quot;Saaja: &quot;)+sql(&#39;sele rtri(nimi)+&quot; &quot;+rtri(tanav)+&quot; &quot;+rtri(piirkond)+&quot; &quot;+rtri(postiindek) from klient where kood=dok.saaja&#39;,&#39;&#39;),&#39;&#39;)</div>
  </div>
  <div class='bg-warning'>
    <div class='panel-footer'><i class='glyphicon glyphicon-chevron-up'></i> GroupHeader 1: str(dokumnr)+str(koopia,2)</div>
  </div>
</div>
rrk
  • 15,677
  • 4
  • 29
  • 45
  • In first time after load, after elements are selected using lasso, mouse click in selected element is requied to make arrow kayes to work. How to fix this so that arrow keys work immediately? Should lasso selection put focus to `designer-panel-body` or to selected div inside it ? – Andrus Jan 10 '16 at 11:27
  • 1
    @Andrus For that you can add this to the `selectable` call. `selected : function(){ $(".ui-selected").first().focus(); }` – rrk Jan 10 '16 at 12:27
  • Thank you. It worked. This code has also serious issue. Resizable is lost on move. Resizable icon in lower right corner remain in old place and does not move with element. It is posted it http://stackoverflow.com/questions/34698117/elements-became-randomly-non-resizable-after-dragging What do you hink about this. Is it possible to fix this is some way? – Andrus Jan 10 '16 at 13:56