HTML Include Functionality

Back to Home
Back to This Website

HTML would have you repeat several elements across webpages if it were completely up to it itself.
This is probably one of the big reasons that any abstractions have been made, and web development is riddled with frameworks.

I will never learn JavaScript.

Include Handling

A big first challenge with making a multi-page website was the idea of a consistent footer across all of the websites.
What if I want to edit this in the future? Would I have to go through every single webpage and edit each instance?

This is obviously unacceptable.
However, HTML explicitly doesn't support this--it's not made to be anything that supports this sort of runtime replacement.
You can not "call" a webpage to duplicate the element across individual pages using pure HTML, per u/udubdavid. My solution is to instead start introducing additional functionality to pure HTML in a sort of compile-time source file replacement. The initial requirement will be to introduce include functionality that directly enumerates the contents of a file in the built file, replacing the directive's line.

The compile-time "directive" is arbitrary. I personally like C. I'll start off by using #​#include fileNameHere. At a later date this may be corrected to something else should it be necessary.
Without a desire to create a brand new Python script that will do this for me, I'm instead staring down the idea of doing all of this in a shell script. This is fine--it can be scaled with a shell script through the nice Makefile enumerations.
The goal is to take 2 raw files and expand the included one inside of the including one.
Turns out that this isn't as easy as one may think. Text replacement schemes in shell scripts are few and far in between. These are largely their own scripts provided here as CLI arguments to individual files. I will be using `awk` to do this, and feel it necessary to state that it is GNU's implementation if there are portability problems with i.e. Mac. This is all mostly being worked on from a Rocky Linux VM on a Windows environment. I've run into some things like that with specifically `sed` vs. `gsed` on Mac systems.

So the goal would be to turn #​#include include/footer.ihtml from the source file into the contents of include/footer.ihtml in the output...
< contents of include/footer.ihtml >:
<​!DOCTYPE html>
<​!--
footer.ihtml

bottom of the content on the website
-->
    <​footer>
        <​p>
            Nate Wichers<​br>
            Electrical Engineer doing Embedded Software, GE Aerospace<​br>
            <​p>Views expressed are my own--any employer that I may have at any given time should not be associated with my own thoughts and opinions as expressed here.<​/p>
            <​a href="https://github.com/BluRosie" style="color: rgb(184, 184, 184)"><​b>GitHub<​/b><​/a>
                
            <​a href="https://twitter.com/bluroseai" style="color: rgb(184, 184, 184)"><​b>Twitter (currently X)<​/b><​/a>
        <​/p>
        <​p>
            <​a href="mailto:ncwicher@mtu.edu" style="color: rgb(184, 184, 184)"><​b>ncwicher@mtu.edu<​/b><​/a>
        <​/p>
    <​/footer>
This way the footer gets copied into wherever it needs to be.

Initial Commands

The way that finally worked for me to do this was this nice command:
awk '/#​#include include\/footer.ihtml/{while((getline<"'include/footer.ihtml'") > 0){print};next}{print}' index.html > output.html
The command passes a script to awk essentially has an outer loop that prints each line of index.html.
However, when the specific line is encountered, it enters the inner loop where it prints every line from the included file.
But now we want to process multiple different includes! This requires an intermediate file and would look something like this:
cp -f index.ihtml output.html
for file in $(grep "#​#include" index.ihtml | sed -e "s+#​#include ++g"); do
    awk '/#​#include '$file'/{while((getline<"'$file'") > 0){print};next}{print}' output.html > intermed.html;
    mv -f intermed.html output.html
done
The grep command just finds all of the #​#include lines in the file and essentially generates a list of the files that are to be included. Note that I had to include the \ in front of the /, but only for the first filename to escape the awk script.

But we forgot to handle the escape character for the special awk character '/'.
That makes the code look something like this:
cp -f index.ihtml output.html
for file in $(grep "#​#include" index.ihtml | sed -e "s+#​#include ++g"); do
    awk '/#​#include '$(echo $file | sed -e 's|/|\\/|g')'/{while((getline<"'$file'") > 0){print};next}{print}' output.html > intermed.html;
    mv -f intermed.html output.html
done

Putting into a Makefile

Finally it is possible to make a Makefile rule.
Because each output file has its own set of includes, we have to make a rule that defines each of the individual files as an output.
This is where you find out that inside of a rule, a single $ passed to the shell actually needs to be $$$$ instead of the typical 2. I still can not find any documentation that states this, but found it out before digging in too deeply.
SOURCE_DOCS := $(wildcard *.ihtml)
BUILD := build
ARTEFACTS := $(patsubst %.ihtml,$(BUILD)/%.html, $(SOURCE_DOCS))

all: $(ARTEFACTS)

define INDIVIDUAL_OUTPUT_DEFINE

$(BUILD)/$2: $1 $(shell grep "#​#include" $1 | sed -e "s+#​#include ++g")
	cp -f $1 $(BUILD)/$2
	for file in $(shell grep "#​#include" $1 | sed -e "s+#​#include ++g"); do \
		awk '/#​#include '$$$$(echo $$$$file | sed -e 's|/|\\/|g')'/{while((getline<"'$$$$file'") > 0){print};next}{print}' $(BUILD)/$2 > intermed.html; \
		mv intermed.html $(BUILD)/$2; \
	done

endef
$(foreach source, $(SOURCE_DOCS), $(eval $(call INDIVIDUAL_OUTPUT_DEFINE,$(source),$(subst ihtml,html,$(source)))))
Now I can separate out individual font files and the entire footer file. Soon I'll even have a menu or something that will be easy to include in every header because of this system.
And whenever I want to make changes across every page, I just have to run a quick make and redeploy the website.

Bonus

All of the embedded HTML and the "#​#include" string not triggering the grep/sed commands is accomplished with a zero-width space character splitting up the tags as necessary!

A very nice Makefile debugging oneliner as well:
print-% : ; $(info $($*)) @true
This allows me to run i.e. make print-ARTEFACTS and get the output:
build/other-things.html build/code_injection_nds.html build/index.html build/hg-engine.html build/this-website.html build/html-include-functionality.html
Which is immensely helpful for debugging anything that looks like arcane wizardry when it comes to the Makefile syntax.