Alpine JS меняет изображения с интервалом с помощью функции window.setInterval()

Я пытаюсь решить тривиальную задачу с помощью Alpine JS и стандартной функции JS setInterval. Итак, я пытаюсь создать селектор изображений, в котором изображения будут меняться каждую секунду (1000 мс).

Вот что я получил:

<div x-data = "imgFunc()">
        <h1 class = "text-xl py-3">Chosing the picture</h1>

        <div class = "w-1/2">
            <img x-show = "img === 'img1'" src = "{{ asset('images/training/img1.jpg') }}" alt = "Main Image">
            <img x-show = "img === 'img2'" src = "{{ asset('images/training/img2.jpg') }}" alt = "Main Image">
            <img x-show = "img === 'img3'" src = "{{ asset('images/training/img3.jpg') }}" alt = "Main Image">
        </div>

        <div>
            <ul class = "flex">
                <li class = "w-1/6 p-3" :class = "{ 'border' : img==='img1' }"><img src = " {{ asset('images/training/img1.jpg') }} " alt = "image1" @click = "startImg"></li>
                <li class = "w-1/6 p-3" :class = "{ 'border' : img==='img2' }"><img src = " {{ asset('images/training/img2.jpg') }} " alt = "image2" @click = "img='img2'"></li>
                <li class = "w-1/6 p-3" :class = "{ 'border' : img==='img3' }"><img src = " {{ asset('images/training/img3.jpg') }} " alt = "image3" @click = "img='img3'"></li>
            </ul>
        </div>



    </div>



<script>
    function imgFunc(){
            var i = 1;
            var img ='img'+i;
            return {
            i,
            img,
            startImg(){
                var myvar = window.setInterval(function(){
                    this.img = 'img'+this.i;
                    if (this.i<3){
                        this.i++;
                    }else{
                        this.i=1;
                    }    
                }, 1000);
            },
            changeImg() {
                this.img = 'img'+this.i;
                if (this.i<3){
                    this.i++;
                }else{
                    this.i=1;
                }                       
            }

        }
        
    }

</script>

При нажатии на изображение № 1 в списке изображения должны начать меняться, но этого никогда не происходит. Почему?

Я также попытался передать имя функции changeImg() в событии @click, и это сработало отлично, при нажатии на одно изображение менялось одно за другим.

Я также попытался передать имя функции changeImg() в setInerval() следующим образом:

startImg(){
     var myvar = window.setInterval(this.changeImg, 1000);
},

Или вот так:

startImg(){
     var myvar = window.setInterval(changeImg, 1000);
},

Я пытался изменить "это" на "я"

<script>
    function imgFunc(){
            var i = 1;
            var img ='img'+i;
            var self=this;
            return {
            i,
            img,
            startImg(){
                var myvar = window.setInterval(function(){
                    self.img = 'img'+self.i;
                    if (self.i<3){
                        self.i++;
                    }else{
                        self.i=1;
                    }    
                }, 1000);
            },
            changeImg() {
                this.img = 'img'+this.i;
                if (this.i<3){
                    this.i++;
                }else{
                    this.i=1;
                }                       
            }

        }
        
    }

</script>

Но на самом деле ничего не работает.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
0
1 894
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Ваша проблема заключается в использовании function, поэтому this внутри функции не является Alpine.js this. this внутри imgFunc также не является Alpine.js this (быстрое объяснение этого заключается в том, что imgFunc запускается до инициализации Alpine.js для создания начального «состояния», поэтому this — это окно)

Есть 2 способа исправить это: либо использовать this, либо использовать функцию стрелки внутри setInterval:

<script>
    function imgFunc(){
            var i = 1;
            var img ='img'+i;
            return {
            i,
            img,
            startImg(){
                var myvar = window.setInterval(() => {
                    this.img = 'img'+this.i;
                    if (this.i<3){
                        this.i++;
                    }else{
                        this.i=1;
                    }    
                }, 1000);
            },
            changeImg() {
                this.img = 'img'+this.i;
                if (this.i<3){
                    this.i++;
                }else{
                    this.i=1;
                }                       
            }

        }
        
    }

</script>

В качестве альтернативы вы можете установить self = this в функции startImg

<script>
    function imgFunc(){
            var i = 1;
            var img ='img'+i;
            return {
            i,
            img,
            startImg(){
                var self=this;
                var myvar = window.setInterval(function(){
                    self.img = 'img'+self.i;
                    if (self.i<3){
                        self.i++;
                    }else{
                        self.i=1;
                    }    
                }, 1000);
            },
            changeImg() {
                this.img = 'img'+this.i;
                if (this.i<3){
                    this.i++;
                }else{
                    this.i=1;
                }                       
            }

        }
        
    }

</script>

Есть еще один способ сделать это — использовать .bind в обратном вызове setInterval, но это более многословно и сложно.

Большое спасибо!!! работал как шарм! Я знаю, что что-то не так с видимостью, но я не мог понять, что именно...

IVN 10.12.2020 14:27

Как упомянул Хьюго, контекст this не разрешается во внешнюю функцию, поэтому вам нужно использовать функцию стрелки, поскольку у нее нет контекста.

Я внес несколько изменений, чтобы использовать alpine.js для того, для чего он создан.

<link href = "https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.0.1/tailwind.min.css" rel = "stylesheet" />
<script src = "https://cdnjs.cloudflare.com/ajax/libs/alpinejs/2.7.3/alpine.js"></script>
<div x-data = "imgFunc()">
  <h1 class = "text-xl py-3">Chosing the picture</h1>

  <div class = "w-1/2">
    <img :src = "currentImage" alt = "Main Image">
  </div>

  <div>
    <ul class = "flex">
      <template x-for='(image,index) in images'>
        <li class = "w-1/6 p-3" :class = "{'border': currentImage===image}">
          <img :src = "image" alt = "image1" @click = "startImg(index)">
        </li>
      </template>

    </ul>
  </div>
</div>



<script>
  function imgFunc() {
    return {
      currentImage: 'https://images.pexels.com/photos/5549634/pexels-photo-5549634.jpeg?auto=compress&cs=tinysrgb&dpr=3&h=200&w=400',
      images: [
        'https://images.pexels.com/photos/5549634/pexels-photo-5549634.jpeg?auto=compress&cs=tinysrgb&dpr=3&h=200&w=400',
        'https://images.pexels.com/photos/5101387/pexels-photo-5101387.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260',
        'https://images.pexels.com/photos/2363832/pexels-photo-2363832.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260'
      ],
      startImg(index) {
        this.currentImage = this.images[index];

        if (index > 0)
          return;

        let i = 0;
        var myvar = setInterval(() => {
          i = (i >= this.images.length - 1) ? 0 : ++i;
          this.currentImage = this.images[i];
        }, 1000);
      },
    }
  }
</script>

Я бы предпочел использовать блейд-шаблоны для тех же целей... Но все равно большое спасибо за внимание к этому вопросу.

IVN 10.12.2020 14:37

Другие вопросы по теме