# How I made a lightweight JavaScript Framework and then used it to build my Website - jasna.js

I am not often using JavaScript, and I am sure that there is already a framework which does the same thing. I didn't even expect to use it, but surprisingly it really helped to build my website.  
The minified version only takes up 479 bytes. Not even half a kilobyte!

Get it on [GitHub](https://github.com/remboldt/JasnaJS)

# Why I built it

## The problem I tried to solve

When building a website the HTML file can get heavily nested quite fast. I wanted to split up the website into multiple HTML files, so I can work on each component separately without the file getting too confusing to look at. Then I would simply put every component together in one clearly structured HTML file.

But as I didn't want to learn a complex JS framework with tons of features that would slow down my website, I decided to make my own lightweight and robust framework.

## Name

The original name of the script was really simple and overused `component_loader.js`. So I came up with this idea: As this framework makes your code clearer and more readable, I chose `jasna.js` for publishing it. Ясно ("Yasno") is the Russian word for "understand" or "clear" and it is pronounced like "Jansa".

# Development

## Selector

First of all, I had to figure out how to clarify which parts of the HTML document should be affected at all. First I had the idea to go through each `<div>` tag and check if it has the value `"component"` in the class attribute. But then I had a much better idea. I could just create a unique tag: `<component>`. This one is very easy to remember and you can find it more quickly in the code, so I decided on this. Also, this was very simple to implement:

```javascript
var components = document.getElementsByTagName("component");
```

Now I had a collection of every element with this specific tag name.

## Looping through

Now I had a collection of items. But I could only work with one at a time. So I made a simple for-loop to accomplish this.

```javascript
var components = document.getElementsByTagName("component");
for (let i = 0; i < components.length; i++) {
    // do stuff with each components[i]
}
```

## Source

Next, I had to come up with an Idea, how the developer specifies, where the component is stored at. In the first version of the script, I checked the id attribute for the URL of the source.

```javascript
var url = component.id; //was a bad Idea
```

But later I figured, that it would be much smarter to check the src attribute. Like in `<img src="">` or `<script src="">`.

```javascript
var components = document.getElementsByTagName("component");
for (let i = 0; i < components.length; i++) {
    var url = components[i].attributes['src'].textContent; //beter idea
}
```

## Fletching my teeth on fetching data

Now, for this part, I was overwhelmed, as I have barely used asynchronous functions before. I have used "threading" in Python before, which is roughly the equivalent of JavaScript's async functions.

At the end of watching several YouTube Videos, reading some documentations at MDN, and some tutorials. I have decided to go with XMLHttpRequest.  
On [W3Schools](https://www.w3schools.com/XML/ajax_xmlhttprequest_response.asp) I found exactly what I was looking for:

```javascript
var components = document.getElementsByTagName("component");
for (let i = 0; i < components.length; i++) {
    var url = components[i].attributes['src'].textContent; 

    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
           content = xhttp.responseText;
           components[i].innerHTML = content;
        }
    };
    xhttp.open("GET", url, true);
    xhttp.send();

}
```

At this point, the code was already working (I think). But I have decided to split the code up a bit because I wanted to implement live data. I made a separate function called `load_component()`:

```javascript
function load_content(component) {
    var url = component.attributes['src'].textContent;

    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
           content = xhttp.responseText;
           component.innerHTML = content;
        }
    };
    xhttp.open("GET", url, true);
    xhttp.send();
}

var components = document.getElementsByTagName("component");
for (let i = 0; i < components.length; i++) {
    load_content(components[i]);
}
```

You can see this in action on my webpage (website updated by now)

## Extra feature: "live\_components"

At this point, you could only load everything just once. But I have decided, that dynamic content would be cooler. I won't go into details here, because the article is already really long.

### Extending the for-Loop

```javascript
for (let i = 0; i < components.length; i++) {
    load_content(components[i]);

    // If the first value of the class attribute is "refresh", then the component will be passed to a async function, which contains a never ending while-Loop
    if (components[i].classList[0] == "refresh") {
        live_components(components[i])
    }
}
```

Example:

```xml
<component src="https://examp.le/dynamic.php" class="refresh"></component>
```

### The asynchronous function

```javascript
async function live_components(component) {
    if (isNaN(component.classList[1])){
        // If the second value of the class attribute is not a number (NaN), the function will check every 2 seconds (2000ms) if the content is new
        sleep_time = 2000;
    } else {
        // If the second value is a number, then this will be taken as the "sleep_time"
        sleep_time = parseInt(component.classList[1]);
    }

    while (true) {
        await new Promise(r => setTimeout(r, sleep_time));
        load_content(component);
    }
}
```

Now you can specify in what time intervals it should check for updated content:

```xml
<component src="dynamic.php" class="refresh 10000"></component>
<!-- it will re-check every 10 seconds -->
```

### Only reload the content if it has changed

```javascript
function load_content(component) {
    var url = component.attributes['src'].textContent;

    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
            content = xhttp.responseText;

            // I have added this if-condition, to check if the content of the component is already the same as the newly fetched content
            if (content !== component.innerHTML) {
                // Only updates if the source content has changed
                component.innerHTML = content;
            }
        }
    };
    xhttp.open("GET", url, true);
    xhttp.send();
}
```

# Finished Code

jasna.js

```javascript
function load_content(component) {
    var url = component.id;

    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
           content = xhttp.responseText;
           component.innerHTML = content;
        }
    };
    xhttp.open("GET", url, true);
    xhttp.send();
}

async function live_components(component) {
    if (isNaN(component.classList[1])){
        sleep_time = 2000;
    } else {
        sleep_time = parseInt(component.classList[1]);
    }
    while (true) {
        await new Promise(r => setTimeout(r, sleep_time));
        load_content(component);
    }
}

var components = document.getElementsByTagName("component");

for (let i = 0; i < components.length; i++) {
    load_content(components[i]);
    if (components[i].classList[0] == "refresh") {
        live_components(components[i])
    }
}
```

# Live Example

As I said, I have written my website with the help of this little framework. And the index file looks really clear and readable.

(Website updated by now)

```xml
<!DOCTYPE html>
<html>
    <head>
        <title>One Remboldt</title>
        <meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
        <link rel="stylesheet" href="css/bulma.min.css" />
        <link rel="stylesheet" href="css/style.css" />
    </head>
    <body>
        <component src="components/navbar.html"></component>
        <component src="components/header.html"></component>
        <component src="components/about_me.html"></component>
        <component src="components/links.html"></component>
        <component src="components/bottom_part.html"></component>
        <component src="components/footer.html"></component>

        <script src="js/component_loader.js"></script>
        <script src="js/counter.js"></script>
    </body>
</html>
```

View [JasnaJS on GitHub](https://github.com/remboldt/JasnaJS).
