diff --git a/doc/PROTOCOL.md b/doc/PROTOCOL.md
index 9af56bfb66b60e9aa25446301a2f5b46c7034a85..22cc5649efbd48416a4ff799be85de9db05f5223 100644
--- a/doc/PROTOCOL.md
+++ b/doc/PROTOCOL.md
@@ -133,6 +133,51 @@ having the following structure
     radio button `value`-attribute to be activated in `"value"`.
 	
 
+### Form Group Configuration Response
+
+As part of the server response, the JSON stream may contain a key
+`element-update`. This key stores information on how to modify HTML elements identified by `id`. Modifying in this 
+context refers to:
+
+ * Setting attribute values
+ * Deleting attributes
+ * Setting content of a HTML element.
+ 
+The content of `element-update` is outlined below
+
+    {
+        ...
+        "element-update" : {
+            "<element_id1>": {
+                "attr": {
+                    "<attr_name1>": "<value1>" | null,
+                    ...
+                    "<attr_nameN>": "<valueN>" | null
+                },
+                "content": "<element_content>"
+            },
+            ...
+            "<element_idN>": {
+                "attr": {
+                    "<attr_name1>": "<value1>" | null,
+                    ...
+                    "<attr_nameN>": "<valueN>" | null
+                },
+                "content": "<element_content>"
+            }
+        },
+        ...
+    }
+    
+The presence of `element-update` is optional. `<element_idN>` refers to the element's `id`-attribute value. It used 
+to uniquely identify the HTML element in the DOM. The properties `"attr"` and `"content"` are both optional.
+
+Supplying `null` as value for `"<attr_nameN>"` will remove the attribute from the HTLM element identified by 
+`"<element_idN>"`.
+
+If the element has no `"<attr_nameN>"` attribute, it will be created. In any case, the attribute's value will be set 
+to the value specified by `"<valueN>"`. See above for handling of `null` value.
+
 ### Redirection Response
 
 Depending on the request, the server may return redirection
diff --git a/javascript/src/ElementUpdate.js b/javascript/src/ElementUpdate.js
new file mode 100644
index 0000000000000000000000000000000000000000..177199fdef1c4e274e838b597fd560659ea4ca5a
--- /dev/null
+++ b/javascript/src/ElementUpdate.js
@@ -0,0 +1,90 @@
+/**
+ * @author Rafael Ostertag <rafael.ostertag@math.uzh.ch>
+ */
+
+/* global $ */
+/* global console */
+
+/* @depend Utils.js */
+
+var QfqNS = QfqNS || {};
+
+(function (n) {
+    'use strict';
+
+    /**
+     * Update HTML elements by a given id. Supports adding, setting, and removing attributes as well setting the
+     * text enclosed by the element.
+     *
+     * @type {{}}
+     */
+    n.ElementUpdate = {};
+
+
+    /**
+     * Update all elements according to configuration.
+     *
+     * @param config JSON configuration
+     * @public
+     */
+    n.ElementUpdate.updateAll = function (config) {
+        for (var idName in config) {
+            if (!config.hasOwnProperty(idName)) {
+                continue;
+            }
+
+            n.ElementUpdate.update(idName, config[idName]);
+        }
+    };
+
+    /**
+     *
+     * @param elementId id of the element to update
+     * @param config configuration
+     */
+    n.ElementUpdate.update = function (elementId, config) {
+        var $element = n.ElementUpdate.$getElementById(elementId);
+
+        if (config.attr) {
+            n.ElementUpdate.handleAttributeUpdate($element, config.attr);
+        }
+
+        if (config.content) {
+            n.ElementUpdate.setElementText($element, config.content);
+        }
+
+    };
+
+    n.ElementUpdate.$getElementById = function (id) {
+        return $("#" + n.escapeJqueryIdSelector(id));
+    };
+
+    n.ElementUpdate.handleAttributeUpdate = function ($element, attributes) {
+        var attributeValue;
+        for (var attributeName in attributes) {
+            if (!attributes.hasOwnProperty(attributeName)) {
+                continue;
+            }
+
+            attributeValue = attributes[attributeName];
+
+            if (attributeValue === null) {
+                n.ElementUpdate.deleteAttribute($element, attributeName);
+            } else {
+                n.ElementUpdate.setAttribute($element, attributeName, attributeValue);
+            }
+        }
+    };
+
+    n.ElementUpdate.setAttribute = function ($element, attributeName, attributeValue) {
+        $element.attr(attributeName, attributeValue);
+    };
+
+    n.ElementUpdate.deleteAttribute = function ($element, attributeName) {
+        $element.removeAttr(attributeName);
+    };
+
+    n.ElementUpdate.setElementText = function ($element, text) {
+        $element.empty().append($.parseHTML(text));
+    };
+})(QfqNS);
diff --git a/javascript/src/QfqForm.js b/javascript/src/QfqForm.js
index 408098e266543ae336e8e16c910a5fa2e1539ddb..b54db6a6663753d2eca31f8b0dbb7934606a0d3d 100644
--- a/javascript/src/QfqForm.js
+++ b/javascript/src/QfqForm.js
@@ -5,6 +5,7 @@
 /* global $ */
 /* global EventEmitter */
 /* @depend QfqEvents.js */
+/* @depend ElementUpdate.js */
 
 /**
  * Qfq Namespace
@@ -78,9 +79,9 @@ var QfqNS = QfqNS || {};
                 n.Helper.showAjaxError(null, obj.textStatus, obj.errorThrown);
             });
 
-        var configurationData = this.readElementConfigurationData();
+        var configurationData = this.readFormConfigurationData();
 
-        this.applyElementConfiguration(configurationData);
+        this.applyFormConfiguration(configurationData);
 
         // Initialize jqxDateTimeInput elements.
         n.Helper.jqxDateTimeInput();
@@ -135,7 +136,7 @@ var QfqNS = QfqNS || {};
      *
      * @private
      */
-    n.QfqForm.prototype.readElementConfigurationData = function () {
+    n.QfqForm.prototype.readFormConfigurationData = function () {
         var $configuredElements = $("#" + this.formId + " [data-hidden],#" + this.formId + " [data-disabled],#" + this.formId + " [data-required]");
 
         var configurationArray = [];
@@ -296,7 +297,8 @@ var QfqNS = QfqNS || {};
             }
 
 
-            this.applyElementConfiguration(data['form-update']);
+            this.applyFormConfiguration(data['form-update']);
+            this.applyElementConfiguration(data['element-update']);
             return;
         }
 
@@ -650,7 +652,8 @@ var QfqNS = QfqNS || {};
 
                 // do we have to update the HTML Form?
                 if (data['form-update']) {
-                    this.applyElementConfiguration(data['form-update']);
+                    this.applyFormConfiguration(data['form-update']);
+                    this.applyElementConfiguration(data['element-update']);
                 }
 
                 if (data.redirect === "url" || data['redirect-url']) {
@@ -782,7 +785,7 @@ var QfqNS = QfqNS || {};
      *
      * @param configuration {array} array of objects.
      */
-    n.QfqForm.prototype.applyElementConfiguration = function (configuration) {
+    n.QfqForm.prototype.applyFormConfiguration = function (configuration) {
         var arrayLength = configuration.length;
         for (var i = 0; i < arrayLength; i++) {
             var configurationItem = configuration[i];
@@ -821,6 +824,14 @@ var QfqNS = QfqNS || {};
         }
     };
 
+    n.QfqForm.prototype.applyElementConfiguration = function (configuration) {
+        if (!configuration) {
+            return;
+        }
+
+        n.ElementUpdate.updateAll(configuration);
+    };
+
     /**
      * @private
      * @param triggeredBy
diff --git a/javascript/src/Utils.js b/javascript/src/Utils.js
index 9f253860e313b179adb8507bb1ad88f63f354145..1ff047c02b5af654282dcd39ffeb0fcc4ae93ac7 100644
--- a/javascript/src/Utils.js
+++ b/javascript/src/Utils.js
@@ -14,6 +14,6 @@ var QfqNS = QfqNS || {};
     'use strict';
 
     n.escapeJqueryIdSelector = function (idSelector) {
-        return idSelector.replace(/(:)/, "\\$1");
+        return idSelector.replace(/(:|\.)/, "\\$1");
     };
 })(QfqNS);
\ No newline at end of file
diff --git a/mockup/elementupdate.html b/mockup/elementupdate.html
new file mode 100644
index 0000000000000000000000000000000000000000..2e770e4cf8b7657517d52ea3ce3466ef205d3e61
--- /dev/null
+++ b/mockup/elementupdate.html
@@ -0,0 +1,204 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+
+    <link rel="stylesheet" href="../css/bootstrap.min.css">
+    <link rel="stylesheet" href="../css/bootstrap-theme.min.css">
+    <link rel="stylesheet" href="../css/jqx.base.css">
+    <link rel="stylesheet" href="../css/jqx.darkblue.css">
+    <link rel="stylesheet" href="../css/qfq-bs.css">
+    <title>Element Configuration</title>
+</head>
+<body>
+
+<label>Refresh URL
+    <select name="refreshUrl" id="refreshUrl">
+        <option>404 error</option>
+        <option>form_refresh.json</option>
+        <option>form_refresh_error.json</option>
+    </select>
+</label>
+
+<div style="float: right">
+<pre id="sample">
+
+</pre>
+    <button id="copy">Copy</button>
+</div>
+<p>
+    <textarea name="configuration" rows="20" cols="80" id="configuration"></textarea>
+</p>
+
+<p>
+    <button id="applyconfig">Apply</button>
+</p>
+
+
+<div class="container-fluid">
+    <div class="row hidden-xs">
+        <div class="col-md-12">
+            <h1>Title with a long text</h1>
+        </div>
+    </div>
+
+
+    <form id="myForm" class="form-horizontal" data-toggle="validator">
+
+        <div id="formgroup1" class="form-group">
+            <div class="col-md-2">
+                <label for="text" class="control-label">Text input (name: text)</label>
+            </div>
+
+            <div class="col-md-6">
+                <input id="text" type="text" class="form-control" name="text" data-disabled="true">
+            </div>
+
+        </div>
+
+        <div class="form-group">
+            <div class="col-md-2">
+                <label for="select" class="control-label">Select (name: select)</label>
+            </div>
+            <input type="hidden" name="select">
+            <div class="col-md-6">
+                <select id="select" class="form-control" name="select">
+                    <option>a</option>
+                    <option>b</option>
+                    <option>c</option>
+                </select>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-md-2">
+                <b class="control-label">
+                    Radio (name: radio)
+                </b>
+            </div>
+
+            <div class="col-md-6">
+                <div class="radio">
+                    <label>
+                        <input type="radio" name="radio" value="a">a
+                    </label>
+                </div>
+                <input type="hidden" name="radio">
+                <div class="radio">
+                    <label>
+                        <input type="radio" name="radio" value="b">b
+                    </label>
+                </div>
+            </div>
+            <div class="col-md-4">
+                <p class="help-block"></p>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-md-2">
+                <b class="control-label">
+                    Checkbox (name: checkbox)
+                </b>
+            </div>
+
+            <div class="col-md-6">
+                <input type="hidden" name="checkbox">
+                <div class="checkbox">
+                    <label>
+                        <input type="checkbox" id="checkbox" name="checkbox" data-hidden="no">
+                    </label>
+
+                    <p class="help-block"></p>
+                </div>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-md-2">
+                <b class="control-label">
+                    Data reload trigger
+                </b>
+            </div>
+
+            <div class="col-md-6">
+                <div class="checkbox">
+                    <label>
+                        <input type="checkbox" id="checkbox2" name="trigger" data-load="">
+                    </label>
+
+                    <p class="help-block">
+                        Changing the value of this checkbox triggers reload
+                    </p>
+                </div>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-md-2">
+                <b class="control-label">
+                    Checkbox 3 test
+                </b>
+            </div>
+
+
+            <div class="col-md-6">
+                <div class="checkbox">
+                    <label>
+                        <input name='checkbox3_1' type="checkbox" value="reminder_value">
+                    </label>
+
+                </div>
+                <div class="checkbox">
+                    <label>
+                        <input name='checkbox3_2' type="checkbox" value="reminder_value">
+                    </label>
+
+                </div>
+                <div class="checkbox">
+                    <label>
+                        <input name='checkbox3_3' type="checkbox" value="reminder_value">
+                    </label>
+
+                </div>
+            </div>
+            <div class="col-md-4">
+
+            </div>
+        </div>
+    </form>
+</div>
+
+<script src="../js/jquery.min.js"></script>
+<script src="../js/bootstrap.min.js"></script>
+<script src="../js/validator.min.js"></script>
+<script src="../js/jqx-all.js"></script>
+<script src="../js/EventEmitter.min.js"></script>
+<script src="../js/qfq.debug.js"></script>
+<script type="text/javascript">
+    $(function () {
+        var form = new QfqNS.QfqForm("myForm", 'none', 'none', 'api/' + $("#refreshUrl").val());
+        QfqNS.Log.level = 0;
+
+        $("#applyconfig").click(function () {
+            var configAsText = $("#configuration").val();
+            var configAsJson = JSON.parse(configAsText);
+
+            QfqNS.ElementUpdate.updateAll(configAsJson);
+        });
+
+        $("#copy").click(function () {
+            $("#configuration").val($("#sample").text());
+        });
+
+        $("#refreshUrl").on("change", function (evt) {
+            form.dataRefreshUrl = 'api/' + $(evt.target).val();
+        });
+
+
+    });
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/mockup/elementconfiguration.html b/mockup/formupdate.html
similarity index 99%
rename from mockup/elementconfiguration.html
rename to mockup/formupdate.html
index dd517d4a05376484b35c154ef314036add095cf1..4d95209888b626f9ad3737d2756e13dc6fd22c7b 100644
--- a/mockup/elementconfiguration.html
+++ b/mockup/formupdate.html
@@ -222,7 +222,7 @@
             var configAsText = $("#configuration").val();
             var configAsJson = JSON.parse(configAsText);
 
-            form.applyElementConfiguration(configAsJson);
+            form.applyFormConfiguration(configAsJson);
         });
 
         $("#copy").click(function () {
diff --git a/mockup/personmock.html b/mockup/personmock.html
index 23e7188616b06e1c40c2c877ccc75b3e3bcd91fe..32c7b976b7e27b59c9b63c27936e0870b0bffcd1 100644
--- a/mockup/personmock.html
+++ b/mockup/personmock.html
@@ -91,7 +91,8 @@
         <div class="col-md-2 ">
             <div class="btn-toolbar pull-right" role="toolbar">
                 <div class="btn-group" role="group">
-                    <button id="save-button" type="button" class="btn btn-default navbar-btn"><span
+                    <button id="save-button" type="button" class="btn btn-default navbar-btn"
+                            data-class-on-change="wdc"><span
                             class="glyphicon glyphicon-ok"></span></button>
                     <button id="close-button" type="button" class="btn btn-default navbar-btn"><span
                             class="glyphicon glyphicon-remove"></span></button>