2

I'm building a Wordpress site, where I've made it so that you can insert fullwidth images with parallax through the CKEditor by assigning the inserted images with a class. The script I've written replaces the found images with a div, and uses the source of the replaced image as background for the div. The rest is done by CSS. My problem is that so far I'm only able to get this to work with the first image that has the class.

I know where my code breaks down – it's when I replace the image with the div.

Here is my code:

if (document.getElementsByClassName('fullwidth-image').length > 0) {
      let pictures = document.getElementsByClassName('fullwidth-image');
      Array.prototype.forEach.call(pictures, function(e){
        let imgUrl = e.src,
        imgWrapper = document.createElement('div');
        imgWrapper.classList.add('fullwidth-container');
        imgWrapper.style.backgroundImage = 'url(' + imgUrl + ')';
        e.replaceWith(imgWrapper);
      });
}

If I comment out

e.replaceWith(imgWrapper);

then

console.log(e);

outputs all the images on the page, as it should. When it's in the code though, only the first image with class="fullwidth-image" gets outputted. How can I write this better?

  • 1
    Please, post a [Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve) – Ele Apr 01 '19 at 16:57
  • As a general rule: don't change the enumerable you're iterating over - unless you know exactly what you're doing ;) – Andreas Apr 01 '19 at 17:00
  • Are you sure that the variable `e` that is passed in is a jQuery object? The `replaceWith` method is exclusive to a jQuery object. – Jimmy Leahy Apr 01 '19 at 17:05
  • [`replaceWith`](https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/replaceWith) is not exclusive to jQuery, however, they *are* different. – Tim Klein Apr 01 '19 at 17:13
  • @TimKlein huh, today I learned! – Jimmy Leahy Apr 01 '19 at 17:14

2 Answers2

0

The returned value from getElementsByClassName is a live HTMLCollection, which means it is automatically updated when the underlying document is changed.

Therefore, your iteration through this "array-like" object is affected whenever you replace one of the elements inside of it.

You can simply create a normal array out of the elements in the collection and iterate over that (which won't get messed up as you iterate through).

let pictureElements = document.getElementsByClassName('fullwidth-image');

const pictures = [...pictureElements]; // Uses object spread syntax

pictures.forEach(function(e) {
  let imgUrl = e.src,
  imgWrapper = document.createElement('div');
  imgWrapper.classList.add('fullwidth-container');
  imgWrapper.style.backgroundImage = 'url(' + imgUrl + ')';
  e.replaceWith(imgWrapper);
});
div.fullwidth-container {
  width: 100%;
  height: 50px;
  border: 1px solid black;
}
<img class="fullwidth-image" id="img1" src="" alt="No Image 1"/>
<img class="fullwidth-image" id="img2" src="" alt="No Image 2"/>
<img class="fullwidth-image" id="img3" src="" alt="No Image 3"/>
<img class="fullwidth-image" id="img4" src="" alt="No Image 4"/>
<img class="fullwidth-image" id="img5" src="" alt="No Image 5"/>

Notice, that all replaced elements should be a <div> with a black border.

Tim Klein
  • 2,538
  • 15
  • 19
0

Try querySelectorAll instead of getElementsByClassName, it worked for me