When you browse site like http://github.com and http://readthedocs.org, the documents hosted there have the nice property that headers of paragraphs reveal a small permalink icon on hovering. Unfortunately, though many other sites do have ids in headers, permalinks to said #id are sometimes not provided or at least hidden elsewhere. As an example: http://pandoc.org/MANUAL.html#extension-yaml_metadata_block. So what I would like to achieve is to automatically obtain github/rtd-ish on-hover permalinks on websites that by default don't provide them.
Can this be achieved via stylish alone or does this involve a userscript? Or better yet, has someone already implemented it?
A simple attempt would have been e.g.
h4:after { content: "¶" ; }
but that is literally rendered as ¶ instead of an actual link, i.e. content cannot contain html tags. So something more complicated seems necessary, especially considering not all headers have an id and some site is <a name="id"> instead...
This would need a userscript; Stylish is purely for CSS and, as you said, this can't be done with pure CSS because we need to add some extra HTML.
Userscripts, on the other hand, allow you to add custom JavaScript to the page, so you'd need to loop through all the h4 elements in the page and append <a href="#" + id>¶</a> to them. Something like (if you //#require jQuery):
$('h4').each(function() {
var id = $(this).attr('id');
if (id) { //make sure the element has an id
$(this).append($('<a/>', {
href: '#' + $(this).attr('id'),
text: '¶'
}));
}
});
Also, in HTML5, the name attribute doesn't exist for a elements anymore, and you're supposed to just use id and link to them with #id.
Related
Problem
I have a collection of images with linked captions on a page. I want them each to have identical HTML.
Typically, i copy and paste the HTML over and over for each item. The problem is, if i want to tweak the HTML, i have to do it for all of them. It's time-consuming, and there's risk of mistakes.
Quick and Dirty Templating
I'd like to write just one copy of the HTML, list the content items as plain text, and on page-render the HTML would get automatically repeated for each content-item.
HTML
<p><img src=IMAGE-URL>
<br>
<a target='_blank' href=LINK-URL>CAPTION</a></p>
Content List
IMAGE-URL, LINK-URL, CAPTION
/data/khang.jpg, https://khangssite.com, Khang Le
/data/sam.jpg, https://samssite.com, Sam Smith
/data/joy.jpg, https://joyssite.com, Joy Jones
/data/sue.jpg, https://suessite.com, Sue Sneed
/data/dog.jpg, https://dogssite.com, Brown Dog
/data/cat.jpg, https://catssite.com, Black Cat
Single Item
Ideally, i could put the plain-text content for a single item anywhere on a page, with some kind of identifier to indicate which HTML template to use (similar to classes with CSS).
TEMPLATE=MyTemplate1, IMAGE-URL=khang.jpg, LINK-URL=https://khangssite.com, CAPTION=Khang Le
Implementation
Templating systems are widely used, like Django and Smarty on the server side, and Mustache on the client side. This question seeks a simple, single-file template solution, without using external libs.
I want to achieve this without a framework, library, etc. I'd like to put the HTML and content-list in the same .html file.
Definitely no database. It should be quick and simple to set it up within a page, without installing or configuring additional services.
Ideally, i'd like to do this without javascript, but that's not a strict requirement. If there's javascript, it should be ignorant of the fieldnames. Ideally, very short and simple. No jquery please.
you mean Template literals (Template strings) ?
const arrData =
[ { img: '/data/khang.jpg', link: 'https://khangssite.com', txt: 'Khang Le' }
, { img: '/data/sam.jpg', link: 'https://samssite.com', txt: 'Sam Smith' }
, { img: '/data/joy.jpg', link: 'https://joyssite.com', txt: 'Joy Jones' }
, { img: '/data/sue.jpg', link: 'https://suessite.com', txt: 'Sue Sneed' }
, { img: '/data/dog.jpg', link: 'https://dogssite.com', txt: 'Brown Dog' }
, { img: '/data/cat.jpg', link: 'https://catssite.com', txt: 'Black Cat' }
]
const myObj = document.querySelector('#my-div')
arrData.forEach(({ img, link, txt }) =>
{
myObj.innerHTML += `
<p>
<img src="${img}">
<br>
<a target='_blank' href="${link}">${txt}</a>
</p>`
});
<div id="my-div"></div>
This answer is a complete solution. It's exciting to edit the HTML template in codepen and watch the layout of each copy change in real time -- similar to the experience of editing a CSS class and watching the live changes.
Here's the code, followed by explanation.
HTML
<span id="template-container"></span>
<div hidden id="template-data">
IMG,, LINK,, CAPTION
https://www.referenseo.com/wp-content/uploads/2019/03/image-attractive.jpg,, khangssite.com,, Khang Le
https://i.redd.it/jeuusd992wd41.jpg,, suessite.com,, Sue Sneed
https://picsum.photos/536/354,, catssite.com,, Black Cat
</div>
<template id="art-template">
<span class="art-item">
<p>
<a href="${LINK}" target="_blank">
<img src="${IMG}" alt="" />
<br>
${CAPTION}
</a>
</p>
</span>
</template>
Javascript
window.onload = function LoadTemplate() {
// get template data.
let sRawData = document.querySelector("#template-data").innerHTML.trim();
// load header and data into arrays
const headersEnd = sRawData.indexOf("\n");
const headers = sRawData.slice(0, headersEnd).split(",,");
const aRows = sRawData.slice(headersEnd).trim().split("\n");
const data = aRows.map((element) => {
return element.split(",,");
});
// grab template and container
const templateHtml = document.querySelector("template").innerHTML;
const container = document.querySelector("#template-container");
// make html for each record
data.forEach((row) => {
let workingCopy = templateHtml;
// load current record into template
headers.forEach((header, column) => {
let value = row[column].trim();
let placeholder = `\$\{${header.trim()}\}`;
workingCopy = workingCopy.replaceAll(placeholder, value);
});
// append template to page, and loop to next record
container.innerHTML += workingCopy;
});
};
New version on github:
https://github.com/johnaweiss/HTML-Micro-Templating
Requirement
As specified in the question, this solution is intended to optimize the coding experience on the HTML side. That's the whole point of any web templating. Therefore, the JS has to work a little harder to make life easier for the HTML programmer.
The question seeks a reusable solution. Therefore, JS should be ignorant of the template, fields, and data-list. So unlike #MisterJojo's answer, the template and all data are in my HTML, not javascript. The JS code is generic.
Design
My solution is based on the <template> tag, which is intended for precisely this usage. It has various advantages, like the template isn't displayed, processed, or validated by the browser, so it has less impact on performance. Programmer doesn't have to write an explicit display:none style.
https://news.ycombinator.com/item?id=33089975
However, <template> tags are normally only intended for loading content into the layout. That's inadequate. This tool allows template variables anywhere in the HTML, including inside the tags (eg attributes like <img src).
HTML
My HTML has three blocks:
template: The HTML coder develops their desired display-structure of the output, in real HTML (not plain text). Uses <template>
data: The list of records each of which should be rendered using the same template. Uses <span> with a HIDDEN attribute.
container: The place to display all the output blocks. Uses <span>.
Template
My sample template includes 3 placeholders for data:
${LINK}
${IMG}
${CAPTION}
But of course you can use any placeholders, any number of them. I use string-literal delimiting-style (although i'm not actually using them as string-literals -- i just borrowed the delimiter style.)
Data Element
The question specifies data should be stored in HTML. It should require minimal keystrokes.
I didn't want to redundantly retype the fieldnames on every row. I didn't use slotting, JSO, Jason, or XML syntax, because those are all verbose.
https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_templates_and_slots
It's a simple delimited list. I eliminated all braces, brackets, equals, parens, colons etc.
I put the fieldname-headers only on the first row. The headers are a visual aid for the HTML developer, and a key for Javascript to know the fieldnames and order.
Record Delimiter: End-of-line
Field Delimiter: Double-commas. Seems safe, and they're easy to type. I don't expect to see double-commas in any actual data. Beware, the developer must enter a space for any empty cells, to prevent unintended double-commas. The programmer can easily use a different delimiter if they prefer, as long as they update the Javascript. You can use single-commas if you're sure there will be no embedded commas within a cell.
The data block is hidden using the hidden attribute. No CSS needed.
It's a span to ensure it takes up no room on the page.
JAVASCRIPT
Data
The data is processed by Javascript with two split statements, first on newline delimiter, then on the double-comma delimiter. That puts the whole thing into a 2D array. My JS uses trims to get rid of extra whitespace as needed.
Place-holder Substitution
Handling multiple entries requires plugging each entry into the template.
i went with simple string-replacement instead of string literals.
Multiple Templates
New version which supports multiple templates, and ability to use same template in multiple locations on same page.
https://github.com/johnaweiss/HTML-Micro-Templating
Future
Inspired by #MisterJojo, an earlier version of my solution used template literals to do the substitution. However, that was a bit more complicated and verbose, and seemed to require use of eval. So i switched to .replaceAll. Yet template-literals seems like a more appropriate method for templates, so maybe i'll revisit that.
A future version may adapt to whatever custom field-delimiter the HTML developer uses for the data block.
The dollar-curly delimiter for placeholders is a bit awkward to type. So i'm interested in finding a less awkward non-alpha delimiter that won't conflict with HTML. Considering double-brackets or braces [[NAME]]
Maybe there are simpler ways to pull the data-table into JS.
I've read components work well with <template>, but i didn't go there.
Imo, the JS committee should develop a variable-placeholder feature for <template> tags, and natively accommodate storing the data in HTML. It would be great if something like this solution was part of the rendering engine.
This seems like a very basic HTML question, but I cannot find an answer here or elsewhere that actually works.
What I want to do is jump to an id link on the same document without reloading the document.
Here's my setup. The document is http://www.example.com/mydocument.htm/.
<head>
.
<base href="http://www.example.com">
.
.
</head>
<body>
<!-- Jump from ... -->
<div>
Jump to here.
</div>
<!-- Jump to ... -->
<div id="myid">
<Do stuff>
<Do more stuff>
</div>
</body>
This syntax, according to everything I have read on this site and elsewhere, is supposed to result in a jump within the current document without a page reload.
Doesn't work. My browsers (Firefox, Chrome) automatically stick the base href in front of the bookmark, viz: http://www.example.com/#myid, which opens my home page.
Not what I want.
If I change the href from "#myid" to /mydocument.htm#myid, then the jump completes, but the page reloads. Ditto if I use the absolute address: http://www.example.com/mydocument.htm/#myid.
I'm stuck. Any guidance?
The <base> element instructs the browser to append the URL in the href to all relative URLs on the page. So having:
<base href="http://www.example.com" />
Means that for :
here.
The href is handled as :
http://www.example.com/#myid
Instead of
<current_page>/#myid
You almost certainly don't need that <base> element in the head section, especially based on your further point that using the full URL (which also has http://www.example.com in it) works, meaning your page is already at http://www.example.com and thus doesn't need to make it explicit with <base>.
Alternatively (and I don't actually recommend this, because your use of base seems incorrect), you could change the href of your link to be the current page plus the id hash, like:
here.
As the browser will render the URL (when applying the base href) to :
http://www.example.com/mydocument.htm/#myid
and thus not try to leave the current page as it will treat it the same as if the base weren't set. (Note that this would only work when you have the base href set to the URL of the actual page's base, and as I mentioned earlier, that would make the base element unnecessary).
https://jsfiddle.net/ouLmvd3g/
If you are considering a javascript solution, since the <base> is apparently never necessary, I would recommend an event listener that removes the base element from the DOM rather than your suggested :
a fix using an event listener to remove the base URL for local links
A simple solution would be:
window.onload=function(){
var baseElement = document.getElementsByTagName("base")[0];
baseElement.parentNode.removeChild(baseElement);
}
https://jsfiddle.net/vLa0zgmc/
You could even add a bit of logic to check if the base element's href matches the current page's actual URL base, and only remove when it does. Something like:
var baseElements = document.getElementsByTagName("base");
if (baseElements.length > 0) {
var baseElement = baseElements[0];
var current_url = window.location.toString();
var base_url = baseElement.getAttribute("href");
// If the base url and current url overlap, remove base:
if (current_url.indexOf(base_url) === 0) {
baseElement.parentNode.removeChild(baseElement);
}
}
Example here : https://jsfiddle.net/gLeper25/2/
Thanks to all who responded.
In the end it turns out I was asking the wrong questions. What I needed was a means of jumping to an anchor on the same document without the document reloading. Unfortunately I got fixated on the problem with <base> interfering with the normal <a href....> process.
The actual answer was to use onClick instead, and the code was provided by #Davide Bubz in "Make anchor links refer to the current page when using <base>", and it's simple and elegant, using document.location.hash instead of <a href...>:
Anchor
where "test" is the ID identifying the item to be jumped to.
Several responders pointed to this thread as answering my issues, but I was not smart enough to understand its import until I had read it for the third time. Had I been smarter, I would have saved 6-1/2 hours of wasting my time on trying to fix the <base> problem.
Anyway, problem solved. Thanks to all and especially to Mr. Bubz.
Is it possible in CSS (only) to hide some text of a string?
I know there these attribute-selectores like:
[att^=val] – the “begins with” selector
But for instance, having this:
<div class="some_random_text">
This is a default text
</div>
I want to (display: none) only a certain substring - in thise case "default". I know how to do it with JS, but I'm looking for a CSS-solution only (if there is any).
Even though I guess it isn't possible to manipulate the DOM via CSS, which would be neccessary to have something like:
this is a <span class="hideThis">default</span> text
why would you need this and where does it occur?
For instance in a CMS (in my case OXID). You can add a title to a specific payment-method. Here I have
paypal
paypal (provider1)
paypal (another dude)
I want to have only PayPal visible in the frontend. The other PayPal-Paymenttypes have to remain however. Naming them all PayPal just leads to confusion.
there is the content-property. Is it somehow managable with that?
Again, no JS :-)
To answer your question - no, it's not possible using only CSS.
You can;
Edit the HTML as you suggested
this is a <.span class="hideThis">default<.span > text
Use JS to alter the elements innerHTML value
Use a pre-processing language (like PHP or ASP, whatever you are able to use) to reduce the string to a substring.
Sorry if that's not the answer you wanted, but those are your options.
It it not possible. The only thing that can actually modify the inside text is the content property. Assuming something changes in your dom, you can have rules like:
.some_random_text:after {
content: "This is a text";
}
other_select .some_random_text:after {
content: "This is a default text";
}
But sincerely, I don't get the point, as JS and consors are made for that.
It's not possible, here's the documentation on selectors: https://www.w3.org/TR/css3-selectors/#selectors
The product I work on supports users providing custom descriptions in markdown format (this is new, previously they could only provide raw html). Unfortunately many users have been using this product for years and as a result there are many descriptions that consist of markup that "sort of works" or "works in IE8".
I don't particularly care if their descriptions don't render right because they are broken, what I am concerned about is that the rest of the page shouldn't be broken because of it.
Example broken code
<ul>
</li>
<li>foo</li>
<li>bar</li>
</li>
<!-- no closing ul -->
Things I have done to mitigate the effect
I remove the following tags: html head body style frameset frame iframe script markdown-rendered
Surround descriptions with <markdown-rendered> as a way to contain the code.
Even with these mitigations, code like the example above still "breaks out". For the above example, a large amount of markup after it shifts inside the ul. What else can I do to "contain" bad markup?
The moment you inject the invalid markup into the document, it's going to be parsed and repaired to the best of the browser's ability. I would suggest doing this beforehand, and injecting the result of this operation, rather than allowing this operation to potentially disrupt your pre-existing structure.
One way in which libraries and frameworks have done this in the past is to create a bit of temporary structure, assign the invalid markup as the innerHTML, and then read back out the innerHTML:
var markup = clean( "<ul></li><li>foo</li><li>bar</li></li>" );
console.log( markup ); // "<ul><li>foo</li><li>bar</li></ul>"
function clean ( invalid ) {
var container = document.createElement( "div" );
return ( container.innerHTML = invalid ), container.innerHTML;
}
When the markup is assigned, it will be parsed, repaired, and constructed into actual DOM objects. When we read back out the innerHTML, we'll get nice and clean code directly from the browser.
I have been busy making an interactive tab layout using only CSS and I've had someone tell me that this is not the intended semantic meaning of <input> tags. Now, I know that HTML5 focuses a lot more on semantics than previous versions of HTML did, so I was wondering, is something that does something like the following against the input semantics:
<label for="toggleTab" class="togglelabel">Toggle tab</label>
<input type="checkbox" id="toggleTab" class="toggleinput">
<div class="toggletab">Look at this thing hide and show</div>
CSS:
.toggletab, .togglelabel {border:1px solid #AAA;display:block;}
.toggleinput, .toggletab {display:none;}
.toggleinput:checked + .toggletab {display:block;}
(demo)
The standards[1][2] both say the same thing:
The input element represents a typed data field, usually with a form control to allow the user to edit the data.
So to me this seems like this is indeed against what the input tag should be used for (since this has nothing to do with data, but just with displaying certain things to the user or not).
And then of course my followup question would be, if this is indeed not what input tags should be used for according to the standard, is it bad to go against the semantics standards?
Well structurally this will be quite difficult. Inputs need go inside form elements, and if you are going to be using these all over the page, most of your page will be wrapped as one giant form, potentially will nested forms, for search bars, user input and the like.
As your quote says:
The input element represents a typed data field, usually with a form control to allow the user to edit the data.
The control you are suggesting isn't for editing data its for adding graphical user functionality. If you were to bing the input to a form, either by placing the input inline into a form, or using the #form attribute as your comment suggests, what exactly is the semantic of that form? If would have no action, it would have no site to post to, and if an accessibile browser were to try and render the elements of that form in a different way it would have no semantic content.
For the kind of hide/show toggle functionality, I'd recommend instead using an a link and hanging some javascript off that. As stated:
If the a element has an href attribute, then it represents a hyperlink (a hypertext anchor) labeled by its contents.
In this context Hyperlink does not preclude the use of javascript as the acting force on the page, if it did most pages would be non-compliant to the spec. This is backed up by the first type of link suggested in the definition of hyperlink suggests that the link can be "used to augment the current document".
As a side note, in accessible browsers, inputs like these maye be rendered or presented in ways different to what you expect. Each form might be pulled out into a list of possible data editing options, and this would not fit the semantics of what a user might expect.
Semantics & custom behaviour
If the question is about which element has the least implied semantics, I reckon your best bet is the <button type="button"> element: the HTML5 spec describes it as « a button with no additional semantics» which, without scripting, « does nothing » — unlike the <a> element, which has implicit behaviour as a hypertext link and anchor.
The HTML4 forms spec refers to these as 'push buttons', which « …have no default behavior. Each push button may have client-side scripts associated with the element's event attributes ».
Furthermore, the HTML4 spec cites buttons repeatedly in its description of scripts: they are the only element referenced specifically in the introduction to scripts, although inputs also feature in the examples of DOM-event triggered scripts — however, as buttons are non-self-closing, they can contain other DOM nodes, which may make them more flexible depending on your needs.
Non-<input> solution
Using the push button, custom behaviour can be inferred by using data-* attributes for CSS selectors:
.toggleinput[ data-checked ] + .toggletab {
display:block;
}
…and scripted as follows:
// Use Array's forEach to loop through selection
Array.prototype.forEach.call(
// …of all `.toggleinput` elements
document.querySelectorAll( '.toggleinput' ),
function bind( input ){
input.addEventListener( 'click', function toggleCheckedState(){
// `dataset` is an object representation of data-* attributes
if( this.dataset.checked ){
// Remove the attribute if it exists
delete this.dataset.checked;
}
else {
// Declare it if it doesn't
this.dataset.checked = true;
}
}, false );
}
);
Forked code demo