function altTextToImageCaption() {
let imgs = document.getElementsByTagName('img');
for (var i = 0; i < imgs.length; i++) {
var att = imgs[i].attributes.getNamedItem('alt');
if (!att) continue;
var alt = att.value;
if (!alt) continue;
if (!alt.startsWith('Fig ')) continue;
var cap = document.createElement('div');
cap.setAttribute('class', 'imageCaption');
cap.appendChild(document.createTextNode(alt));
document.body.insertBefore(cap, imgs[i].nextSibling);
}
}
<body onload="altTextToImageCaption()">
<h1>Image Caption No Workie</h1>
<img src="https://www.easyrgb.com/look/home_logo.png" alt="Fig 1. First caption.">
<div>
<img src="https://www.easyrgb.com/look/home_logo.png" alt="Fig 2. Second caption.">
</div>
</body>
I'm trying to come up with a jscript function which inserts a caption text underneath all images in an html document which contain an alt
attribute which starts with "Fig ". Seems to work except when the img
node is inside a div
or span
. In that case there's an exception:
Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node."
I'd suggest changing the line:
document.body.insertBefore(cap, imgs[i].nextSibling);
to:
imgs[i].parentNode.insertBefore(cap, imgs[i].nextSibling);
This ensures that the <div>
is always appended after the <img>
itself, regardless of where it appears in the DOM:
function altTextToImageCaption() {
let imgs = document.getElementsByTagName('img');
for (var i = 0; i < imgs.length; i++) {
var att = imgs[i].attributes.getNamedItem('alt');
if (!att) continue;
var alt = att.value;
if (!alt) continue;
if (!alt.startsWith('Fig ')) continue;
var cap = document.createElement('div');
cap.setAttribute('class', 'imageCaption');
cap.appendChild(document.createTextNode(alt));
imgs[i].parentNode.insertBefore(cap, imgs[i].nextSibling);
}
}
<body onload="altTextToImageCaption()">
<h1>Image Caption No Workie</h1>
<img src="https://www.easyrgb.com/look/home_logo.png" alt="Fig 1. First caption.">
<div>
<img src="https://www.easyrgb.com/look/home_logo.png" alt="Fig 2. Second caption.">
</div>
</body>
However, while that solves the problem I'd also completely rewrite your function to the following – and also use unobtrusive JavaScript – to the following:
// using an Arrow function to create the function as a const:
const altTextToImageCaption = () => {
// using document.querySelectorAll() to retrieve only
// <img> elements that have an 'alt' attribute that starts
// with the string "Fig "
let images = document.querySelectorAll('img[alt^="Fig "]');
// using NodeList.prototype.forEach() to iterate over the
// NodeList of <img> element-nodes:
images.forEach(
// using an anonymous Arrow function:
(img) => {
// retrieving the attribute-value of the 'alt'
// attribute, and removing leading/trailing
// white-space:
let alt = img.alt.trim(),
// creating a <div> element:
div = document.createElement('div');
// using the Element.classList API to add the
// 'imageCaption' class-name:
div.classList.add('imageCaption');
// using ParentNode.append() to append
// the string:
div.append(alt);
// navigating to the <img> element's parent,
// and using ParentNode.insertBefore() to
// insert the new ('div') content before the
// next-sibling of the <img> element:
img.parentNode.insertBefore(div, img.nextSibling);
});
}
// binding the event-handling in JavaScript rather than inline
// event-binding; here we bind the altTextToImageCaption() function
// as the event-handler for the 'DOMContentLoaded' event:
window.addEventListener('DOMContentLoaded', altTextToImageCaption);
<body>
<h1>Image Caption No Workie</h1>
<img src="https://www.easyrgb.com/look/home_logo.png" alt="Fig 1. First caption.">
<div>
<img src="https://www.easyrgb.com/look/home_logo.png" alt="Fig 2. Second caption.">
</div>
</body>
As a belated edit, I'd also suggest – if you wish to pair an <img>
and a caption – that you use a <figure>
element, with a <figcaption>
:
// a simple alias for document.createElement() that also allows properties to be
// passed into the created-element:
const create = (tag, props) => Object.assign(document.createElement(tag), props);
// using an Arrow function to create the function as a const:
const altTextToImageCaption = () => {
// using document.querySelectorAll() to retrieve only
// <img> elements that have an 'alt' attribute that starts
// with the string "Fig "
let images = document.querySelectorAll('img[alt^="Fig "]');
// using NodeList.prototype.forEach() to iterate over the
// NodeList of <img> element-nodes:
images.forEach(
// using an anonymous Arrow function:
(img) => {
// retrieving the content of the 'alt' attribute, removing leading
// and trailing white-space with String.prototype.trim():
let altText = img.getAttribute('alt').trim(),
// creating a <figure> element:
figure = create('figure'),
// creating a <figcaption> element, and passing in the
// text of the 'alt' attribute as the text-content of
// the created element:
figCaption = create('figcaption',{textContent: altText});
// using Element.append() to append the <figcaption> to the
// <figure>:
figure.append(figCaption);
// using Element.before() to insert the created <figure>
// element before the current <img>:
img.before(figure);
// using Element.prepend() to insert the <img> element to
// the <figure> element, as its first-child (moving it from
// its original position):
figure.prepend(img);
});
}
// binding the event-handling in JavaScript rather than inline
// event-binding; here we bind the altTextToImageCaption() function
// as the event-handler for the 'DOMContentLoaded' event:
window.addEventListener('DOMContentLoaded', altTextToImageCaption);
<h1>Image Caption No Workie</h1>
<img src="https://www.easyrgb.com/look/home_logo.png" alt="Fig 1. First caption.">
<div>
<img src="https://www.easyrgb.com/look/home_logo.png" alt="Fig 2. Second caption.">
</div>