Saturday, June 1, 2013

ZK CDT: Add Attributes to Create Enhanced Mask


This is the second article of ZK CDT (ZK Component Development Tutorial) walkthrough, this article describe how to add attributes to a component then initiate component with attributes and/or dynamically update them.

The goal of this walkthrough is to create a quicknote component that help you do some quick note on web page (similar to you crop some screenshot, highlight something and add some text in photo editor), and describe each part of a component through each article in this walkthrough.

This is the second part: Handle attributes of a component


View demo online:

Similer to the mask component in previous article, but now you can change the opacity and background-color of mask as needed.


ZK CDT: Create Custom Mask Component



Contains an enhancedmask component, a slider/colorbox that control the opacity/background-color of the enhancedmask.

        <!-- The enhancedmask component that will cover
            its children by a mask -->
        <enhancedmask width="500px" id="emask"
            opacity="20" maskColor="#00FF00">
            <button label="ZK Website" />
            <iframe width="100%"
            <!-- slider used to control opacity of enhancedmask -->
            <slider curpos="20" maxpos="100">
                <attribute name="onScroll"><![CDATA[
                <attribute name="onScrolling"><![CDATA[
            <!-- colorbox used to control mask color of enhancedmask -->
            <colorbox color="#00FF00">
                <attribute name="onChange"><![CDATA[

The java class of enhancedmask comopnent, extends XulElement and add attributes opacity and maskColor.

package custom.zk.components.quicknote;

import org.zkoss.zul.impl.XulElement;

 * java class for EnhancedMask component,
 * extends XulElement and add attributes opacity and maskColor
 * Two new things:
 * smartUpdate: used to update client side attribute with the UI thread,
 *                 call it within ZK UI thread then it will bring the status
 *                 back to client side
 * renderProperties: used to render all properties to client side at the beginning,
 *                     it is a part of component life cycle in ZK,
 *                     will be called by ZK framework automatically.
 * @author benbai123
public class EnhancedMask extends XulElement {
    private static final long serialVersionUID = -2084534449227910442L;

    private int _opacity = 35;
    private String _maskColor = "#ccc";

    public void setOpacity (int opacity) {
        // no negative
        if (opacity < 0) {
            opacity = 0;
        // cannot larger than 100
        if (opacity > 100) {
            opacity = 100;
        // update if value is changed
        if (_opacity != opacity) {
            _opacity = opacity;
            smartUpdate("opacity", _opacity);
    public int getOpacity () {
        return _opacity;
    public void setMaskColor (String maskColor) {
        // update if there is a different value
        if (maskColor != null // no null
            && !maskColor.isEmpty() // no empty
            && !maskColor.equals(_maskColor)) { // value is changed
            _maskColor = maskColor;
            smartUpdate("maskColor", _maskColor);
    public String getMaskColor () {
        return _maskColor;
    //-- ComponentCtrl --//
    // the renderProperties method is a part of component life cycle in ZK,
    // it will be called by ZK framework automatically,
    // remember to render super's properties first
    protected void renderProperties(org.zkoss.zk.ui.sys.ContentRenderer renderer)
        throws {
        if (_opacity != 35) {
            // this will call setOpacity (opacity) in widget class at client side
            render(renderer, "opacity", _opacity);
        if (!"#ccc".equals(_maskColor)) {
            // this will call setMaskColor (maskColor) in widget class at client side
            render(renderer, "maskColor", _maskColor);


The widget class of enhancedmask component, provide zclass and setters of opacity/maskColor.

 * Widget class of EnhancedMask component,
 * extends zul.Widget, change zclass and add setters for
 * attributes opacity and maskColor
 * Two new things:
 * setters: the setSomething methods are mapping to 'something' at
 *             server side when render or smartUpdate is called.
 *             e.g., setOpacity (in widget class) is called if executes
 *                 smartUpdate("opacity", _opacity);
 *                 or
 *                 render(renderer, "opacity", _opacity);
 *                 at server side
 * $n():    This is the API to get dom elements by specified id.
 *             ZK will assign each component an ID (say uuid) (is unique within an ID Space),
 *             we usually assign the uuid to root dom element,
 *             and assign uuid+'-'+suffix to child elements
 *             In this case, this api will return the root element when you call this.$n(),
 *             and return the child element with specific suffix when you call this.$n(suffix)
 *          e.g., We specified uuid+'-mask' as the ID of child mask element in enhancedMask.js,
 *              so we can get the mask dom element by this.$n('mask');
 *  NOTE: It is important to specify the uuid to root dom element of a widget,
 *          or ZK can not find it while detaching it,
 *          i.e., will leave the element in dom tree after the component is detached,
 *          and causes weird bug or memory leak in browser.
 *          In most cases, you can call this.domAttrs_() directly when you output the html
 *          of root dom element then it should work fine.
custom.zk.components.quicknote.EnhancedMask = zk.$extends(zul.Widget, {
     * default values: set default value in both java class and
     * widget class, so do not need transfer them via network if
     * we just want to use the default values
    _opacity: 35,
    _maskColor: '#ccc',
    setOpacity: function (opacity) {
        // update if value is changed
        if (this._opacity != opacity) {
            // update value
            this._opacity = opacity;
            // try to get mask element
            var mask = this.$n('mask');
            if (mask) {
                // apply opacity to mask if it is available
                jq(mask).css('opacity', (opacity/100));
    setMaskColor: function (maskColor) {
        // update if value is changed
        if (this._maskColor != maskColor) {
            // update value
            this._maskColor = maskColor;
            // try to get mask element
            var mask = this.$n('mask');
            if (mask) {
                // apply background-color to mask if it is available
                jq(mask).css('background-color', maskColor);
    // override with new css class name
    getZclass: function () {
        var zcls = this._zclass;
        return zcls? zcls : 'z-enhancedmask';


Define the redraw function that output the html code of enhancedmask component.

 * redraw for enhancedmask component,
 * wrap children with a div then cover them,
 * also generate style based on attributes
function (out) {
    var uuid = this.uuid,
        zcls = this.getZclass(),
        style = ' style="background-color: ' + this._maskColor
                        +'; opacity:' + (this._opacity/100) + ';"';
    // output root dom element of this widget
    out.push('<div', this.domAttrs_(), '>');
    // output children
    for (var w = this.firstChild; w; w = w.nextSibling)
    // output mask that cover children
    // with generated style
    out.push('<div id="', uuid, '-mask"', style, ' class="', zcls,'-cover"></div>');


Define the style of the dom elements in enhancedmask component.

<%--// ------------------------------------------- --%>
<%--//                                             --%>
<%--//            EnhancedMask component           --%>
<%--//                                             --%>
<%--// ------------------------------------------- --%>
<%--// root element --%>
.z-enhancedmask {
    <%--// be the anchor of absolute positioned children --%>
    position: relative;
    overflow: hidden;
<%--// the mask that cover whole element --%>
<%--// no background-color and opacity specified --%>
<%--// since we specified them in widget class --%>
.z-enhancedmask-cover {
    <%--// absoluted positioned --%>
    position: absolute;
    <%--// align the left-top corner of parent (root) element --%>
    left: 0px;
    top: 0px;
    <%--// cover whole root element --%>
    height: 100%;
    width: 100%;
    <%--// make it the top most element under root element --%>
    z-index: 99999;


Define components under "custom.zk.components.quicknote"

* only the added part, not the full code

NOTE: more components will be added with other articles later

    <widget name="EnhancedMask" />


Define all components in the project

* only the added part, not the full code

NOTE: more components will be added with other articles later

    <!-- the second, enhancedmask component
        can dynamically change the opacity and
        background-color of mask


Full project at github



  1. This comment has been removed by a blog administrator.

    1. deleted since not meaningful for this article