I want to hover on text and have a video appear and play when the user hovers and when you are not hovering, the video would disappear.
I found the following on codepen which does work for both images and video when hovering on specific text, but when I copy the HTML code to list another video since I don't want this for images the next video won't play. It will only play the first video listed. Please help!
function vidPlay() {
$("#video1").get(0).play();
};
function vidPause() {
$("#video1").get(0).pause();
};
$(document).ready(function(){
$("#textToggler").click(function(){
$(".toggleText").toggle();
});
});
function toggleImage() {
$(".hiddenclickimg").toggle();
};
.hiddenimg {
display: none;
}
.hiddentxt {
font-weight: bold;
color: #F00;
z-index:99;
}
.hiddentxt a {
color: #F00;
text-decoration: none;
z-index: 99;
}
.hiddenclick {
font-weight: bold;
color: #F00;
text-decoration: none;
cursor: pointer;
}
.hiddenclick a {
color: #F00;
text-decoration: none;
}
.hiddenclick a:visited {
color: #F00;
text-decoration: none;
}
.hiddentxt:hover ~ .hiddenimg {
display: block;
position: absolute;
z-index: 2
}
.hiddenclickimg {
display: none;
}
<head>
<script
src="https://code.jquery.com/jquery-3.4.1.js"
integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU="
crossorigin="anonymous"></script>
</head>
<body>
<p>
Hover to see a <span class="hiddentxt">banana</span><span class="hiddenimg"><img src="https://upload.wikimedia.org/wikipedia/commons/8/8a/Banana-Single.jpg" width="250" /></span>.
</p>
<p>
Let's see if it works with <span class="hiddentxt" onmouseover="vidPlay()" onmouseout="vidPause()">videos</span>. (The video should play when it's displayed, but sometimes it takes a while to load.)
<span class="hiddenimg"><video id="video1" loop>
<source src="https://mtc.cdn.vine.co/r/videos/ECBC329ACC1050514912117440512_1c6599f182f.4.6.16444000809832514200.mp4" type="video/mp4">
</video></span>
</P>
<p> Click <span class = "hiddenclick" onclick = "toggleImage()">here</span> to show image.<br>
<span class="hiddenclickimg"><img src = "https://s-media-cache-ak0.pinimg.com/originals/22/74/8c/22748c9b9cc2c039aed206cab0247404.gif"/></span>
</p>
</body>
Use classes instead of ID. IDs (as the name suggests) should be unique.
Here's an example how to hover to play/pause multiple videos - without the unnecessary jQuery:
const ELS_aVid = document.querySelectorAll(".hoverVid");
const toggleVideo = (ev) => {
const EL = ev.currentTarget;
const EL_video = EL.querySelector("video");
const isPlay = ev.type === "mouseenter";
EL_video[isPlay ? "play" : "pause"]();
};
ELS_aVid.forEach(EL => {
["mouseenter", "mouseleave"]
.forEach(evt => EL.addEventListener(evt, toggleVideo))
});
.hoverVid {
position: relative;
}
.hoverVid video {
position: absolute;
height: 150px;
top: 100%;
left: 0;
z-index: 1;
box-shadow: 0 0 30px rgba(0,0,0,0.3);
/* "hidden" and prepared animation state*/
transition: 0.5s;
visibility: hidden;
opacity: 0;
pointer-events: none;
transform: translateY(20px);
}
.hoverVid:hover video {
/* on hover, animate into view */
visibility: visible;
opacity: 1;
pointer-events: auto;
transform: translateY(0px);
}
<a class="hoverVid" href="https://mtc.cdn.vine.co/r/videos/ECBC329ACC1050514912117440512_1c6599f182f.4.6.16444000809832514200.mp4">
Hover to watch video
<video loop src="https://mtc.cdn.vine.co/r/videos/ECBC329ACC1050514912117440512_1c6599f182f.4.6.16444000809832514200.mp4"></video>
</a>
<br>
or if you feel like
<a class="hoverVid" href="https://mtc.cdn.vine.co/r/videos/ECBC329ACC1050514912117440512_1c6599f182f.4.6.16444000809832514200.mp4">
watch the same video again LOL
<video loop src="https://mtc.cdn.vine.co/r/videos/ECBC329ACC1050514912117440512_1c6599f182f.4.6.16444000809832514200.mp4"></video>
</a>
Search for any JavaScript Method you don't understand (like i.e: querySelectorAll or forEach) on the MDN Docs
Related
Im building an app where you can like and dislike movies. I have a database of movies and show each one to the user. When you click like or dislike it takes you to the next poster.
I also have access to the description and rating. I've added my own modal to show the user when clicked more information on that movie.
The problem Im having is the modal works for the first movie and changes the class to active. However on the second one it stops working. I can see in the inspector the modal code is there and has the correct information for that movie. But the modal seems to still not work/become unresponsive. Perhaps its an error that I've missed. I notice that it does add active once again on the first item. Perhaps I need to find a way to switch to the current movie instead or incorporate the modal into the slides more? or find a way to reset it..
EDIT -:
Adding to the above. Having done a bit more testing. It seems like the modal doesnt know what id the slide is on.. So it never knows when the slide is changed.. Any ideas how I would do this?
EDIT2 -:
I've now got it so every modal has a unique id per poster using the contact.id. Any way to link the modal id together with the slides so when I change the slide the modal knows and changes to active on the next slide?
const modalBtn = document.querySelectorAll(".modal-btn");
const modalBg = document.querySelectorAll("movie-modal");
const modalClose = document.querySelectorAll(".close-button");
const overlay = document.querySelectorAll("overlay");
const overlayClose = document.querySelectorAll("overlay");
const activeModal = document.querySelectorAll(".movie-modal:first-child")
modalBtn.forEach(function(btn, index){
btn.addEventListener('click', function(click) {
console.log(click)
modalBg.classList.add('active');
overlay.classList.add('active');
var content_id = activeModal.data("id");
console.log(content_id)
});
});
modalClose.forEach(function(btn, index){
btn.addEventListener('click', function(click) {
console.log(click)
modalBg.classList.remove('active');
overlay.classList.remove('active');
});
});
overlay.forEach(function(overlay, index){
overlay.addEventListener('click', function() {
overlayClose.classList.remove('active');
});
});
overlay.forEach(function(overlay, index){
overlay.addEventListener('click', function() {
const modals = document.querySelectorAll('.movie-modal.active')
modals.forEach(modal => {
modal.classList.remove('active');
overlay.classList.remove('active');
})
})
});
$(function(){
var $activeSlide = $('#slides .slide:first-child');
// show first slide
$activeSlide.addClass("active");
// on click event decline
$("#decline").on("click", function(){
goToSlide('decline');
});
// on click approve then what?
$("#approve").on("click", function(){
var content_id = $activeSlide.data("id");
console.log(content_id)
goToSlide('approve');
// $.ajax({
// url: "/user_contents/liked" + content_id,
// method: "post",
// dataType: "ajax"
// });
});
// adding the 'showing' or 'active' slide class to each element
function goToSlide(action) {
$activeSlide.removeClass("active");
$activeSlide = $activeSlide.next(".slide");
// send data to controller
if(action == "approve"){
console.log(action);
} else {
console.log('dislike');
}
$activeSlide.addClass("active");
}
});
.movie-modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(0);
transition: 200ms ease-in-out;
box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;
border-radius: 10px;
z-index: 10;
background-color: white;
width: 300px;
max-width: 80%;
color: black;
}
.movie-modal.active{
transform: translate(-50%, -50%) scale(1);
}
.movie-modal-header {
padding: 8px 16px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid rgb(182, 182, 182);
}
.movie-rating {
font-size: 1.25rem;
font-weight: bold;
}
.close-button {
cursor: pointer;
border: none;
outline: none;
background: none;
font-size: 1.25rem;
font-weight: bold;
}
.movie-modal-body {
padding: 16px;
color: rgb(160, 160, 160);
}
#overlay {
position: fixed;
opacity: 0;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.35);
pointer-events: none;
transition: 200ms ease-in-out;
}
#overlay.active {
opacity: 1;
pointer-events: all;
}
#slides {
position: relative;
height: 100%;
padding: 0px;
margin: 0 auto;
list-style-type: none;
}
.slide {
position: absolute;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
opacity: 0;
z-index: 1;
box-sizing: border-box;
color: #fff;
-webkit-transition: opacity 1s;
-moz-transition: opacity 1s;
-o-transition: opacity 1s;
transition: opacity 1s;
}
.showing {
opacity: 1;
z-index: 2;
}
#slide-controls {
margin: 20px 0 400px;
width: 100%;
position: absolute;
display: flex;
justify-content: center;
justify-content: space-between;
z-index: 10;
color: #ffffffba;
font-size: 24px;
top: 50%;
}
.image-carousel {
width: 100%;
height: 642px;
margin: 0 auto;
background-size: contain;
background-repeat: no-repeat;
position: relative;
}
#decline {
margin-left: 16px;
}
#approve {
margin-right: 16px;
}
.img-thumbnail {
padding: 0 !important;
}
<ul id="slides">
<% #contents.each_with_index do |content, i| %>
<li class="slide <%= 'active' if i == 0 %>" data-id="<%= content.id %>">
<div class="image-carousel" style="background-image: url('<%= content.poster %>')">
</div>
<button class="modal-btn">Open Modal</button>
<div id="movie-modal <%= content.id %>" class="movie-modal">
<div class="movie-modal-header">
<div class="movie-rating" style="">Average rating: <strong><%= content.rating %></strong></div>
<button class="close-button">×</button>
</div>
<div class="movie-modal-body"><p><%= content.description %></p></div>
</div>
<div id="overlay <%= content.id %>"></div>
</li>
<% end %>
</ul>
<div id="slide-controls">
<span id="decline"><i class="fa fa-solid fa-thumbs-down fa-2x"></i></span>
<span id="approve"><i class="fa fa-solid fa-thumbs-up fa-2x"></i></span>
</div>
This shows when the first poster is clicked the modal works and movie-modal active is added to the modal class
The second image with Morbius in it.. The modal class doesnt change for the current slide showing however it changes for the previous slide.. BUT the previous slide only changes once and then sticks to active. Nothing is shown on the poster.
I've added a search bar in the header that is set to display: none by default and I used js to make it appear on a button click via assigning a .show class which contains display: block !important to the search bar element (#search). It's working fine but my only problem is the rough transition from display: none to block, so I've been looking into ways to make this transition smooth and most of the answers I found were using jQuery, which I don't really want to do since I'm still in the learning phase of js, so if there's a way I can do this using vanilla js, please help me with it.
Here's my code https://jsfiddle.net/5jxLq9ck/
In CSS line 38, I add the .show utility class
.show {
display: block !important;
}
And I'm assuming I'll have to edit something in here (js) to get the desired effect:
function showSearch(e) {
e.preventDefault;
if (
e.target.classList.contains("show-btn") ||
e.target.classList.contains("fas")
) {
const searchBar = document.querySelector("#search");
searchBar.classList.add("show");
}
}
Additional question: is my use of e.preventDefault correct here? The functionality didn't work until I used it.
Thanks a lot in advance.
Here is an updated snippet, I've changed the input width for the animation. You can make it even more smooth by set the input height.
const searchDiv = document.querySelector("#search-div");
// ADD EVENT LISTENERS
searchDiv.addEventListener("click", showSearch);
// FUNCTION: SHOW SEARCH BAR ON BUTTON CLICK
function showSearch(e) {
e.preventDefault;
if (
e.target.classList.contains("show-btn") ||
e.target.classList.contains("fas")
) {
const searchBar = document.querySelector("#search");
searchBar.classList.add("show");
}
}
/* GENERAL */
:root {
--light-color: #ccc;
--lighter-color: #f4f4f4;
--dark-color: #333;
--darker-color: #222;
--brand-color: #ff4;
--danger: #f44;
--danger-dark: #c00;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: var(--dark-color);
color: var(--light-color);
font-family: "Trebuchet MS";
}
ul li {
list-style: none;
}
button,
input {
outline: none;
}
/* UTILITY */
.highlight {
color: var(--brand-color);
}
.show {
width: 300px !important;
border: black 2px solid;
padding: 0.6rem 1rem;
}
/* HEADER */
header {
background: var(--darker-color);
display: flex;
flex-direction: row;
align-items: center;
text-align: center;
justify-content: space-between;
padding: 1.4rem 6rem;
width: 100%;
}
#logo {
font-size: 2.4rem;
font-weight: 200;
}
#search-div {
width: auto;
height: auto;
display: flex;
gap: 0.4rem;
}
.show-btn {
padding: 0.6rem 0.7rem;
background: var(--light-color);
border-radius: 5px;
border: none;
transition: ease-in 300ms;
font-size: 1.2rem;
cursor: pointer;
height: 100%;
margin-top: 2px;
}
.show-btn:hover {
background: var(--brand-color);
transition: ease-in 300ms;
}
#search {
width: 0;
background: var(--lighter-color);
color: var(--darker-color);
height: 100%;
font-size: 1.2rem;
border-radius: 2px;
transition: ease-in 300ms;
border: none;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Contact List</title>
<link rel="stylesheet" href="css/style.css">
<script src="https://kit.fontawesome.com/3ad7573e76.js" crossorigin="anonymous"></script>
</head>
<body>
<header>
<div id="logo-div">
<h1 id="logo">
<span class="highlight"><i class="fas fa-user-friends"></i></span> My<span
class="highlight">Contact</span>List
</h1>
</div>
<div id="search-div">
<button class="show-btn"><i class="fas fa-search"></i></button>
<input id="search" type="text" placeholder="Search contacts...">
</div>
</header>
<script src="js/main.js"></script>
</body>
</html>
You can make the transition from no display to block display smooth by playing with the opacity property so that when the element is given the "show" class it animates from an opacity of 0 to an opacity of 1 like so.
function showSearch(e) {
e.preventDefault;
if (
e.target.classList.contains("show-btn") ||
e.target.classList.contains("fas")
) {
const searchBar = document.querySelector("#search");
searchBar.classList.add("show");
}
}
document.getElementById("show").addEventListener("click", e => {
showSearch(e);
});
#keyframes smooth {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.show {
animation: smooth 1s ease;
display: block !important;
}
.none {
display: none;
}
<div class="none" id="search">Example</div>
<button class="show-btn fas" id="show">Show</button>
(setting disabled = 'true' didn't seem to work for me so this is another way around it)
How do you disable an HTML range slider, for example when a button is pressed:
<input type="range" class="tempo-slider" max="300" min="1" value="150" />
I couldn't find the answer anywhere but this is my fix to the issue:
My fix for it was constantly resetting the range slider's value when you a user tried to change it using:
event.target.value = bpm;
Where event is the clicking of the event slider.
My full code is here, I hope it can help someone:
//This is code taken from a larger oop project and so some of the logic may look janky, but I just made it work for this example
//These two variable need to be predefined
let bpm = 150;
let playing = false;
//Select your slider and button from your html:
const tempoSlider = document.querySelector(".tempo-slider");
const playButton = document.querySelector(".play");
//Update the html function, essentially purely for styling
updateHTML = function () {
if (!playing) {
tempoSlider.classList.toggle("inactive");
playButton.innerText = "Stop";
playButton.classList.toggle("active");
playing = true;
} else {
tempoSlider.classList.toggle("inactive");
playButton.innerText = "Play";
playButton.classList.toggle("active");
playing = false;
}
};
//this fucntion updates the temp text and the slider
function changeTempo(event) {
//get the temp number from the document
const tempoText = document.querySelector(".tempo-number");
if (!tempoSlider.classList.contains("inactive")) {
//if the slider isnt inactive then update the bpm as usual
bpm = event.target.value;
tempoText.innerText = bpm;
} else {
//else just make the slider reset to the preset bmp, this way it will not change
event.target.value = bpm;
}
}
//add event listeners to the button and the range slider
tempoSlider.addEventListener("input", function (event) {
changeTempo(event);
});
playButton.addEventListener("click", function () {
updateHTML();
});
/*All of this styling just makes it clear when the temp slider is inactive*/
:root {
--background-color: #ffffff;
--text-color: #322e2f;
}
body {
display: flex;
flex-direction: column;
align-items: center;
}
.play {
width: 10rem;
transition: all 0.5s ease;
padding: 1rem 2rem;
font-size: 1.5rem;
background: var(--text-color);
color: var(--background-color);
border: none;
cursor: pointer;
margin-top: 3rem;
outline: none;
border: solid 0.01rem var(--text-color);
}
.play.active {
color: var(--text-color);
background: var(--background-color);
border: solid 0.01rem var(--text-color);
}
.tempo {
margin: 1rem;
width: 20%;
}
.tempo-slider {
transition: all 0.5s ease;
padding: 0.3rem;
-webkit-appearance: none;
appearance: none;
margin: 1rem 0rem;
outline: none;
width: 100%;
position: relative;
background: var(--text-color);
cursor: pointer;
border-radius: 2rem;
border: solid 0.05rem var(--text-color);
}
.tempo-slider::-webkit-slider-thumb {
transition: all 0.5s ease;
-webkit-appearance: none;
appearance: none;
width: 1rem;
height: 1rem;
border-radius: 2rem;
background: var(--background-color);
cursor: pointer;
}
.tempo-slider.inactive {
background: var(--background-color);
}
.tempo-slider.inactive::-webkit-slider-thumb {
background: var(--text-color);
}
.tempo p {
font-size: 1.5rem;
text-align: center;
}
<!--This is part of a larger project I have scaled back to display the slider-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<!--Basic button to say start an audio file-->
<button class="play">Play</button>
<!-- slider to devide the audio's bpm-->
<div class="tempo">
<input type="range" class="tempo-slider" max="300" min="1" value="150" />
<p>Tempo: <span class="tempo-number">150</span></p>
</div>
<script src="app.js"></script>
</body>
</html>
The full project should be done in the next week and available on my github here: https://github.com/MichealNestor01
So I have already coded other stuff to appear after a background video finishes playing using this javascript
var vid = document.getElementById("bgvid");
var vid2 = document.getElementById('akiratrailer');
vid.onended = function() {myFunction()};
function myFunction() {
vid2.style.display = "block";
document.getElementById("headline2").innerHTML = "stuff";
document.getElementById("headline3").innerHTML = "stuff";
};
How would I add a custom button (I already have the CSS for the button) to this javascript so that it may show up after the background video finishes along with the other stuff?
This is my the html for the button
<a href='newpage.html' class='button'>blah blah blah</a>
my website for reference My Website
Try this:
var vid = document.getElementById( 'bgvid' ),
vid2 = document.getElementById( 'content' );
vid.onended = function() {
vid2.style.display = 'block';
document.getElementById( 'headline2' ).innerHTML = 'stuff';
document.getElementById( 'headline3' ).innerHTML = 'stuff';
}
* {
box-sizing: border-box
}
body {
margin: 0
}
#bgvid {
position: fixed;
top: 0;
left: 0;
min-width: 100%;
min-height: 100%
}
#content {
display: none;
position: fixed;
overflow-y: auto;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: rgba(0, 0, 0, 0.5);
color: #f1f1f1;
text-align: center
}
#akiratrailer {
margin: 0 auto;
width: 50%
}
.button {
width: 200px;
font-size: 18px;
padding: 10px;
border: none;
background: #000;
color: #fff;
cursor: pointer;
text-decoration: none
}
.button:hover {
background: #ddd;
color: black
}
<video autoplay muted id="bgvid">
<source src="https://www.w3schools.com/howto/rain.mp4" type="video/mp4">
</video>
<div id="content">
<h2 id="headline2"></h2>
<video id="akiratrailer" controls>
<source src="http://andiviaandes.com/videos/akiratrailer.mp4" type="video/mp4">
</video>
<h2 id="headline3"></h2>
<a href='#' class='button'>blah blah blah</a>
</div>
Well you could either generate the button dynamically using the below code in onended callback
var button = document.createElement('button');
var node = document.createTextNode('Click me');
button.appendChild(node);
button.classList.add('button');
body.appendChild(button); //or any other parent element you wish to add the button to
or just simply add the button in HTML, set up the class and adjust display to 'display: none' and change it similarily to how you manipulate the headlines right now.
Here's the jsbin with a working example: http://jsbin.com/pepidedimo/edit?html,css,js,console,output
I have created a context menu item but it does not work all of the time. For instance, when I click on pause, it pauses, and when I click play, it plays again, and that happens one more time. After that, it does not do anything when I click on it. Then, it goes back to where it started. Here is my code:
<script>
var x = 1;
</script>
<script>
// JAVASCRIPT (jQuery)
// Trigger action when the contexmenu is about to be shown
$(document).bind("contextmenu", function (event) {
// Avoid the real one
event.preventDefault();
// Show contextmenu
$(".custom-menu").finish().toggle(100).
// In the right position (the mouse)
css({
top: event.pageY + "px",
left: event.pageX + "px"
});
});
// If the document is clicked somewhere
$(document).bind("mousedown", function (e) {
// If the clicked element is not the menu
if (!$(e.target).parents(".custom-menu").length > 0) {
// Hide it
$(".custom-menu").hide(100);
}
});
// If the menu element is clicked
$(".custom-menu li").click(function(){
// This is the triggered action name
switch($(this).attr("data-action")) {
// A case for each action. Your actions here
case "first": alert("first"); break;
case "second": alert("second"); break;
case "third": alert("third"); break;
}
// Hide it AFTER the action was triggered
$(".custom-menu").hide(100);
});
</script>
<script>
function changeContextMenuIn() {
</script>
<style>
/* CSS3 */
/* The whole thing */
.custom-menu {
display: none;
z-index: 1000;
position: absolute;
overflow: hidden;
border: 1px solid #CCC;
white-space: nowrap;
font-family: sans-serif;
background: #FFF;
color: #333;
border-radius: 5px;
padding: 0;
}
/* Each of the items in the list */
.custom-menu li {
padding: 8px 12px;
cursor: pointer;
list-style-type: none;
}
.custom-menu li:hover {
background-color: #DEF;
}
</style>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.js"></script>
<video width="640" id="vidPlayerMain" autoplay="" controls="" onclick="if (x == 1) {document.getElementById('vidPlayerMain').pause(); document.getElementById('pauseContextMenu').innerHTML='Play'; x = 0;} else {document.getElementById('vidPlayerMain').play(); document.getElementById('pauseContextMenu').innerHTML='Pause'; x = 1;}" name="media">
<source src="https://video-lax1-1.xx.fbcdn.net/hvideo-xpa1/v/t42.1790-2/1063787_495053737243964_500266464_n.mp4?efg=eyJybHIiOjYzNSwicmxhIjo3MzZ9&rl=635&vabr=353&oh=d763b8ecfa2a823b9971c497732e4b69&oe=55C8F2C9" type="video/mp4">
</video>
<br />
<ul id='contextMenu' class='custom-menu'>
<li data-action="first">Pause</li>
</ul>
It is because the function to pause and play video is bound to contextMenu's <a> element but function to alert data-action attribute is bound to contextMenu's <li> element and <a> element is not fully occupying width and height of <li> element. (The video does not play/pause at times when you click on context menu button outside of its area occupying text)
One simple solution to this is to change the CSS of these two element such that <a> element is occupying full width and height.
CSS:
/* CSS3 */
/* The whole thing */
.custom-menu {
display: none;
z-index: 1000;
position: absolute;
overflow: hidden;
border: 1px solid #CCC;
white-space: nowrap;
font-family: sans-serif;
background: #FFF;
color: #333;
border-radius: 5px;
padding: 0;
}
/* Each of the items in the list */
.custom-menu li {
cursor: pointer;
list-style-type: none;
}
.custom-menu li:hover {
background-color: #DEF;
}
.custom-menu li a {
padding: 8px 12px;
display:inline-block;
}