Home > Blockchain >  Iterate HTML classes to create class objects with javascript
Iterate HTML classes to create class objects with javascript

Time:12-03

class CVP {
  constructor(obj) {
    const { P, V,  PC, BPB, MPB, VRC, VBC, FS, PROG, PROGC, TW, TC } = obj;
    
    // player
    this.player = document.querySelector(P);

    // video el
    this.video = document.querySelector(V);

    // player controller
    this.playerController = document.querySelector(PC);

    // play controlers
    this.bigPlayBtn = document.querySelector(BPB);
    this.miniPlayBtn = document.querySelector(MPB);

    // volume controllers
    this.volumeRangeController = document.querySelector(VRC);
    this.volumeBtnController = document.querySelector(VBC);

    // fullscreen
    this.fullscreen = document.querySelector(FS);

    // progress bar
    this.progress = document.querySelector(PROG);
    this.progressCurr = document.querySelector(PROGC);

    // time displayers
    this.timeWhole = document.querySelector(TW);
    this.timeCurr = document.querySelector(TC);

    // bool vars
    this.isMousedown = false;
    this.isFullScreen = false;

    // timer vars
    this.timer = '';
    this.timerTime = 1000;
  }

  setIsMouseDown = () => {
    this.isMousedown = !this.isMousedown;
  };

  bigPlayBtnHandler = () => {
    this.bigPlayBtn.classList.toggle('HideBigPlayBtn');
  };

  showHideControlls = () => {
    this.player.classList.toggle('paused');
  };

  static changeIcon(target, remove, add) {
    target.classList.remove(remove);
    target.classList.add(add);
  }

  changePlayIcon() {
    console.log(this.video.src);
    const target = this.miniPlayBtn;
    let icon = this.video.paused ? 'play' : 'pause';

    if (icon === 'pause') {
      CVP.changeIcon(target, 'play', 'pause');
    } else {
      CVP.changeIcon(target, 'pause', 'play');
    }

  }

  changeVolumeIcon(volElIconID) { // not in use
    const target = document.querySelector(volElIconID);
    if (this.video.muted) {
      CVP.changeIcon(target, 'max', 'mute');
    } else {
      CVP.changeIcon(target, 'mute', 'max');
    }

  }

  handleVideoTime(time) {
    // minutes and seconds So Far
    let hours;
    let min = Math.floor(time / 60);
    let sec = Math.floor(time % 60);
    let output = '';

    sec = (sec < 10) ? sec = `0${sec}` : sec;
    min = (min < 10) ? min = `0${min}` : min;

    output = `${min}:${sec}`;

    return output;
  }

  rangeVolumeController() {
    cvp.video[this.name] = this.value;
    if (cvp.volumeRangeController.value == 0) {
      cvp.video.muted = true;
      CVP.changeIcon(cvp.volumeBtnController, 'max', 'mute');
    } else {
      cvp.video.muted = false;
      CVP.changeIcon(cvp.volumeBtnController, 'mute', 'max');
    }

  }

  btnVolumeController() {
    if (cvp.video.muted) {
      cvp.video.muted = false;
      CVP.changeIcon(cvp.volumeBtnController, 'mute', 'max');
      cvp.volumeRangeController.value = 1;
      cvp.video.volume = 1;
    } else {
      cvp.video.muted = true;
      CVP.changeIcon(cvp.volumeBtnController, 'max', 'mute');
      cvp.volumeRangeController.value = 0;
    }

  }

  hideWhenFullScreen() {
    if (this.isFullScreen) {
      this.playerController.hidden = false; // or this.playerController.style.bottom = '0px';
      document.body.style.cursor = 'auto';
      clearTimeout(this.timer);

      this.timer = setTimeout(() => {
        this.playerController.hidden = true; // or this.playerController.style.bottom = '-50px';
        document.body.style.cursor = 'none';
      }, this.timerTime);

    } else {
      this.playerController.hidden = false; // or this.playerController.style.bottom = '0px';
      document.body.style.cursor = 'auto';
    }
  }

  toggleFullScreen() {
    if (this.isFullScreen) {
      if (document.exitFullscreen) {
        document.exitFullscreen();
      } else if (document.mozCancelFullScreen) {
        document.mozCancelFullScreen();
      } else if (document.webkitCancelFullScreen) {
        document.webkitCancelFullScreen();
      } else if (document.msExitFullscreen) {
        document.msExitFullscreen();
      } else {
        console.error('Unable to find a fullscreen exit method.');
      }
    } else {
      if (this.player.requestFullscreen) {
        this.player.requestFullscreen();
      } // standard
      else if (this.player.webkitRequestFullscreen) {
        this.player.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
      } else if (this.player.mozRequestFullScreen) {
        this.player.mozRequestFullScreen();
      } else if (this.player.msRequestFullscreen) {
        this.player.msRequestFullscreen();
      } else {
        console.error('Unable to find a fullscreen request method');
      }

    }
  }

  toggleFullScreenHelper() {
    this.player.classList.toggle('isFullScreen');
    this.isFullScreen = !this.isFullScreen;
  }

  togglePlay() {
    const action = this.video.paused ? 'play' : 'pause';
    this.video[action]();

    this.playerController.style.bottom = '0px';
    this.changePlayIcon();
    this.showHideControlls();
  }

  showVideoTime() {
    const {
      currentTime,
      duration
    } = cvp.video;
    const pct = (currentTime / duration) * 100;
    cvp.progressCurr.style.width = pct   '%';

    cvp.timeCurr.innerText = cvp.handleVideoTime(currentTime.toFixed(0));
    cvp.timeWhole.innerText = cvp.handleVideoTime(duration);
  }

  scrub(e) {
    console.log(e);
    console.log('e', e, 'this', this);
    let seconds = (e.offsetX / this.progress.offsetWidth) * this.video.duration;
    this.video.currentTime = seconds;
  }

  loadVideo() {
    // il metodo è da applciare assieme a un loader e ad altre gestioni degli eventi video
    // esempi di loader: https://www.w3schools.com/howto/howto_css_loader.asp
    // metodi video: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/loadeddata_event
    // per i tooltips: https://www.w3schools.com/howto/howto_css_tooltip.asp
    console.log(this.video.src);
  }

  init() {
    // Hook up the Event Listeners

    // toggle Play
    this.video.addEventListener('click', this.togglePlay.bind(this));
    this.miniPlayBtn.addEventListener('click', this.togglePlay.bind(this));
    this.bigPlayBtn.addEventListener('click', this.togglePlay.bind(this));

    this.video.addEventListener('loadeddata', this.loadVideo.bind(this));

    // bigPlayBtn show/hide handler
    this.video.addEventListener('click', this.bigPlayBtnHandler);
    this.bigPlayBtn.addEventListener('click', this.bigPlayBtnHandler);
    this.miniPlayBtn.addEventListener('click', this.bigPlayBtnHandler);

    // time update
    this.video.addEventListener('timeupdate', this.showVideoTime);

    // progress bar events
    this.progress.addEventListener('click', this.scrub.bind(this));
    this.progress.addEventListener('mousemove', (e) => this.isMousedown && this.scrub(e));
    this.progress.addEventListener('mousedown', this.setIsMouseDown);
    this.progress.addEventListener('mouseup', this.setIsMouseDown);

    // fullscreen
    this.fullscreen.addEventListener('click', this.toggleFullScreen.bind(this));

    // volume controllers
    this.volumeBtnController.addEventListener('click', this.btnVolumeController);
    this.volumeRangeController.addEventListener('change', this.rangeVolumeController);
    this.volumeRangeController.addEventListener('mousemove', this.rangeVolumeController);

    // cross-browser fullscreen hanlder
    const browsersFullScreen = [
      'fullscreenchange',
      'mozfullscreenchange',
      'webkitfullscreenchange',
      'msfullscreenchange',
    ];

    browsersFullScreen.forEach(browser => document.addEventListener(browser, this.toggleFullScreenHelper.bind(this)));

    // player show/hide controlls on mousemove event
    this.player.addEventListener('mousemove', this.hideWhenFullScreen.bind(this));
  }

}

const initializeCVP = {
  P: '.CVP',
  V: '.video',
  PC: '.playerController',
  BPB: '.bigPlayBtn',
  MPB: '.miniPlayBtn',
  VRC: '.volumeRC',
  VBC: '.volumeBC',
  FS: '.fullscreen',
  PROG: '.progress',
  PROGC: '.progressCurr',
  TW: '.timeWhole',
  TC: '.timeCurr',
};

let cvp = new CVP(initializeCVP);
cvp.init();
body {
  margin: 0;
  padding: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-content: center;
  align-items: center;
  height: 100vh;
  background-image: linear-gradient(to top, #dad4ec 0%, #dad4ec 1%, #f3e7e9 100%);
  /* background-image: linear-gradient(-20deg, #f794a4 0%, #fdd6bd 100%); */
}

[hidden] {
  display: none !important;
}

.player {
  display: flex;
  justify-content: center;
  border-radius: 10px;
  width: 700px;
  max-height: 100%;
  position: relative;
  font-size: 16px;
  overflow: hidden;
  text-align: center;
  background: #000;
  color: #fff;
  box-shadow: 0 0 15px rgba(0, 0, 0, 0.4), 0 10px 20px -3px rgba(0, 0, 0, 0.2);
  margin-bottom: 5vh;
}


/* This css is only applied when fullscreen is active. */

.player.isFullScreen {
  max-width: none;
  max-height: none;
  width: 100%;
  height: 100%;
  background: #000;
}

.player.isFullScreen video {
  width: 100%;
  height: auto;
}

.video {
  max-width: 100%;
  max-height: 100%;
  -o-object-fit: fill;
  object-fit: fill;
  /* These are the actual dimensions of the video. Avoid the awkward element resize after the video loads. */
  /* width: 640px;
    height: 358px; */
}

.playerController {
  display: flex;
  align-items: center;
  justify-content: space-evenly;
  width: 98%;
  max-width: 1000px;
  /* limiting in fullscreen mode */
  position: absolute;
  background: rgba(51, 51, 51, 0.8);
  color: #fff;
  border-radius: 10px;
  margin-bottom: 1%;
  z-index: 2147483649;
  transform: translateY(150px);
  transition: .2s;
  bottom: -50px;
  /* 0 to show in preview */
}

.playerController * {
  color: rgba(255, 255, 255, 0.9);
}

.playerController button {
  background: none;
  border: 0;
  outline: 0;
  text-align: center;
  cursor: pointer;
}

.icon {
  font-size: 16px;
  font-family: 'Font Awesome 5 Free';
  font-weight: 900;
}

.player:hover .playerController,
.player.paused .playerController {
  transform: translateY(0);
}

.player__btn {
  max-width: 50px;
}

.play::before {
  content: '\f04b';
}

.pause::before {
  content: '\f04c';
}

.progress {
  /* position: relative; */
  width: 50%;
  cursor: pointer;
  user-select: none;
  height: 10px;
  /* or 15px */
  background: rgba(160, 154, 154, 0.8);
  overflow: hidden;
  border-radius: 50px;
}

.progressCurr {
  height: 100%;
  width: 0;
  background: rgba(255, 255, 255, 0.9);
  /* flex: 0;
    flex-basis: 0%; */
  /* old styles yellow progressCurr bg flex-basic insted of width*/
}

.fullscreen::before {
  font-family: 'FontAwesome';
  content: '\f065';
  font-size: 16px
}

.player.isFullScreen .playerController .fullscreen::before {
  content: '\f066';
}

.timeCurr,
.timeWhole {
  font-size: 14px;
  user-select: none;
}

.volumeContainer {
  display: flex;
}

.volumeBC {
  cursor: pointer;
  padding-right: 5px;
}

.volumeRC {
  width: 10px;
  height: 30px;
}

.max::before {
  content: '\f028'
}

.mute::before {
  content: '\f6a9'
}

.bigPlayBtn {
  font-size: 80px;
  position: absolute;
  top: 50%;
  left: 50%;
  cursor: pointer;
  box-sizing: border-box;
  transform: translate(-50%, -50%);
  transition: .5s;
}

.bigPlayBtn::before {
  content: '\f04b';
}

.HideBigPlayBtn {
  cursor: default;
  opacity: 0;
  transform: translate(-50%, -50%) scale(1.2);
}
<div class='player paused CVP'>
  <video class='video' src='http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4' width='700' height='350' poster='https://i.ytimg.com/vi/A1sVB8XBNDo/maxresdefault.jpg'>
    </video>
  <div class='bigPlayBtn icon'></div>
  <div class='playerController'>
    <button class='player__btn miniPlayBtn icon' title='Play/Pause'></button>
    <div class='timeCurr'>00:00</div>
    <div class='progress'>
      <span id='buffered'>
                <div class='progressCurr'></div>
            </span>
    </div>
    <div class='timeWhole'>--:--</div>
    <div class='volumeContainer'>
      <button class='player__btn max volumeBC icon'></button>
      <input type='range' name='volume' class='volume volumeRC' min='0' max='1' step='0.01' value='1' title='Volume' />
    </div>
    <button class='player__btn fullscreen icon' title='Full Screen'></button>
  </div>
</div>
<div class='player paused CVP'>
  <video class='video' src='http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4' width='700' height='350' poster='https://i.ytimg.com/vi/A1sVB8XBNDo/maxresdefault.jpg'>
    </video>
  <div class='bigPlayBtn icon'></div>
  <div class='playerController'>
    <button class='player__btn miniPlayBtn icon' title='Play/Pause'></button>
    <div class='timeCurr'>00:00</div>
    <div class='progress'>
      <span id='buffered'>
                <div class='progressCurr'></div>
            </span>
    </div>
    <div class='timeWhole'>--:--</div>
    <div class='volumeContainer'>
      <button class='player__btn max volumeBC icon'></button>
      <input type='range' name='volume' class='volume volumeRC' min='0' max='1' step='0.01' value='1' title='Volume' />
    </div>
    <button class='player__btn fullscreen icon' title='Full Screen'></button>
  </div>
</div>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

Sorry if I'm disturbing you for something that will probably appear you very simple, but I can't find a way to solve the problem on my own. And, maybe, the title is not appropriated... Well, I have a class with a constructor that works with HTML tag VIDEO, named CVP. It creates methods to act on video controls. The initial method consist in hooking the event listeners to HTML controls'classes. Anyway, with a single VIDEO tag in the HTML code, it works correctly, but I don't know how to iterate the class on any VIDEO when there are many of them. This is an examples of the VIDEO tag:

<div class='player paused CVP'>
<video class='video' src="../../video/pietro.mp4">
</video>
etc...

This is the beginnig of the JS CLASS:

class CVP {
  constructor (obj) {
    const { P, V,  PC, BPB, MPB, VRC, VBC, FS, PROG, PROGC, TW, TC } = obj;
    this.player = document.querySelector(P);
    this.video = document.querySelector(V);
etc..

The class ends this way:

(many other methods...)
init () {
    // Hook up the Event Listeners

    // toggle Play
    this.video.addEventListener('click', this.togglePlay.bind(this));
    this.miniPlayBtn.addEventListener('click', this.togglePlay.bind(this));
    this.bigPlayBtn.addEventListener('click', this.togglePlay.bind(this));
etc...
  }
}

Then, there's a inizialization's variable:

const initializeCVP = {
  P: '.CVP',
  V: '.video',
  PC: '.playerController',
etc...
};

To add a single VIDEO tag:

const cvp = new CVP(initializeCVP);
cvp.init();

In this case it works perfectly. But, How can I assign the JS class to many VIDEO tags? I tried this way, but it doesn't work:

window.onload = function () {
  const CVPs = document.querySelectorAll('.CVP');
  CVPs.forEach((cvp) => {
    cvp = new CVP(initializeCVP);
    cvp.init();
  });
};

Is it clear? I hope so. I'm quite a beginner, so plase, don't complain...

CodePudding user response:

Change your constructor so it takes elements rather than selectors. The caller can call document.querySelector() or document.querySelectorAll() when creating the initialization object.

class CVP {
  constructor (obj) {
    const { P, V,  PC, BPB, MPB, VRC, VBC, FS, PROG, PROGC, TW, TC } = obj;
    this.player = P;
    this.video = V;
    etc..
  }
}

window.onload = function() {
  const players = document.querySelectorAll('.player');
  players.forEach(player => {
    const initializeCVP = {
      P: player,
      V: player.querySelector('.video'),
      PC: player.querySelector('.playerController'),
      BPB: player.querySelector('.bigPlayBtn'),
      MPB: player.querySelector('.miniPlayBtn'),
      VRC: player.querySelector('.volumeRC'),
      VBC: player.querySelector('.volumeBC'),
      FS: player.querySelector('.fullscreen'),
      PROG: player.querySelector('.progress'),
      PROGC: player.querySelector('.progressCurr'),
      TW: player.querySelector('.timeWhole'),
      TC: player.querySelector('.timeCurr'),
    };
    const cvp = new CVP(initializeCVP);
    cvp.init();
  });
};

CodePudding user response:

I found a way to fix what @Barman and @RandyCasburn pointed out regarding the single 'cvp' istance connected to class methods.

First, I applied @Barman suggestion, so to iterate all players has in the previous answer.

Then, I put a global array to collect the single 'cvp', with a couple of other new variables:

let cvpItems = [];
let cvpItem;
let currentCVP;

Then, I pushed all player istances in the HTML, applying to anyone an addEventListener. The window.onload code is changed in this way:

window.onload = function () {
  const players = document.querySelectorAll('.player');
  players.forEach(player => {
    const initializeCVP = {
      P: player,
      V: player.querySelector('.video'),
      PC: player.querySelector('.playerController'),
      BPB: player.querySelector('.bigPlayBtn'),
      MPB: player.querySelector('.miniPlayBtn'),
      VRC: player.querySelector('.volumeRC'),
      VBC: player.querySelector('.volumeBC'),
      FS: player.querySelector('.fullscreen'),
      PROG: player.querySelector('.progress'),
      PROGC: player.querySelector('.progressCurr'),
      TW: player.querySelector('.timeWhole'),
      TC: player.querySelector('.timeCurr'),
    };
    cvpItem = new CVP(initializeCVP);
    cvpItems.push(cvpItem);
    cvpItem.init();
    player.addEventListener('click', currentItem, false);
  });
};

With the currentItem function I find the current player iteration using the title attribute in the HTML video tags:

let currentItem = function () {
  for (var i = 0; i < cvpItems.length; i  ) {
    if (cvpItems[i].video.title == this.querySelectorAll('.video')[0].title) {
      currentCVP = i;
      break;
    }
  }
};

So, now I can identify the specific player in the JS class. For examples, the previous rangeVolumeController method changes from:

  rangeVolumeController () {
    cvp.video[this.name] = this.value;
    if (cvp.volumeRangeController.value == 0) {
      cvp.video.muted = true;
      CVP.changeIcon(cvp.volumeBtnController, 'max', 'mute');
    } else {
      cvp.video.muted = false;
      CVP.changeIcon(cvp.volumeBtnController, 'mute', 'max');
    }

  }

to

  rangeVolumeController () {
    console.log(CVP, this, event.target);
    cvpItems[currentCVP].video[this.name] = this.value;
    if (cvpItems[currentCVP].volumeRangeController.value == 0) {
      cvpItems[currentCVP].video.muted = true;
      CVP.changeIcon(cvpItems[currentCVP].volumeBtnController, 'max', 'mute');
    } else {
      cvpItems[currentCVP].video.muted = false;
      CVP.changeIcon(cvpItems[currentCVP].volumeBtnController, 'mute', 'max');
    }
  }

It works! But, I know, it's not an elegant way for coding. If someone wants to propose a better or more "elegant" solution, I'll be grateful.

  • Related