Lazy load blog post images by default

* Post.HTML() and PreviewHTML() both mogrify the resulting HTML code to
  ensure all <img> tags have loading="lazy" unless a loading attribute
  is already present.
This commit is contained in:
Noah 2020-04-09 19:16:41 -07:00
parent 211b7d8318
commit 8f98e72e47
4 changed files with 90 additions and 10 deletions

View File

@ -160,11 +160,7 @@ func PostFragment(w http.ResponseWriter, r *http.Request) {
v.V["post"] = post
// Render the body.
if post.ContentType == models.Markdown {
v.V["rendered"] = template.HTML(markdown.RenderTrustedMarkdown(post.Body))
} else {
v.V["rendered"] = template.HTML(post.Body)
}
v.V["rendered"] = post.HTML()
responses.RenderTemplate(w, r, "_builtin/blog/view-post.gohtml", v)
}

View File

@ -12,6 +12,7 @@ import (
"git.kirsle.net/apps/gophertype/pkg/console"
"git.kirsle.net/apps/gophertype/pkg/markdown"
"git.kirsle.net/apps/gophertype/pkg/mogrify"
"git.kirsle.net/apps/gophertype/pkg/rng"
"github.com/albrow/forms"
)
@ -383,12 +384,13 @@ func (p Post) PreviewHTML() template.HTML {
if hasMore {
body += fmt.Sprintf("\n\n[Read more...](/%s)", p.Fragment)
}
return template.HTML(markdown.RenderTrustedMarkdown(body))
}
if hasMore {
body = markdown.RenderTrustedMarkdown(body)
} else if hasMore {
body += fmt.Sprintf(`<p><a href="/%s">Read more...</a></p>`, p.Fragment)
}
// Make all images lazy loaded. TODO: make this configurable behavior?
body = mogrify.LazyLoadImages(body)
return template.HTML(body)
}
@ -396,8 +398,11 @@ func (p Post) PreviewHTML() template.HTML {
func (p Post) HTML() template.HTML {
body := strings.ReplaceAll(p.Body, "<snip>", "")
if p.ContentType == Markdown {
return template.HTML(markdown.RenderTrustedMarkdown(body))
body = markdown.RenderTrustedMarkdown(body)
}
// Make all images lazy loaded. TODO: make this configurable behavior?
body = mogrify.LazyLoadImages(body)
return template.HTML(body)
}

35
pkg/mogrify/lazy_load.go Normal file
View File

@ -0,0 +1,35 @@
package mogrify
import (
"fmt"
"regexp"
"strings"
)
// Outgoing HTML filters for responses (experimental).
var reImgTag = regexp.MustCompile(`<img ([^>]+)>`)
// LazyLoadImages modifies <img> tags to add loading="lazy" attribute to them.
// Ignores image tags that already include the attribute.
func LazyLoadImages(input string) string {
m := reImgTag.FindAllStringSubmatch(input, -1)
if m == nil {
return input
}
for _, match := range m {
var (
tag = match[0]
attrs = match[1]
replace = fmt.Sprintf(`<img %s loading="lazy">`, attrs)
)
if strings.Contains(attrs, "loading=") {
continue
}
input = strings.Replace(input, tag, replace, -1)
}
return input
}

View File

@ -0,0 +1,44 @@
package mogrify_test
import (
"testing"
"git.kirsle.net/apps/gophertype/pkg/mogrify"
)
func TestLazyLoadImages(t *testing.T) {
var tests = []struct {
In string
Expect string
}{
{
In: `<img src="logo.jpg">`,
Expect: `<img src="logo.jpg" loading="lazy">`,
},
{
In: `<img src="https://example.com/image.png" loading="eager">`,
Expect: `<img src="https://example.com/image.png" loading="eager">`,
},
{
In: `Hello world`,
Expect: `Hello world`,
},
{
In: `<img src="a"><img src="b"><img src="a">`,
Expect: `<img src="a" loading="lazy"><img src="b" loading="lazy"><img src="a" loading="lazy">`,
},
{
In: `<img src="a"><img src="b" loading="eager"><img src="a">`,
Expect: `<img src="a" loading="lazy"><img src="b" loading="eager"><img src="a" loading="lazy">`,
},
}
for _, test := range tests {
actual := mogrify.LazyLoadImages(test.In)
if actual != test.Expect {
t.Errorf("for input {%s} I expected {%s} but got: {%s}",
test.In, test.Expect, actual,
)
}
}
}