JSFiddle - React, Tailwind, and code Playground

by coltrane

HTML

<div id="errors"><div class="box"><span class="icon"></span><span class="content"></span></div></div>
<form class="cms-form" action="">
    <table width="800">
        <tr>
            <td width="30%">Name:</td>
            <td><input type="text" name="name" class="cms-input-text" maxlength="127" required="required" /></td>
        </tr>
        <tr>
            <td width="30%">Bla:</td>
            <td><input type="text" name="bla" class="cms-input-text" maxlength="127" required="required" /></td>
        </tr>
        <tr>
            <td>Image:</td>
            <td><textarea name="icon" class="cms-input-file" data-file-resource="images" data-options="{&quot;min&quot;:1,&quot;max&quot;:3}">/location-to-image.png</textarea></td>
        </tr>
        <tr>
            <td></td>
            <td><input type="submit" value="Next"/></td>
        </tr>
    </table>
</form>

CSS

#errors {
    position: absolute;
    width: 100%;
    display: none;
}

#errors > .box {
    position: relative;
    width: 200px;
    z-index: 1000;
    background: white;
    border: 1px solid #ccc;
    border-radius: 10px;
    padding: 5px;
    font-size: 1em;
}

#errors > .box > .content {
    display: inline;
    line-height: 1.2em;
}
#errors > .box > .icon {
    display: inline-block;
    width: 24px; height: 24px;
    vertical-align: middle;
    background: url('http://b.dryicons.com/images/icon_sets/coquette_icons_set/png/24x24/warning.png')
}

JavaScript

$('textarea[name=icon]').on('customValidate', function(evt, reporter) {
    var options = $(this).data('options');

    // ... your validation here ...
    
    // for example:
    var txt = $(this).val();
    if( txt.length < options.min ) {
        reporter.reportValidationError('please add more');
    }
    if( txt.length > options.max ) {
        reporter.reportValidationError('too many. please remove some.');
    }
})


$('form.cms-form').on('submit', function(evt) {
    var frm = $(this);
    var allElements = $(this.elements);
    hideValidationErrors();

    var errors = [];
    var arg = {
        reportValidationError : function( msg ) {
            errors.push({
                msg: msg,
                element: arg.element,
            });
        },
        form : this,
        element : null,
    };
    allElements.each(function(i, el) {
        arg.element = el;
        $(el).trigger('customValidate', [ arg ]);
    });

    if( errors.length !== 0 ) {
        showValidationErrors(errors);
        return false;
    }
    return true;
});

function hideValidationErrors() {
    var el = $('#errors');
    el.fadeOut(100);
}

function showValidationErrors( errs ) {
    if( !errs.length ) { return; }

    // show only the first error, and position it near the
    // relevant form element.
    var el = $('#errors');
    var err = errs[0];
    
    var contentEl = el.find('.content');
    contentEl.empty().text(err.msg);
    
    var target = $(err.element).focus();
    var extents = target.offset();
    extents.right = target.width()+extents.left;
    extents.bottom = target.height()+extents.top;

    console.dir(target);
    
    el.css({
        top: extents.bottom +1,
        left: extents.left +3,
    });

    el.stop().fadeIn(100);
    
    var n = '.'+Math.floor(Math.random()*0xffffffff);
    var frm = $(target[0].form);
    console.log('frm', frm, n);
    frm.on('change'+n +' input'+n +' keydown'+n, '*', _hideErrs)
        .on('focus'+n, '*',...