Browse Source

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.
master
Noah Petherbridge 6 months ago
parent
commit
8f98e72e47
4 changed files with 90 additions and 10 deletions
  1. +1
    -5
      pkg/controllers/posts.go
  2. +10
    -5
      pkg/models/posts.go
  3. +35
    -0
      pkg/mogrify/lazy_load.go
  4. +44
    -0
      pkg/mogrify/mogrify_test.go

+ 1
- 5
pkg/controllers/posts.go 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)
}


+ 10
- 5
pkg/models/posts.go 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
- 0
pkg/mogrify/lazy_load.go 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
}

+ 44
- 0
pkg/mogrify/mogrify_test.go 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,
)
}
}
}

Loading…
Cancel
Save