Inject html element inside page.content within a template


#1

In my layout I have {{ page.content }} injecting all of the yaml/md content.
Can I inject an <img> tag after the closing </p> tag. Is this possible?

Let me know if I can explain this clearer. My logic would say:
On the past instance of </p>, insert <img src="{{site.images.endImg}}>

Thanks in advance.


#2

Hey @vsiege, so you pretty much want to inspect page.content and inject <img src="{{site.images.endImg}}"> somewhere in the middle or at the end, depends on where you encounter closing </p> tag…

Can you post here a few examples of page.content (as many as you believe are relevant) to help understand your use case better?

What you’re looking for is related to text parsing and that can get involved very quickly… however, if you define boundaries and accept limitations - in many cases it should be fairly easy to come up with a snippet to solve some of the one-off cases.


#3

While I could copy & paste some content here, unfortunately, the content is written by multiple parties, and building in consistency isn’t possible. In fact, there may be instances where the closing paragraph tag wouldn’t be used. In that case – nothing gets injected. I couldn’t give you a definitive standard that there would only be for example, 3 paragraphs, as this would be limiting to the content writers.

I would think this of it as the inverse to the current replace_first filter by Liquid, but replace_last.

Yes, text parsing or substring manipulation would be what I’m looking for.


#4

Couldn’t you just put the image after the {{ content }} tag in your layout(s)?

There’s no guarantee your post/page content would even end with a paragraph. What if your content writers ends it with an unordered list, a table, or any other valid HTML element.


#5

And if you wanted to drop an image in the middle of the post content you could consider placing some sort of unique string in there… Say <!--image--> then use a filter on content to replace it with image markup.

Or develop a plugin to do this for you.


#6

In regards to this technique, I’m already have it there but it wil end up on it’s own line rather than be a part of the last this in the paragraph (e.g. <img src="{{site.data.images.endImg}}" alt="">)

As for the second technique, it could work, but still requires everyone to enter the information in every *.md file. I’m not familiar enough with Ruby to write a regex for a plugin.


#7

Why is it important that it the image is inside the last paragraph and not on its own line?

This sounds like something easily solved with your layout or CSS to position the image where you want.

Do you have an example of some content? What it outputs as now and how you’d ideally want it to look? Can probably offer better advice with those bits of info.


#8

The CSS is spot on right now. The position of the element in the DOM is what’s needed. In the end, this will represent an end sign.

I think that link should give a good overview it.


#9

@vsiege, thanks for sharing the link - good to know about the concept. However, that gives an overview of end sign in general, and gives no clue about your specific problem - otherwise, you would have it already solved, wouldn’t you? :smile:

You also keep mentioning one phrase more often than not last closing </p>… so lets consider the following {{ content }} (very basic case, I guess): <p>Keep Calm, and Carry On...</p>.

This would work for you:

<p>Keep Calm, and Carry On...</p><img src="assets/images/endimg.png" alt="">

and this wouldn’t work for you:

<p>Keep Calm, and Carry On...</p>\n
<img src="assets/images/endimg.png" alt="">

Did I get it right?


#10

Thanks for reviewing all.

Using your example above but altering it to this:
<p>Keep Calm, and Carry On...<img src="assets/images/endimg.png" alt=""></p>

So now the <img> is a child of the paragraph tag. Now taking it one step further for the full example:
<p>I am paragraph one.</p><p>I am the last paragraph. Keep Calm, and Carry On...<img src="assets/images/endimg.png" alt=""></p>


#11

@vsiege, ahh - that is your twist. Okay, I have a few ideas to try out, however I need your help to gather a small collection of input & output files that can be used to validate against.

Here is what I mean by that:

// input-a.md
Lorem ipsum...
// output-a.html
<p>Lorem ipsum...<img src="assets/images/endimg.png" alt=""></p>

// input-b.md
something...

something...
// output-b.html
<p>something...</p>
<p>something...<img src="assets/images/endimg.png" alt=""></p>

// and etc.

Scramble the original text (if you need to) and publish a Gist with those files - I will use your Gist as a reference to work with and validate against.


#12

I think an even easier approach would be to use CSS.

Assuming your main content has a class name of some kind (let’s call it foo-main), you’d target that and then the :last-child element. I’m pretty sure you could do something like .foo-main > *:last-child to get it, then use ::after to add content (could be text, could be filled with a background image, etc.) directly after the closing </p> or whatever other child element ends up being.

Example CSS:

.foo-main > *:last-child::after {
  content: '...whatever text';
}

or this if you wanted to use an inline image instead:

.foo-main > *:last-child::after {
  content: '';
  display: inline-block;
  height: ??px; // height of your image
  width: ??px; // width of your image
  background: transparent url(whatever-image.jpg) no-repeat;
}

And since you’re using the ::after pseudo selector it would appear inline with the text and not jump down a line like you’re currently experiencing with <p>Text</p><img src="whatever.jpg" alt="">


#13

last-of-type might be even better for you to target the last <p> element in a container.


#14

That one looks very promising :+1:

Sorry, I just couldn’t help myself but try to fix anything with code :smile:


#15

@pabloduo I put together a repo for you. I think this satisfies your request. Thanks for your help in advance. You can do a pull request this way we can keep it public. :grinning:

@mmistakes Your CSS solution is great:ok_hand:t4: – thank you. The reason I’m pushing for a Jekyll filter, is because I want to be able to compile the result as to not carry unnecessary CSS code from page-to-page.

All images get stored as values in JekyllLiquidInsertBeforeLastInstanceOf_2018/src/_data/images.yml. During compilation, I’ll use a Jekyll/Liquid conditional to check against the contributor, select the corresponding image, then inject the appropriate graphic (data:image/svg+xml;base64 embedded in the src attribute of the <img> tag). This replace_last filter will be very handy. :muscle:t4:


#16

@pabloduo @mmistakes I decided to give Ruby a whirl. I completed the first version of the filter and it works! It is case sensitive, so there will be work to do in the future. I fully commented the file (_src/plugins/replaceLastInstanceOf.rb) in my repo as well as added the future string search using a pattern (beyond my knowledge at this point but you can uncomment to see the beginnings of the concept - much more desirable). It would be cool to get it working with .sub. Clone the repo or view on Github.

So you have it:
@usage {{ page.content | replace_last_instance_of: 'search string', 'replacement string' }}


#17

@vsiege thanks for putting it together! I’m looking into it right now… :slight_smile:


#18

@vsiege yay, that’s the spirit! :man_technologist: I will take a look at the sources and give feedback or submit a PR.


#19

@vsiege did some improvements to your original filter and about to submit a PR: https://github.com/VincentVToscano/JekyllLiquidInsertBeforeLastInstanceOf_2018/pull/1.


#20

That’s great. I merged your request into the repo.

When I put the two different filters into my main project, you’re looking at:

1.341 - 1.361 seconds – new version
1.332 - 1.45 seconds – original

I agree, the reverse method will become extremely taxing as the amount of copy/text increases in size over time.

Thanks for all your help (all). :grin: