1

I'm making a portfolio website and want to make a hero section with transitioning images using SetInterval every few seconds. I tried this:

Do note that I placed my background image in CSS instead, not as an img in HTML.

var images = [
'https://via.placeholder.com/728x90.png?text=slide1',
'https://via.placeholder.com/728x90.png?text=slide2',
'https://via.placeholder.com/728x90.png?text=slide3',
'https://via.placeholder.com/728x90.png?text=slide4',
];
var container = document.getElementById('hero');
var i = 0;
setInterval(function() {
  var newImg = "url('" + images[i] + "')"
//  console.log(newImg);
  container.style.background = newImg;
  i = i + 1;
  if (i == images.length) {
    i = 0;
  }
}, 1000);
#hero {
  height: 100vh;
  width: 100%;
  background: linear-gradient(rgba(0, 0, 0, 0.342), rgba(0, 0, 0, 0.342)), url('https://via.placeholder.com/728x90.png?text=slide1') fixed no-repeat center / cover;
  color: #fff9ff;
  transition: opacity 1s;
  opacity: 1;
}
<section id="hero">
  <div class="heroContainer">
    <div id="info">
      <h1>Lorem Ipsum</h1>
      <h2>Lorem ipsum.</h2>
      <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Reiciendis nesciunt excepturi quos obcaecati incidunt voluptatem ipsam sunt ipsum, autem deleniti cupiditate molestias quis unde quae totam porro dicta</p>
      <a id="heroButton" href="#">Contact Me</a>
    </div>
  </div>

The image didn't transition, and I got these error:

url('undefined')

main.js:70 Uncaught TypeError: Cannot set properties of undefined (setting 'background')

Anyone knows why? I would like to continue using SetInterval btw!

rjyehet
  • 11
  • 3
  • I updated your snippet – mplungjan Nov 09 '21 at 16:56
  • container.style.background = newImg; updates the background and clears all the other attributes of the background – mplungjan Nov 09 '21 at 16:59
  • Please [edit] your question and add the text of your error rather than an image of the error's text. – Heretic Monkey Nov 09 '21 at 17:00
  • You may want to transition the `background` property instead of the `opacity`, since you don't change the `opacity` every second. You may also want to change either the transition time or the timeout so that the transition has time to run completely before the next one starts. – Heretic Monkey Nov 09 '21 at 17:06
  • @HereticMonkey i edited the text of the errors in! – rjyehet Nov 09 '21 at 17:06
  • @mplungjan thanks, does that mean I would have to add the other attributes like linear gradient inside SetInterval? Not sure why i receive this error "Uncaught TypeError: Cannot set properties of undefined (setting 'background')" – rjyehet Nov 09 '21 at 17:15
  • 1
    you don't get that error here. That means you execute the code before the element is found. Wrap in `window.addEventListener("load", function() {..... });` and perhaps change classes instead. Then the images can be in the CSS – mplungjan Nov 09 '21 at 17:20
  • Does this answer your question? [Why does jQuery or a DOM method such as getElementById not find the element?](https://stackoverflow.com/questions/14028959/why-does-jquery-or-a-dom-method-such-as-getelementbyid-not-find-the-element) – Heretic Monkey Nov 09 '21 at 17:47

2 Answers2

1

As others have mentioned, you need to make sure that the JavaScript is executed after the DOM has loaded.

I'm updating a CSS variable instead of setting the style. I just think it's more convenient.

I also get rid of the i variable, and instead use Array.prototype.shift() (Remove from last position - returns the removed item), and Array.prototype.push() to just change the position of the images within the array.

As you can tell, it takes a while for the images to load. You can fix this by preloading the images, but I won't cover that here.

It would also be nicer if you had a smoother transition (two images changing opacity).

I also hope that you don't intend to have a one second interval. Perhaps instead change them every 30 seconds:

var images = [
  'https://via.placeholder.com/701x90',
  'https://via.placeholder.com/702x90',
  'https://via.placeholder.com/703x90',
  'https://via.placeholder.com/704x90'
];

window.addEventListener("load", startBackgroundTransition);

function startBackgroundTransition() {
  var container = document.getElementById('hero');

  setInterval(function() {
    container.style.setProperty("--background-image", `url(${images[1]})`);
    images.push(images.shift());
  }, 1000);
}
#hero {
  --background-image: url(https://via.placeholder.com/701x90);
  height: 100vh;
  width: 100%;
  background: 
    linear-gradient(rgba(0, 0, 0, 0.342),
    rgba(0, 0, 0, 0.342)), 
    var(--background-image) fixed no-repeat center / cover;
  color: #fff9ff;
  /*transition: opacity 1s;
  opacity: 1;*/
}
<section id="hero">
  <div class="heroContainer">
    <div id="info">
      <h1>Lorem Ipsum</h1>
      <h2>Lorem ipsum.</h2>
      <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Reiciendis nesciunt excepturi quos obcaecati incidunt voluptatem ipsam sunt ipsum, autem deleniti cupiditate molestias quis unde quae totam porro dicta</p>
      <a id="heroButton" href="#">Contact Me</a>
    </div>
  </div>
Rickard Elimää
  • 7,107
  • 3
  • 14
  • 30
  • Your `startBackgroundTransition` doesn't start a transition, as I mentioned in a comment on the question... It also assigns the result of calling `startBackgroundTransition()` to the window's `load` event, rather than the function itself. It works here because the JavaScript section of Stack Snippets is executed after the DOM loads. – Heretic Monkey Nov 09 '21 at 17:54
  • Good point. My mistake. Changing that. Thanks for the proof reading as well. :) – Rickard Elimää Nov 09 '21 at 18:23
  • thank you so much! I will give it a try later and let you know if all's good. this newbie really appreciates the help :) – rjyehet Nov 10 '21 at 01:39
  • @RickardElimää Hey there :) Finally had the time to try out! I inserted the snippets of codes but somehow I received this error instead "Uncaught TypeError: Cannot read properties of undefined (reading 'setProperty')" Hmm... – rjyehet Nov 11 '21 at 17:56
  • Not weird, because the `container` variable is undefined. This happens because it's declared before the DOM is created. I updated my answer and moved the declaration inside of `startBackgroundTransition()`. – Rickard Elimää Nov 11 '21 at 19:37
  • @RickardElimää got it to work now! thanks for your help :) – rjyehet Nov 12 '21 at 04:31
-1

Why are you using setInterval?

For loops can deal with this:

var images = [
'https://via.placeholder.com/728x90.png?text=slide1',
'https://via.placeholder.com/728x90.png?text=slide2',
'https://via.placeholder.com/728x90.png?text=slide3',
'https://via.placeholder.com/728x90.png?text=slide4' // < un-needed comma
];
var container = document.getElementById('hero'); // Make sure you call the script after the element

for (var i = 0; i < images.length; i++) {

  // No need for quote marks in css "url()"

  var newImg = "url(" + images[i] + ")";
  container.style.background = newImg;

  if (i >= images.length) {
    i = 0;
  }
}

Or while loops:

var images = [
'https://via.placeholder.com/728x90.png?text=slide1',
'https://via.placeholder.com/728x90.png?text=slide2',
'https://via.placeholder.com/728x90.png?text=slide3',
'https://via.placeholder.com/728x90.png?text=slide4' // < un-needed comma
];
var container = document.getElementById('hero'); // Make sure you call the script after the element
let i = 0;
do {
  // No need for quote marks in css "url()"
  var newImg = "url(" + images[i] + ")";
  container.style.background = newImg;
  if (i >= images.length) {
    i = 0;
  }
} while (i < images.length);

jQuery:

var images = [
'https://via.placeholder.com/728x90.png?text=slide1',
'https://via.placeholder.com/728x90.png?text=slide2',
'https://via.placeholder.com/728x90.png?text=slide3',
'https://via.placeholder.com/728x90.png?text=slide4' // < un-needed comma
];
var container = $("#hero");

for (var i = 0; i < images.length; i++) {
  var newImg = "url(" + images[i] + ")";
  container.css("background", newImg);

  if (i >= images.length) {
    i = 0;
  }
}

jQuery while loop:

var images = [
'https://via.placeholder.com/728x90.png?text=slide1',
'https://via.placeholder.com/728x90.png?text=slide2',
'https://via.placeholder.com/728x90.png?text=slide3',
'https://via.placeholder.com/728x90.png?text=slide4' // < un-needed comma
];
var container = $("#hero"); // Make sure you call the script after the element
let i = 0;
do {
  var newImg = "url(" + images[i] + ")";
  container.css("background", newImg);
  if (i >= images.length) {
    i = 0;
  }
} while (i < images.length);

You also had quotes around the css "url()" value.

You also shouldn't change i back to 0 after the process is done, because this will lead into an infinite loop.

Edit: You said you're using setInterval for 1s delays.

You can put a setTimeout in the for loop:

JavaScript
var images = [
'https://via.placeholder.com/728x90.png?text=slide1',
'https://via.placeholder.com/728x90.png?text=slide2',
'https://via.placeholder.com/728x90.png?text=slide3',
'https://via.placeholder.com/728x90.png?text=slide4'
];
var container = document.getElementById('hero');
for (var i = 0; i < images.length; i++) {
  setTimeout(function() {
    var newImg = "url(" + images[i] + ")";
    container.style.background = newImg;

    if (i >= images.length) {
      i = 0;
    }
  }, 1000); // 1000ms -> 1s
}
Parking Master
  • 551
  • 1
  • 4
  • 20
  • 1
    Where is the constant transition between the images with a one sec delay? – Rickard Elimää Nov 09 '21 at 17:39
  • Your "jQuery" examples wouldn't work. jQuery objects don't have `style` properties. You'd have to use `.css('background', newImg)`. – Heretic Monkey Nov 09 '21 at 17:49
  • No empty value is added to the array; the trailing comma is allowed syntax in modern ECMAScript. Quotes around the parameter passed to the CSS `url` function are perfectly valid, and indeed required in some cases. The OP wants an infinite loop. Loops make no sense when you want a pause between each occurrence. – Heretic Monkey Nov 09 '21 at 17:59
  • thanks for your input!! i'm using setinterval as this is part of my school project (so using setinterval would help to garner more marks haha) – rjyehet Nov 10 '21 at 01:39
  • You could put a `setTimeout` inside the for loop for 1000ms (1s) delays – Parking Master Nov 10 '21 at 15:35