Drag & Drop

Rich user interfaces can benefit from supporting drag & drop behavior. Think of a calendar where the user can drag an appointment to a different date, or a lane-based project management tool where users can drag their task from the ToDo lane to the Done lane. Sircl provides support for drag & drop by linking drop operations to move or copy page elements, to call server actions, or to upload files.

Drag & Drop page elements

Introduction

Page elements can represent business concepts of your application. Think of a DIV element representing an appointment on a calendar.  Rich user interfaces can offer enhanced user experience bu supporting dragging and dropping of such elements.

Note that Drag & Drop page elements is not supported under Internet Explorer.

Defining drag & drop matches

To use drag & drop on page elements, both draggable elements and drop zones must comply to certain conditions.

First of all, draggable elements must have a draggable attribute with value "true".

Next, to determine on which drop zone a draggable element can be dropped, the draggable element must have a drop-type attribute telling the type(s) of the element, and the drop zone must have an ondrop-accept attribute telling which draggable type(s) it accepts.

Both attributes can have multiple values separated by spaces and at least one must match for the drop operation to be accepted.

All draggable elements also automatically get the "any" type assigned.

So in the minimal setup for a drag & drop to be allowed, the draggable elements must have a draggable attribute with value "true" and the drop zones must have an ondrop-accept attribute with value "any":

<div draggable="true">
  Drag me
</div>

<div ondrop-accept="any">
  Drop me here
</div>

However, more complex setups are possible, as in the following:

<div draggable="true" drop-type="customer"> Caesar </div>
<div draggable="true" drop-type="supplier"> Sophia </div>
<div draggable="true" drop-type="customer supplier"> Bernice </div>
<div draggable="true" drop-type="partner"> Peter </div>

<div ondrop-accept="customer">
  Drop customers here
</div>

<div ondrop-accept="supplier">
  Drop suppliers here
</div>

<div ondrop-accept="customer supplier">
  Drop customers or suppliers here
</div>

Here, for instance, Bernice is defined as both customer and supplier, and can be dropped in any of the drop zones. While Caesar is a customer, and can only be dropped in the first or the last drop zone, as the last one accepts both customers and suppliers. Peter is a partner and cannot be dropped on any of the drop zones.

Serverless drop handling

Drop operations can be handled without intervention of the server (without a serverside call) using the drop zone classes ondrop-move and ondrop-copy.

The ondrop-move class will cause the dragged element to be moved (appended) to the drop zone, while the ondrop-copy class will cause a copy of the dragged element to be appended to the drop zone.

<h2 draggable="true">
  Put me in a box
</h2>

<div class="ondrop-move" ondrop-accept="any" style="border: solid 4px blue; min-height: 100px;">
</div>

No server call is envolved, hence, the server will only be aware of the drag & drop operations once, for instance, the surrounding form is submitted. In addition, the server will have to figure out by itself if and so which drag & drop operations took place. It could do so by analyzing the set and order of form fields received.

Server drop handling

Alternatively, Sircl also supports server side handling of the drop operation. This is achieved by setting the ondrop-submit class on the drop zone, which will make the form holding the drop zone to be submitted.

For the server to be able to handle the drop operation correctly, we need to provide the server with information about the dropped element and the drop zone (especially if there is more than one drop zone possible). To inform the server about the drop operation and the drop zone, each drop zone can have a distinct formaction attribute. That attribute holds the URL to which the form is submitted and can for instance contain additional query parameters to identify the drop zone.

To identify the dropped element draggable items must have a drop-value attribute with as value the value used to identify them (an id, name,...). Then, two solutions are possible. The first: add a special "{drop-value}" substitution field in the URL. This field will then be replaced by the value of the drop-value attribute of the dropped element. For instance:

<form>
  <h2 draggable="true" drop-type="vehicle" drop-value="bike">Bike</h2>
  <h2 draggable="true" drop-type="vehicle" drop-value="car">Car</h2>
  <div class="ondrop-submit"
       ondrop-accept="vehicle"
       formaction="/Dropped?Vehicle={drop-value}&Zone=favorite">
    Drop your favorite vehicle here
  </div>
</form>

Dropping the bike on the drop zone, will submit the form with the following action URL:

GET /Dropped?Vehicle=bike&Zone=favorite

The second solution does not customize the submit URL, but uses a form input control which must have the class drop-value and will receive the value of the drop-value attribute of the dropped element just before submitting the form to the server. The server will then receive the value as part of the submitted form fields.

The server can now be informed that a drop operation was done by either the formaction, or by the fact the input with class drop-value has a value. It can use that value to determine the dropped element. And it can use the formaction to identify the drop zone:

<form>
  <input class="drop-value" type="hidden" name="Vehicle" />
  <h2 draggable="true" drop-type="vehicle" drop-value="bike">Bike</h2>
  <h2 draggable="true" drop-type="vehicle" drop-value="car">Car</h2>
  <div class="ondrop-submit"
       ondrop-accept="vehicle"
       formaction="/Dropped?Zone=favorite">
    Drop your favorite vehicle here
  </div>
</form>

When dropping the bike in the favorite zone, the value of the drop-value attribute of the bike is copied to the input element named Vehicle, because it has the drop-value class. Then the surrounding form is submitted using the action URL defined in the formaction attribute of the drop zone. The submit causes the following URL to be requested to the server, allowing it to identify both the drop zone and the dropped verhicle:

GET /Dropped?Zone=favorite&Vehicle=bike

Visualizing valid drop zones

For a better visualization of drop zones, you can add an ondragover-addclass attribute to the drop zone element with as value a class name to add to the drop zone element when a draggable element is dragged over the drop zone (and drop is allowed). This attribute also takes care of removing the class when the dragged element leaves the drop zone or when dragging is ended.

<style>
  .drop-zone {
    background-color: lightgreen;
  }
  .drop-zone * {
    pointer-events: none;
  }
</style>

<div draggable="true" drop-type="customer"> Caesar </div>

<div ondrop-accept="customer" ondragover-addclass="drop-zone">
  Drop customers here
</div>

We have defined two styles here. The second, for all children of the drop-zone, setting pointer-events to none, is required to avoid children of the drop zone to fire dragleave events that would remove the class. Some browsers may fail to support this or may require additional settings. See https://stackoverflow.com/a/18582960 for more information.

Drag & Drop files

Dropping files into the application is a behaviour often expected. Sircl offers support for dropping files and setting a file INPUT element to contain the dropped files.

Defining a file drop zone

To mark a file drop zone, we use the ondropfile-set Event-Action attribute. The attribute value selects the form INPUT element to set the file in:

<form method="post" enctype="multipart/form-data">
  <div ondropfile-set="#fileinp">
    <p>Drop a file here...</p>
  </div>
  <input id="fileinp" type="file" name="file">
</form>

If the drop zone element contains the file INPUT element, you can also use the ondropfile-set class:

<form method="post" enctype="multipart/form-data">
  <div class="ondropfile-set">
    <p>Drop one or more files here...</p>
    <input type="file" name="files" multiple>
  </div>
</form>

The file type INPUT element can have accept and multiple attributes to indicate which file types are to be accepted and whether multiple files are supported.

In addition, following Sircl specific attributes to further limit or control file uploads. These attributes work when using file drag & drop as well as with direct interaction with the file INPUT elements:

  • accept-alert : the alert text to show if a file of wrong type is selected.
  • maxcount : the maximum number of files (multiple attribute must be set).
  • maxcount-alert : the alert text to show if the maximum number of files is exceeded.
  • maxsize : the maximum size of each individual file. Either expressed in bytes, or postfixed with "KB" or "MB". I.e: "4.5 MB".
  • maxsize-alert : the alert text to show if the maximum size is exceeded for one of the files.
  • maxtotalsize : the maximum size of all file together. Either expressed in bytes, or postfixed with "KB" or "MB". I.e: "12 MB".
  • maxtotalsize-alert : the alert text to show if the maximum total size is exceeded.
  • onchange-setname : when a file is chosen, sets the name of the (first) file to the selected element(s).
    If the selected element is an INPUT element, it's value is set, otherwise it's inner text is set.
  • onchange-setbasename : when a file is chosen, sets the base name of the (first) file (file name without extension) to the selected element(s).
    If the selected element is an INPUT element, it's value is set, otherwise it's inner text is set.

Uploading files

The ondropdile-set attribute or class sets the value of a file INPUT element. The files will be uploaded whenever the containig form is posted.

By setting an onchange-submit class on the file INPUT element, the form will be submitted as soon as the files are dropped (or files have manually been selected using the file INPUT element).

The following uploads a file as soon as it is dropped. It then returns new content for the DIV element which could contain a preview of the image and hidden fields with data to identify the uploaded file when submittig the whole form through the OK button:

<form>
  <input type="text" name="Name" placeholder="Enter name" />
  <div id="picture" class="ondropfile-set">
    <p>Drop your picture here</p>
    <input class="onchange-submit" type="file" name="picture"
        accept="image/*"
        accept-alert="Only images are accepted."
        maxsize="4 MB"
        maxsize-alert="Please upload a file smaller than 4 MB."
        maxcount-alert="Only a single file is accepted."
        formaction="/UploadHandler"
        formmethod="post"
        formenctype="multipart/form-data"
        formtarget="#picture"
        hidden>
    </div>
  <button type="submit">OK</button>
</form>

The file INPUT element has the class onchange-submit. When it's value is changed (by selecting or dropping an image), the form is submitted. In this case, the INPUT element also overrides the default form method, action, encoding type and target using the formaction, formmethod, formenctype and formtarget attributes.

Visualizing file drop zones

Indentical to page element drop zones, file drop zones can benefit from a better visualization by adding an ondragover-addclass attribute to the drop zone element with as value a class name to add to the drop zone element when files are dragged over the drop zone. This attribute also takes care of removing the class when the files leaves the drop zone or when dragging is ended.

<form method="post" enctype="multipart/form-data">
  <div class="ondropfile-set" ondragover-addclass="highlighted">
    <p>Drop one or more files here...</p>
    <input type="file" name="files" multiple>
  </div>
</form>

Disable file dropping

By default, when a file is dropped outside a file dropping zone, the browser will render or download the file.

You can disable this behaviour by setting the ondropfile-ignore class on the elements that should not accept file drops.

You typically set the ondropfile-ignore class on the BODY element. Nested elements with ondropfile-set will still be operational.

<body class="ondropfile-ignore">
  ...
</body>

 

Loading...