In Jekyll How do I grab a post's first image? - jekyll

In my index of blog posts I'd like to grab the first image from the post to display it in the index using just liquid so it works on github pages.
I have a feeling split is the way to go, but I'm not good with liquid.
I'd like to be able to get the image url and put it in a variable to style it.
The ideal solution would be something like:
{% for post in site.posts %}
<li>
{{post.content | first_image}}
</li>
{% endfor %}
Any ideas?

You can define a custom variable to your Front Matter called "image", so it's going to work like Wordpress's posts Featured Image:
---
image: featured-image.jpg
---
Note to remember where is your image saved. In my case, I created a directory called "imagens" (PT-BR here). Then, go to your index.html and add the image to your template, wherever you want. In my site it looks like this:
<ul class="post-list">
{% for post in site.posts %}
<li>
<h2>
<a class="post-link" href="{{ post.url | prepend: site.baseurl }}">{{ post.title }}</a>
</h2>
<span class="post-meta">{{ post.date | date: "%b %-d, %Y" }},</span>
<span class="post-meta">por</span>
<span class="post-meta">{{ post.author }}</span>
</li>
//IMAGE
<img src="{{ site.baseurl }}/imagens/{{ post.image }}">
//IMAGE
{{ post.excerpt }}
<a class="btn btn-default" href="{{ post.url | prepend: site.baseurl }}">Continuar lendo</a>
{% endfor %}
</ul>
That's it.

Some solutions to your problem :
1 - Use the Post Excerpt tag Documentation is here
Your post :
---
layout: post
title: Testing images
---
## Title
Intro Text
![Image alt]({{ site.baseurl }}/assets/img/image.jpg "image title")
More intro text
Some more text blah !
Your template :
<ul>
{% for post in site.posts %}
<li>
{{ post.title }}
{{ post.excerpt }}
</li>
{% endfor %}
</ul>
As your image tag appears before the excerpt_separator (\n\n = two newlines) it will be in the post excerpt.
2 - Use your post's Yaml front matter to store your image's datas
Post :
---
layout: post
title: Testing images
images:
- url: /assets/img/cypripedium-calceolum.jpg
alt: Cypripedium Calceolum
title: Cypripedium Calceolum
- url: /assets/img/hello-bumblebee.jpg
alt: Hello bumblebee !
title: Hello bumblebee !
---
Intro here yo ! <-- THIS IS THE EXCERPT
Post body begin, and first image not in excerpt
{% assign image = page.images[0] %} <-- first element of the array is zero
{% include image.html image=image %}
Some more text blah !
{% assign image = page.images[1] %}
{% include image.html image=image %}
_includes/image.html (centralized in an include for standardization, but can be in the template) :
<img src="{{ site.baseurl }}{{ include.image.url }}" alt="{{ include.image.alt }}" title="{{ include.image.title }}">
The index page :
<ul class="posts">
{% for post in site.posts %}
<li>
<span class="post-date">{{ post.date | date: "%b %-d, %Y" }}</span>
<a class="post-link" href="{{ post.url | prepend: site.baseurl }}">{{ post.title }}</a>
{{ post.excerpt }}
{% assign image = post.images[0] %}
{% include image.html image=image %}
</li>
{% endfor %}
</ul>

Got it to work. Not sure how it will scale, but this liquid code loops through all the posts and grabs the source for the first image from a post and displays that post. I tested it with multiple images and it works as expected.
<ul>
{% for post in site.posts %}
<li>
{% assign foundImage = 0 %}
{% assign images = post.content | split:"<img " %}
{% for image in images %}
{% if image contains 'src' %}
{% if foundImage == 0 %}
{% assign html = image | split:"/>" | first %}
<img {{ html }} />
{% assign foundImage = 1 %}
{% endif %}
{% endif %}
{% endfor %}
{{ post.title }}
</li>
{% endfor %}
</ul>

If you just need the image URL instead of the whole thing in img tag, you can use the following method.
Install Liquid filter match_regex:
gem install match_regex
Then add it to your Jekyll config:
plugins:
- match_regex
Create a capture snippet in your template:
{% capture post_first_image %}
{% assign hero_image = page.content | match_regex: '<img .*?src="([^"]+)"' %}
{% if hero_image == nil %}
{% assign hero_image = "/placeholder-image.png" | prepend: site_base %}
{% endif %}
{{ hero_image }}
{% endcapture %}
Template:
<meta property="og:image" content="{{ post_first_image | strip }}">
You can simply remove the if condition if you don't need placeholder image.

I've taken David's answer and found a way to get just the src attribute from the img tag.
{% assign foundImage = 0 %}
{% assign images = post.content | split:"<img " %}
{% for image in images %}
{% if image contains 'src' %}
{% if foundImage == 0 %}
{% assign html = image | split:"/>" | first %}
{% assign tags = html | split:" " %}
{% for tag in tags %}
{% if tag contains 'src' %}
<img {{ tag }} />
{% endif %}
{% endfor %}
{% assign foundImage = 1 %}
{% endif %}
{% endif %}
{% endfor %}

Related

Adding tags to jekyll hyde blog

I am trying to add tags to my blog based on jekyll hyde.
This is what I have in place right now.
_includes/filter_by_tag.html
<div class="message">
Filter by tag:
{% assign all_tags = site.data.tags %}
{% for tag in all_tags %}#{{ tag[1].name }}
{% endfor %}
</div>
_includes/tags_for_page.html
{% assign post = page %}
{% if post.tags.size > 0 %}
{% capture tags_content %}{% if post.tags.size == 1 %}<i class="fa fa-tag"></i>{% else %}<i class="fa fa-tags"></i>{% endif %} {% endcapture %}
{% for post_tag in post.tags %}
{% assign tag = site.data.tags[post_tag] %}
{% if tag %}
{% capture tags_content_temp %}{{ tags_content }}#{{ tag.name }} {% if forloop.last == false %}<!--comma-->{% endif %}{% endcapture %}
{% assign tags_content = tags_content_temp %}
{% endif %}
{% endfor %}
{% else %}
{% assign tags_content = '' %}
{% endif %}
_layouts/blog_by_tag.html
---
layout: default
---
{% assign tag = site.data.tags[page.tag] %}
<div class="page">
<h1 class="page-title">Articles by tag: #{{ tag.name }}</h1>
<div class="message">
All tags:
{% assign all_tags = site.data.tags %}
{% for tag in all_tags %}#{{ tag[1].name }}
{% endfor %}
</div>
<div>
{% if site.tags[page.tag] %}
{% for post in site.tags[page.tag] %}
{{ post.date | date_to_string }} » {{ post.title }}<br>
{% endfor %}
{% else %}
<p>There are no posts for this tag.</p>
{% endif %}
</div>
</div>
_layouts/post.html
comments: true
---
{% include tags_for_page.html %}
<div class="post">
<h1 class="post-title">{{ page.title }}</h1>
<span class="post-date">{{ page.date | date_to_string }} {{ tags_content }}</span>
{% if page.cover_image %}
<img src="{{ page.cover_image }}" alt="{{ page.title }}">
{% endif %}
{{ content }}
</div>
archive.md
title: Blog Archives
{% include filter_by_tag.html %}
{% for post in site.posts %}{{ post.date | date_to_string }} » [ {{ post.title }} ]({{ post.url }})
{% endfor %}
I am not sure where am I going wrong here, I tried following the content from this blog post here, tried replicating the same for my blog but I can't see the tags being generated and displayed.
Not sure where am I going wrong.
Link to my blogs github repo where I am trying to add tags
link to the blog where you can see the blog.
Commit hash where I tried adding the tags
I had missed out the file
_data/tags.yml
whose contents would be like
ansible:
name: ansible
apache:
name: apache
and so on

jekyll/liquid - how to pass variable as an argument to post.title contains statement?

I'm trying to filter results of the loop based on hashtags included in titles. If done manually it works perfectly:
<article class="post-preview">
<a href="{{ post.url | prepend: site.baseurl | replace: '//', '/' }}">
{% if post.title contains "#hashtag" %}
<br>
<div class="float-left">
{% include read-time.html %}
</div>
<h2 class="post-title">{{ post.title }}</h2>
{% if post.subtitle %}
<h3 class="post-subtitle">{{ post.subtitle }}</h3>
{% else %}
<h3 class="post-subtitle">{{ post.excerpt | strip_html | truncatewords: 15 }}</h3>
{% endif %}
</a>
<p class="post-meta float-right">Posted by
{% if post.author %}
{{ post.author }}
{% else %}
{{ site.author }}
{% endif %}
on {{ post.date | date: '%B %d, %Y' }}</p><br>
{% endif %}
</article>
what I'd like to achieve is to be able to use variable instead of string:
e.g.
{% if post.title contains "{{ content }}" %} - (this way it doesn't work)
instead of
{% if post.title contains "#hashtag" %}
Is there a way to pass variable to post.title contains, or any other way to achieve my goal i.e. filtering results of for loop based on the hashtag in the title ?
Simply write the variable name after contains. Example:
{% assign a = "Sample text" %}
{% assign b = "text" %}
{% if a contains b %}
Variable 'a' contains the value of variable 'b'! :)
{% endif %}
In your case, a will be post.title and b will be content.

Add an element on the last Jekyll loop of posts

I'm sure this is simple but cant find the answer.
There is a standard Jekyll/Liquid post iterator. How do i use the {% if %} statement below to put the <hr> element for each post except the last?
<ul class="post-list">
{% for post in site.posts %}
{% if post.url %}
<br>
<li>
<h2>
<a class="post-link" href="{{ post.url | prepend: site.baseurl }}">{{ post.title }}</a>
</h2>
<span class="post-meta">{{ post.date | date: "%b %-d, %Y" }}</span>
</li>
**** {% if post != $last %} ***** How do i do this??
<hr>
{% endif %}
{% endif %}
{% endfor %}
</ul>
Yep, there's an easy solution for this.
Liquid has the forloop object, which can be used inside a loop to access some of its properties.
One of those properties is forloop.last:
Returns true if it's the last iteration of the for loop. Returns false if it is not the last iteration.
In other words, you can just do this:
{% if forloop.last == false %}
<hr>
{% endif %}

How to link post categories in Jekyll

I've got the following code in my index.html for Jekyll. I'm trying to find a way to link the categories associated with each post to the actual post themselves. So, if a post contains the category "travel" I want to click on a link that says "travel" which will bring me to all posts categorized as such.
<ul class="post-list" style="list-style-type: none;">
{% for post in paginator.posts %}
{% unless post.categories contains 'portfolio' %}
<li>
<h3>{{ post.title }}</h3>
<span class="post-meta">{{ post.date | date: "%c" }}</span>
Filed In:
{% unless p.categories == empty %}
{% for categories in post.categories %}
{{ categories }} //problem area
{% endfor %}
{% endunless %}
{{ post.excerpt }} Find out more...<br><br>
</li>
{% endunless %}
{% endfor %}
</ul>
Figured it out. For anyone else wondering how to do the same, first setup a categories.html page in your root directory. This page will list all posts that meet a specific category. It does by turning the category names into named anchor slugs as such <a href="#{{ category | first | remove:' ' }}" and then the preceding code creates the actual named anchor div which displays the post associated with that category. Finally, under the page or section where you want to display the list of categories, you present the final bit of code which links to the named anchor section in your categories.html page.
First piece of code to go into Categories.html:
<h2>Posts by Category</h2>
<ul>
{% for category in site.categories %}
<li><strong>{{ category | first }}</strong></li>
{% if forloop.last %}
{% else %}
{% endif %}
{% endfor %}
</ul>
Second piece of code to go into Categories.html:
{% for category in site.categories %}
<div class="catbloc" id="{{ category | first | remove:' ' }}">
<h2>{{ category | first }}</h2>
<ul>
{% for posts in category %}
{% for post in posts %}
{% if post.url %}
<li>
<a href="{{ post.url }}">
<time>{{ post.date | date: "%B %d, %Y" }}</time> -
{{ post.title }}
</a>
</li>
{% endif %}
{% endfor %}
{% endfor %}
</ul>
</div>
{% endfor %}
Third piece of code to go where you want to display your named anchor linked categories:
Filed In:
{% unless p.categories == empty %}
{% for categories in post.categories %}
{{ categories }}
{% endfor %}
{% endunless %}
Use the following CSS to prevent the sections from displaying prematurely before you click on them:
.catbloc:not(:target){
display: none;
}
Hope this helps!

In my Jekyll include file how do I get the markdown for a post?

Is there a way to get ahold of a posts markdown in an include file?
My include file has this code: {{workingPost.content}}
When I include this in a markdown file I get the HTML. I pass that to another include that expects markdown and instead this is passing HTML.
Is there a way to access the markdown instead of the HTML for the post?
As requested here are the code files. What they do is get a featured post for the right hand side of the site like this. In that link the code is static HTML. I want to update it to make it dynamic via the following code. I already have the first image grabbing code working when it receives markdown. I'd like to use that code to grab the first image from the post here too, but by the time the code gets it the markdown has been turned into HTML.
Include file timely.html
{% assign workingPost = nil %}
{% for page in site.posts %}
{% if page.title == 'We Convert All Dollars To Bitcoin' %}
{% assign workingPost = page %}
{% endif %}
{% endfor %}
<div class="panel panel-default">
<div class="panel-heading text-center">
<h3 class="panel-title">Popular</h3>
</div>
<div class="panel-body">
<a href="{{workingPost.url}}">
{% assign workingPostContent = workingPost.content %}
{% include first-post-image-src2.html param=workingPostContent %}
<img src="{% include first-post-image-src2.html param=workingPostContent %}" alt="{{workingPost.title}}">
<p>{{workingPost.excerpt}}</p>
<p class="btn btn-md btn-success" role="button">READ POST</p>
<br>
<br>
</a>
</div>
</div>
include file first-post-image-src2.html
{% capture result %}
{% assign htmlAgain= 'empty' %}
{% assign foundImageAgain = 0 %}
{% assign imagesAgain = include.param | split:"![" %}
{% for imageAgain in imagesAgain %}
{% if imageAgain contains '](' %}
{% if foundImageAgain == 0 %}
{% assign htmlAgain = imageAgain | split:"](" %}
{% assign htmlAgain = htmlAgain[1] %}
{% assign htmlAgain = htmlAgain | split:")" | first %}
{% assign foundImageAgain = 1 %}
{% endif %}
{% endif %}
{% endfor %}
{%endcapture%}{{site.url}}{{htmlAgain|strip}}
Yes translating from markdown to html is one of the first thinks made when Jekyll build. So, no way to grab the markdown in an include. The only way to bypass this limitation is to do it with a plugin. But it's not the subject.
Now back to you code. It's to complicated and fragile.
Jekyll has all the needed functionalities to do what you want to do. Don't try to do data processing with liquid. Use the tags and filters and you will not have to fear a Gem upgrade that will break your site and bring you to a really difficult debugging.
eg : somewhere in your code, you're processing a string with a | split:"/>" filter that rely on how kramdown is rendering ìmg tag. If one day they decide to remove this useless closing slash, your code will break.
The way you can go : put all the datas in your post, in the simplest form possible, and then use them with simple Jekyll tags and filter.
The idea is to use yaml Front Matter custom variables and Jekyll post or page excerpt functionalities.
In your _config.yml, define a new excerpt separator :
excerpt_separator: "<!-- excerpt end -->" # default is "\n\n" = two new lines
In all your posts :
---
excerpt_image_src: "/images/dollarsToBitcoins.jpg"
excerpt_image_alt: "Bitcoin Bulls converts dollars to bitcoins."
popular : true # I'll explain that latter
---
Bitcoin Bulls customers pay in USD but those dollars are all converted to bitcoin.
<!-- excerpt end -->
Bulls, I'm excited to announce...
In default.html
{% if page.is_post %}
<link rel="alternate" type="application/atom+xml" title="{{ site.name }} — Atom" href="{{ site.url }}/blog/feed.atom" />
<meta property="og:image" content="{{ site.url }}{{ page.excerpt_image_src | strip_newlines }}" />
<meta property="og:description" content="{{page.excerpt}}" />
{% else %}
In _includes/timely.html :
<div class="panel panel-default">
<div class="panel-heading text-center">
<h3 class="panel-title">Popular</h3>
</div>
{% for p in site.posts %}{% if p.popular == true %}
<div class="panel-body">
<a href="{{p.url}}">
<img src="{{p.excerpt_image_src}}" alt="{{excerpt_image_alt}}">
<p>{{p.excerpt}}</p>
<p class="btn btn-md btn-success" role="button">READ POST</p><br><br>
</a>
</div>
{% endif %}{% endfor %}
</div>
Note the {% if p.popular == true %} that filter posts with a front matter variable popular: true.
In _includes/blog-post.html :
<li>
<a href="{{ post.url }}">
<p>{{post.date | date: "%B %d, %Y" }}</p>
<img src="{{post.excerpt_image_src}}" alt="{{post.excerpt_image_alt}}">
<!-- No need to wrap excerpt in <p> tag, Jekyll does it.
If you want to put your own tag :
<div>{{ post.excerpt | strip_html }}</div> -->
{{ post.excerpt }}
<p class="btn btn-md btn-success" role="button">READ POST</p><br><br>
</a>
</li>
In _layouts/post.html :
<h1>{{page.title}}</h1>
<div style="color:#666;">by David Smith on {{page.date | date: "%B %d, %Y" }}</div>
{% if page.excerpt_image_src %}
<p><img src="{{page.excerpt_image_src}}" alt="{{page.excerpt_image_alt}}"></p>
{% endif %}
{{ page.content | remove: page.excerpt | markdownify }}
<br>
As you're not actually displaying post excerpt in post page, it's {{ page.content | remove: page.excerpt | markdownify }}. If you want to display excerpt it's : {{ page.content | markdownify }}
I don't know why but page.content return mardown and not html, so the filter | markdownify to transform markdown to html.
Et voila ! Long live Bitcoinbulls !
The markdown isn't available. It is rendered earlier and not available.
From this answer it sounds like the markdown isn't available.
In my case I made my first-post-image-src2.html include handle the case where it gets markdown or HTML like this:
{% capture result %}
{% assign htmlAgain= 'empty' %}
{% assign foundImageAgain = 0 %}
{% if include.param contains '![' %}
{% assign imagesAgain = include.param | split:"![" %}
{% for imageAgain in imagesAgain %}
{% if imageAgain contains '](' %}
{% if foundImageAgain == 0 %}
{% assign htmlAgain = imageAgain | split:"](" %}
{% assign htmlAgain = htmlAgain[1] %}
{% assign htmlAgain = htmlAgain | split:")" | first %}
{% assign foundImageAgain = 1 %}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% if foundImageAgain ==0 %}
{% assign imagesAgain = include.param | split:"<img" %}
{% for imageAgain in imagesAgain %}
{% if imageAgain contains 'src="' %}
{% if foundImageAgain == 0 %}
{% assign htmlAgain = imageAgain | split:'src="' %}
{% assign htmlAgain = htmlAgain[1] %}
{% assign htmlAgain = htmlAgain | split:'"' | first %}
{% assign foundImageAgain = 1 %}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{%endcapture%}{{site.url}}{{htmlAgain|strip}}