Simple form validation in javascript
I must admit this is pretty good code for someone that has just started, I like that you use the more "functional" style Javascript allow you to use . So good job :) But there are some design issues I see with the code.

1. Factory vs constructor

Your Validator has a make method which takes the rules in, It is basically used as a factory method (A factory is a design pattern if you are not familuar with it). But it has none of the benefits a factory gives you, since it is a instance method.

Having to instantiate a Validator object and then being forced to call make is cumbersome. Instead I suggest you make the make function static and add a constructor

class Validator
{
   static make(rules) 
   {
        return new Validator(rules);
   }

   constructor(rules) 
   {
       this.rules = rules;
   }
}


2. Checking class properties

You don't need to do this check in your valid function.

if (!this.hasOwnProperty('rules')) {
    return true;
}


Since it's practically not possible to not have rules with the proposed factory. I understand why this is here though. You wanted to make sure you can't forget the make call after instantiating a Validator object. But that's a wrong schematic. If the caller uses your code wrong it should fail, and not say "I don't care, but this is valid"

3. Naming

Methods should indicate that they are actions, and properties that they are values.

Your valid method sounds a lot like a property, like a value. I would call it validate since that is a verb and makes it clear that it does something and not "is" something. And I would rename the property valid that your return in your valid method to isValid

4. ES6

EDIT: I noticed to late that you already used an arrow function in your code. Ignore the explanation if you want and just replace the Object.keys().forEach(callback) callback with an arrow function. BTW you do not need the parentheses if you only have one parameter. 

You can use a arrow function in your forEach(function) function. This is a es6 feature and a bit more advanced tho. Using functions is fine but it can solve the issue you have with scoping.

The function keyword does create a new scope and therefore changes the meaning of this. The arrow function on the other hand behaves exactly like a function but does not change the meaning of this

validate() {
    let errors = [];

    Object.keys(rules).forEach(key =&gt; {
        let formElement = document.getElementsByName(key)[0];

        if (formElement == undefined) {
            return;
        }

        let ruleSet = rules[key].split('|');

        ruleSet.forEach((rule) =&gt; {
            let result = this.checkRuleForElement(rule, formElement)

            if (!result.valid &amp;&amp; result.message != undefined) {
                errors.push(result.message);
            }
        });
    });

    return {
        isValid: errors.length == 0,
        errors
    }
}


Another thing you can leave out is the
errors: errors part. You can shorten it to just errors since the name of the variable is the same as key of the object

5. Decouple things

Currently your "rule running" and "rule definitions" are all in the same class/method (in the switch in checkRuleForElement) So if you want to add a new rule you have to modify your Validator class which should only do one thing: Invoke the rules for a element. The rule definitions are a whole separate concern.

You can try to decouple it by using arrays.

A sample implementation may look like this:

// This is not good code, take it as an "idea" or psuedocode

class Validator
{
   static registeredRules = [];

   static registerRule(name, run)
   {
      Validator.registeredRules.push({ name, run });
   }

   checkRuleForElement(ruleName, formElement)
   {
      var rule = Validator.registeredRule.find(element =&gt; element.name === ruleName);
      return rule.run(formElement);
   }
}


6. Abstract things

A thing you may want to consider is that there may be multiple forms or multiple elements, with your current implementation you are relying on the fact that there are is a unique element foreach "rule". Also you are interacting with the Dom directly which makes things hard to change when that assumption changes.

You could solve this by, for example using Dom elements as an input:

Validator.make({
     document.querySelector("#user_input"): "required"
})


Bonus exercises

You can try to add a more advanced "rule parser" and add parameters for rules.

This could look like this:

Validator.make({
  "username": "required|min:5"
})


Another thing you could try is, skipping this whole "naming" thing and using just functions

For example

const required = element =&gt; element != undefined;

// If you do not understand this Syntax, it's basically a function that returns a function.
const min = count =&gt; element =&gt; element.length &gt; count

Validator.make({
  "username": [required, min(5)]
});


This would give you several advantages. For example that your rules can be checked by a static analyzer, If you use strings there is always a chance you mistype something, like use "regired" instead of "required" and not even notice it, with functions on the other hand you will get an error if you use something that is undefined. Also you could reuse rules better.



Have fun :)

I wrote this from my mobile so if there are any mistakes let my know. I will most likely add more when I get my hands on a pc!