3
\$\begingroup\$

I am starting to write a plugin that is aiming to validate a piece of mark up against requirements of schema.org.

I was hoping if I could get some more tips on how to improve the structure of my code. Is this how you would go about this if you were to do it?

    $.fn.hasAttr = function(name) {  
       return this.attr(name) !== undefined;
    };

    $.fn.outerHTML = function(s) {
        return $(this).clone().wrap('<div>').parent().html();
    };

    $.fn.getOpeningTag = function (s) {
      return $(this).outerHTML().slice(0, $(this).outerHTML().indexOf(">") + 1) 
    }


    $.fn.validateSchema = function(options) {

      var defaults = {
        // for a list of international postcode regex see: http://www.thalesjacobi.com/Regex_to_validate_postcodes
        // The one used in the example is from the UK
        localPostCodeFormat: new RegExp("^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([A-Za-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) {0,1}[0-9][A-Za-z]{2})$"),
        utilities : {
            missAtrr : function (target, attrName) {
              if (!target.hasAttr(attrName)){
                 defaults.returnError(target, "The element " + target.getOpeningTag()  + " is missing the required attribute " + attrName);                          
              }
            },              
            isValidPostCode : function (target) {
              if (!defaults.localPostCodeFormat.test(target.text())) {
                defaults.returnError(target, target.getOpeningTag() + " does not have a valid post code");
              }

            }
        },
        returnError : function (target, errorMessage) {
          var errorContainer = $('<span class="schemaError"></span>');  
          target.before(errorContainer);
          errorContainer.css({
            'color' : 'red',
            'border': 'solid 1px red'
            }).text(errorMessage);
        } // more utils to be added
      };
      // Extend our default options with those provided.
      var opts = $.extend(defaults, options);

      var schemaElements = {
            postalAddress : $(this).find('[itemtype*=PostalAddress]'),
            itemProps : $(this).find("[itemprop]"),
            emptyProps : $(this).find("[itemprop='']"),
            postCode : $(this).find("[itemprop=postalCode]"),
            email : $(this).find("[itemprop=email]"),
            outofplaceChild : $(this).find("[itemscope]").siblings("[itemprop]")// more dom refs to be added
        }

      // validation stuff
      //
      // PostalAddress should have itemsscope
      if (schemaElements.postalAddress){
        defaults.utilities.missAtrr(schemaElements.postalAddress, "itemscope");
      }
      // No itemscope can be left empty
      if (schemaElements.emptyProps){
        $.each(schemaElements.emptyProps, function () {
            defaults.returnError($(this), $(this).getOpeningTag() + " can not be left without a value");    
        });             
      }
      // postcode should match the post code format of that country
      if (schemaElements.postCode){
        defaults.utilities.isValidPostCode(schemaElements.postCode);
      }
      // No itemprop can exist without having a itemscope parent
      if (schemaElements.outofplaceChild){
        $.each(schemaElements.outofplaceChild, function () {
            defaults.returnError($(this), $(this).getOpeningTag() + " Does not have a itemscope parent");   
        });   
      }
      // more rules to be added
    };

    $(function(){
        $('body').validateSchema();     
    });
\$\endgroup\$
5
  • \$\begingroup\$ I haven't used code review before? Is the no response due to much lower traffic compared to stack or I am doing something wrong? Do people expect me to start a bounty to make this worth while? \$\endgroup\$ Commented Jan 20, 2012 at 10:36
  • 4
    \$\begingroup\$ i added a bounty for you \$\endgroup\$
    – seand
    Commented Jan 20, 2012 at 13:53
  • 1
    \$\begingroup\$ There are lots of unanswered jQuery questions. A jQuery expert could get lots of rep here :) \$\endgroup\$
    – palacsint
    Commented Jan 20, 2012 at 18:16
  • \$\begingroup\$ @XGreen, I am in the same boat, just added +50 to a basic question of best practices. I don't think there are enough people using this site to get questions from all but the most popular programming languages/tools answered. \$\endgroup\$
    – wespiserA
    Commented Jan 24, 2012 at 19:16
  • \$\begingroup\$ Yes the action is pretty much only in stackoverflow, the ux and here are not very active. seand has put 100 down for this and that is why I am waiting to see a bit more, even though the answers these guys have given are also good. \$\endgroup\$ Commented Jan 24, 2012 at 21:23

2 Answers 2

4
+100
\$\begingroup\$

You could replace .fn.getOpeningTag with:

$.fn.getOpeningTag = function (s) {
    if( this[0] && this[0].nodeType === 1 ) {
        return "<" + this[0].tagName.toLowerCase() + ">";
    }
};

I saw 600x performance improvement in chrome


These are methods on jQuery objects so doing $(this) is redundant, this will already be a jQuery object.


Do not put stuff like validateSchema, in jQuery.fn. It is not a method that operates on a collection DOM elements, make it class that is consists of many small methods. That way you avoid the 9000 configuration options pattern.

function SchemaValidation() {

}

SchemaValidation.prototype = { ... };

var schemaValidation = new SchemaValidation( a, b, c );
schemaValidation.addElements( e, f, g );
schemaValidation.validate();

Of course you don't have to do it that way but for god's sake do not have it as a jQuery method, it doesn't make any sense. Especially in this case you aren't even using the jQuery object the method is called on. Well, technically you are but that's superfluous.

\$\endgroup\$
4
\$\begingroup\$

This will return false for attr in such an object {'attr': undefined}:

$.fn.hasAttr = function(name) {  
   return this.attr(name) !== undefined;
};

If this is not exactly what you want, have a look at related posts (one, two) and search for much more.


This (and similars) will always evaluate to true, if schemaElements object has the postalAddress key:

if (schemaElements.postalAddress){
    defaults.utilities.missAtrr(schemaElements.postalAddress, "itemscope");
}

here's an example session from Chrome developer console:

var rules = {'a': $('html').find('#idontexist')};
rules.a;
// []
if (rules.a) {console.log(true)} else {console.log(false)};
// true

Be consistent with ; at the end of the lines.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.