Hugo: Automatically Link Headers - permalinks

With Hugo static site generator, is it possible to have it automatically put links around header elements? I see that it does fill in the ID attribute so it can be referenced but I would like to automatically create the link as well, like this:
<a href="/post/cool-blog-post#some-interesting-title">
<h2 id="some-interesting-title">Some Interesting Title</h2>
</a>
I did not see anyway in the documentation to do this and I think it would be helpful for users linking to a particular point in a lengthy post.

As far as I'm aware it's not possible to achieve it out-of-the-box, that is, out-of-the-hugo.
I had this same wish myself; I solved it via jQuery, wrapping all h2's and h3's dynamically with links, generating slugs on the go:
Then I've got a sticky sidebar nav which acts as a way to smooth-scroll between the headings, plus it highlights the current heading:
Today I would code it differently code-wise (probably without jQuery) but I'm quite happy with how it works. I think JS overhead is minimal, especially if coded neatly.

This doesn't seem to be possible out of the box. There are two ways of working around it that I can think of: using JavaScript, as revelt suggested, or using HTML in your markdown.
For example, consider the HTML you supplied above.
<a href="/post/cool-blog-post#some-interesting-title">
<h2 id="some-interesting-title">Some Interesting Title</h2>
</a>
If you put this code directly into a Hugo markdown document, it will produce the kind of link you are looking for. However, this is a pain to type every time, so to reduce your work you can make a shortcode.
In layouts/shortcodes/link-heading.html:
{{ $id := .Get 0 | lower | replaceRE "[^0-9a-z]" "-" | replaceRE "-+" "-" -}}
<a href="#{{ $id }}">
<h2 id="{{ $id }}">{{ .Get 0 }}</h2>
</a>
In your markdown document:
{{< link-heading "Some Interesting Title" >}}
I have left the base URL out here, but you can pass it as a parameter from your markdown document if you want. (Of course, then you have to know what the URL is without having Hugo do it for you, which is not ideal.)
This approach has the disadvantages that you can't use the normal markdown heading syntax, and that you don't get Hugo's built-in resolution of duplicate anchors. But it will get the job done.

Easy enough with javascript, add this snippet before your closing </body> tag:
<script>
(function addHeadingLinks(){
var article = document.getElementById('article');
var headings = article.querySelectorAll('h1, h2, h3, h4, h5, h6');
headings.forEach(function(heading){
if(heading.id){
var a = document.createElement('a');
a.innerHTML = heading.innerHTML;
a.href = '#'+heading.id;
heading.innerHTML = '';
heading.appendChild(a);
}
});
})();
</script>

Related

How to display only part of string with CSS

This feels like something that everyone would say to use JavaScript for, but I'm asking anyway.
Take this DOM as an example:
<p>Check out these links:</p>
foo
bar
baz
...
quux
I'm looking for a way that I could show the last part of the URL that is being pointed to, without using JavaScript, or putting it in the HTML.
I was thinking that I might be able to shorten it down to something like this:
<base href="https://example.com/" />
<p>Check out these links:</p>
<a href="foo" />
<a href="bar" />
<a href="baz" />
...
<a href="quux" />
With the following CSS:
a::after {
contents: attr("href");
}
But, I have other links on the web page that use relative links to link to scripts, styles, other pages on the domain, etc.
I was thinking that I might be able to split the last part of the string after / off and display it, but I'm not sure if strings can be manipulated or even read in CSS at all.
It would be nice if we could control the HTML href attribute using CSS, but I doubt that's possible either.
Does anyone have a better solution?

Data-sly-list is adding white space, causing bugginess

I'm having an issue where an unordered list created by data-sly-list is adding whitespace that isn't represented in the DOM or by any class. If I manually code the list rather than letting data-sly-list handle it, the whitespace isn't added.
 <div class="bullets">
    <ul class="columns unordered-list" id="stateList">
      <div data-sly-unwrap data-sly-list.slidesNode="${resource.listChildren}">
        <div data-sly-unwrap data-sly-list.states="${slidesNode.listChildren}">
          <li data-sly-test="${states.valueMap.flag}">
<sly data-sly-use.htmlpaths="${'htmlpaths.js' # thePath=states.valueMap.path}" data-sly-unwrap>
${states.valueMap.name}
</sly>                    
</li>
        </div>
    </div>
    </ul>
</div>
If I hardcode the list like the following, there's no whitespace
  <div class="bullets">
    <ul class="columns unordered-list" id="stateList">
<li>Accessibility   
</li>
<li>Accessibility    
</li>
<li>Accessibility     
</li>
<li>Accessibility     
</li>
    </ul>
</div>
There's also a htmlpaths.js involved:
"use strict";
use(function() {
var path = this.thePath;
var httpRegex = /http/;
    var hashRegex = /#/;
    if (path !== undefined && (httpRegex.test(path) === false && hashRegex.test(path) === false)){
       path = path + '.html';
    }
return {
href: path
}
});
The only difference I see is that its run through Sightly iterating. Is there any fix to this? In addition to listing I'm trying to break them into columns with the following CSS
li {
width:25%;
float:left;
display:inline;
}
This works perfectly fine on the hardcoded list, but on the Sightly iterated one it creates all kind of weird spacing issues that change based on screen width
This whitespace isn't accounted for at all in the DOM. I'm not sure what to do.
More weirdness:
If the margin top is set to -9 or higher, it looks like the above screenshot. But if its set to -10 or lower, it looks like this
It's like its a breakpoint, it goes from one extreme to the other on that one pixel change. No change otherwise. It's bizarre.
It's a little weird behavior in sightly, when you have some extra spaces in your HTML code, it will display with extra spaces in the HTML.
Try to remove all the spaces in the HTML as shown below and try it.
 <div class="bullets"><ul class="columns unordered-list" id="stateList"><sly data-sly-list.slidesNode="${resource.listChildren}"><sly data-sly-list.states="${slidesNode.listChildren}"><li>${states.valueMap.name}</li></sly></sly></ul></div>
You can use HTML formatter in your IDE or online tools like below to format the HTML for a readable format
https://www.freeformatter.com/html-formatter.html.
<div class="bullets">
<ul class="columns unordered-list" id="stateList">
<sly data-sly-list.slidesNode="${resource.listChildren}">
<sly data-sly-list.states="${slidesNode.listChildren}">
<li>${states.valueMap.name}</li>
</sly>
</sly>
</ul>
</div>
This should get rid of the extra spaces in your HTML.
Also, it is best to use sightly tags wherever we need some conditions to check or embed them directly in the actual div tag or html tags instead of using data-sly-unwrap.
You can also use sling models to get the required data and check all the conditions(including appending html) in the backend and send the data just to display and avoid all the conditions in sightly.
Using data-sly-unwrap or a sly tag still adds an empty line in the generated HTML. Even though most browsers ignore those spaces, they might cause issues in some cases. If you want the HTL output to look similar to your hardcoded HTML, try placing the use statement and anchor tag in a single line as shown below.
<div class="bullets">
    <ul class="columns unordered-list" id="stateList" data-sly-list.slidesNode="${resource.listChildren}">
       <li data-sly-repeat.states="${slidesNode.listChildren}" data-sly-test="${states.valueMap.flag}"><sly data-sly-use.htmlpaths="${'htmlpaths.js' # thePath=states.valueMap.path}">${states.valueMap.name} </sly></li>
    </ul>
</div>
Also, a few tips
The sly tag doesn't need a data-sly-unwrap. It is automatically
removed in the generated HTML.
data-sly-list can be added to the parent ul tag itself instead of introducing an extra div tag and then unwrapping it.
Use data-sly-repeat instead of data-sly-list wherever possible. I was able to bring down the generated HTML of one of our complex pages from 20k lines to 12k lines, as data-sly-repeat doesn't introduce additional white spaces.
Solution
The issue is on line 7 of your HTL template:
${states.valueMap.name}
You have a space at the end of the inner HTML of your tag ;)
Unrelated
Regarding your htmlpaths.js script, are you aware of Transformers in AEM? You can use them to implement a global Link Rewriter which will fix links when a page is rendered, much like your script does. You can see an example here: https://helpx.adobe.com/experience-manager/using/aem63_link_rewriter.html
If you decide to keep htmlpaths.js, you may want to review it because I'm afraid there might be some problems with it. Of course, I don't know your requirement so it's just a suggestion :)

what is the correct way to code incoming links for SEO?

our site is giving out 'badges' to our authors. they can post these on their personal blogs and they will serve as incoming links to our site.
We want to give out the best possible code for SEO without doing anything that would get us flagged.
i would like to know what you're thoughts are on the following snippet of code and if anyone has any DEFINITE advice on dos and donts with it. Also, let me know if any of it is redundant or not worth it for SEO purposes.
i've kept the css inline since some of the writers would not have access to add link to external css
i've changed the real values, but title, alt etc would be descriptive keywords similar to our page titles etc (no overloading keywords or any of that)
<div id="writer" style="width:100px;height:50px;>
<h1><strong style="float:left;text-indent:-9999px;overflow:hidden;margin:0;padding:0;">articles on x,y,z</strong>
<a href="http://www.site.com/link-to-author" title="site description">
<img style="border:none" src="http://www.site.com/images/badge.png" alt="description of articles" title="View my published work on site.com"/>
</a>
</h1></div>
thanks
Using H1 to enclose your "badge" is a really bad idea—not in so much as it'll negatively affect SEO for your site, but it will very likely ruin the accessibility (and thus SEO) of the author site. H1-H6 are used to provide document structure by semantically delimiting document headings. Random use of heading tags can confuse screen readers and webcrawlers. There's not much you can do in terms of legitimate SEO aside from making correct use of semantic HTML markup.
Edit:
Something like this would be the safest bet:
<div id="writer-badge" style="width: 100px; height: 50px;">
<strong>
Articles on x,y,z
</strong>
<br />
<a href="..." title="site description" rel="profile">
<img style="border: none" src="..." alt="..."
longdesc="http://site.com/badges-explained"
/>
</a>
</div>
I put a line-break between the text and image to treat the text as sort of a badge title. If it's not meant to be displayed that way, then I would omit the <strong> tags altogether (there's no semantic value in encapsulating the text that way, and any styling could be done using the DIV or a weight-neutral SPAN element).
IMO there's really no reason for a achievement badge to have a heading of its own (it's really not even part of the document, just a flourish in the layout), but if you absolutely must, then H6 would be more appropriate and safer to use than H1.
As far as keyword proximity, that is sorta venturing into the grey-hat area of SEO (similar to keyword stuffing), and I wouldn't know anything about that. I've yet to come across any reliable info on how Google or other search engines treat keyword placement. I think if you properly use tag attributes like alt, title, longdesc, rel, rev, etc. in images and links, you'll be alright.
I don't think there is any issue with this code except your <h1> tag. I would probably change it to <h2> simply because pages are supposed to have only 1 <h1> tag per page.
You could also use an iFrame instead if you wanted. That is what SO does but I know you will not get as much linky goodness.

Can I define a class name on paragraph using Markdown?

Can I define a class name on paragraph using Markdown? If so, how?
Dupe: How do I set an HTML class attribute in Markdown?
Natively? No. But...
No, Markdown's syntax can't. You can set ID values with Markdown Extra through.
You can use regular HTML if you like, and add the attribute markdown="1" to continue markdown-conversion within the HTML element. This requires Markdown Extra though.
<p class='specialParagraph' markdown='1'>
**Another paragraph** which allows *Markdown* within it.
</p>
Possible Solution: (Untested and intended for <blockquote>)
I found the following online:
Function
function _DoBlockQuotes_callback($matches) {
...cut...
//add id and class details...
$id = $class = '';
if(preg_match_all('/\{(?:([#.][-_:a-zA-Z0-9 ]+)+)\}/',$bq,$matches)) {
foreach ($matches[1] as $match) {
if($match[0]=='#') $type = 'id';
else $type = 'class';
${$type} = ' '.$type.'="'.trim($match,'.# ').'"';
}
foreach ($matches[0] as $match) {
$bq = str_replace($match,'',$bq);
}
}
return _HashBlock(
"<blockquote{$id}{$class}>\n$bq\n</blockquote>"
) . "\n\n";
}
Markdown
>{.className}{#id}This is the blockquote
Result
<blockquote id="id" class="className">
<p>This is the blockquote</p>
</blockquote>
Raw HTML is actually perfectly valid in markdown. For instance:
Normal *markdown* paragraph.
<p class="myclass">This paragraph has a class "myclass"</p>
Just make sure the HTML is not inside a code block.
If your environment is JavaScript, use markdown-it along with the plugin markdown-it-attrs:
const md = require('markdown-it')();
const attrs = require('markdown-it-attrs');
md.use(attrs);
const src = 'paragraph {.className #id and=attributes}';
// render
let res = md.render(src);
console.log(res);
Output
<p class="className" id="id" and="attributes">paragraph</p>
jsfiddle
Note: Be aware of the security aspect when allowing attributes in your markdown!
Disclaimer, I'm the author of markdown-it-attrs.
If your flavour of markdown is kramdown, then you can set css class like this:
{:.nameofclass}
paragraph is here
Then in you css file, you set the css like this:
.nameofclass{
color: #000;
}
Markdown should have this capability, but it doesn't. Instead, you're stuck with language-specific Markdown supersets:
PHP: Markdown Extra
Ruby: Kramdown, Maruku
But if you need to abide by true Markdown syntax, you're stuck with inserting raw HTML, which is less ideal.
Here is a working example for kramdown following #Yarin's answer.
A simple paragraph with a class attribute.
{:.yourClass}
Reference: https://kramdown.gettalong.org/syntax.html#inline-attribute-lists
As mentioned above markdown itself leaves you hanging on this. However, depending on the implementation there are some workarounds:
At least one version of MD considers <div> to be a block level tag but <DIV> is just text. All broswers however are case insensitive. This allows you to keep the syntax simplicity of MD, at the cost of adding div container tags.
So the following is a workaround:
<DIV class=foo>
Paragraphs here inherit class foo from above.
</div>
The downside of this is that the output code has <p> tags wrapping the <div> lines (both of them, the first because it's not and the second because it doesn't match. No browser fusses about this that I've found, but the code won't validate. MD tends to put in spare <p> tags anyway.
Several versions of markdown implement the convention <tag markdown="1"> in which case MD will do the normal processing inside the tag. The above example becomes:
<div markdown="1" class=foo>
Paragraphs here inherit class foo from above.
</div>
The current version of Fletcher's MultiMarkdown allows attributes to follow the link if using referenced links.
In slim markdown use this:
markdown:
{:.cool-heading}
#Some Title
Translates to:
<h1 class="cool-heading">Some Title</h1>
It should also be mentioned that <span> tags allow inside them -- block-level items negate MD natively inside them unless you configure them not to do so, but in-line styles natively allow MD within them. As such, I often do something akin to...
This is a superfluous paragraph thing.
<span class="class-red">And thus I delve into my topic, Lorem ipsum lollipop bubblegum.</span>
And thus with that I conclude.
I am not 100% sure if this is universal but seems to be the case in all MD editors I've used.
If you just need a selector for Javascript purposes (like I did), you might just want to use a href attribute instead of a class or id:
Just do this:
Link
Markdown will not ignore or remove the href attribute like it does with classes and ids.
So in your Javascript or jQuery you can then do:
$('a[href$="foo"]').click(function(event) {
... do your thing ...
event.preventDefault();
});
At least this works in my version of Markdown...

How does CSS formatting in a Google Maps bubble work?

I'm using KML and the GGeoXml object to overlay some shapes on an embedded Google map. The placemarks in the KML file have some custom descriptive information that shows up in the balloons.
<Placemark>
<name />
<description>
<![CDATA[
<div class="MapPopup">
<h6>Concession</h6>
<h4>~Name~</h4>
<p>Description goes here</p>
<a class="Button GoRight FloatRight" href="#"><span></span>View details</a>
</div>
]]>
</description>
<styleUrl>#masterPolyStyle</styleUrl>
...Placemarks go here ...
</Placemark>
So far so good - the popups show up and have the correct text in them. Here's the weird thing: I'm trying to use CSS to format what goes in the popups, and it halfway works.
Specifically:
The <h6> and <h4> elements are rendered using the colors and background images I've specified in my stylesheet.
Everything shows up in Arial, not in the font I've specified in my CSS.
The class names seem to be ignored (e.g. none of the a.Button formatting is applied; if I define a style like the one below, it's ignored.)
div.MapPopup { background:pink; }
Any ideas? I wouldn't have been surprised for the CSS not to work at all, but it's weird that it only partly works.
Update
Here's a screenshot to better illustrate this. I've reproduced the <div class="MapPopup"> markup further down on the page (in yellow), to show how it should be rendered according to my CSS.
As suggested I've gone in with Firebug to see what's going on. It looks like Google is doing two obnoxious things:
It's stripping out all class attributes from my HTML.
It's throwing all kinds of hard-coded styles around.
Here's my HTML along with the first couple of wrappers inserted by Google:
<div style="font-family: Arial,sans-serif; font-size: small;">
<div id="iw_kml">
<div>
<h6>Concession</h6>
<h4>BOIS KASSA 1108000 (Mobola-Mbondo)</h4>
<p>
Description goes here</p>
<a target="_blank"><span />View details </a>
</div>
</div>
</div>
As you can see, my classes (e.g. MapPopup in my first div, Button etc. in the <a> tag) have all been stripped out.
Knowing this I'll be able to work around Google's interference, using !important and targeting the container div for the whole map - still, this is annoying, and unexpectedly clumsy coming from Google.
More related obnoxiousness related to the HTML in a KML <description> block: Any links are given the attribute target="_blank", whether you like it or not. I'm currently exploring ways to undo that, using jQuery, but what a drag. I really don't understand why Google feels the need to tamper with this HTML.
See also this thread on the official Google Group.
I've had similar issues. I don't know how you are implementing your Marker, or if you are using InfoWindow, or .addListener, but they way I have had to get css styling to work inside of the "pop up bubble" (over the Marker) is to use what is called "inline styling." So I have a variable that I pass into InfoWindow. Assuming you have initialized a variable "marker" with some options, and have the "map" instance created, some example code would look like this:
/*start of myHtml2 variable*/
var myHtml2 = "<div style=\"background-color:lightgray\"><div style=\"padding:5px\"><div
style=\"font-size:1.25em\">Some text</div><div>Some more text<br/>
Yet more text<br/></div><table style=\"padding:5px\"><tr><td><img src=\"A lake.jpg\"
width=\"75px\" height=\"50px\"></td><td>More text<br/>Again, more text<br/><div
style=\"font-size:.7em\">Last text</div></td></tr></table></div></div>"
/*end of variable*/
var infowindow2 = new google.maps.InfoWindow({content: myHtml2});
/*mouseover could be 'click', etc.*/
google.maps.event.addListener(marker, 'mouseover', function(){
infowindow2.open(map, marker);
});
I know the css styling code is cumbersome, but I haven't found a way to use complicated css styling inside "the bubble pop up" using css in the head, or from a style sheet There are always conflicts, and some features don't render properly.
My first guess is that you're running into an issue with CSS specificity. There is a good article on it at http://www.smashingmagazine.com/2007/07/27/css-specificity-things-you-should-know/, so if you can include a container element ID, that may help.
Let me know if this doesn't turn out to be the problem and I'll come up with more ideas.

Resources