HTMLAudioElement.currentTime is not a function - javascript

I'm using an range slider <input> element inside a React component to scrub/seek through an HTML5 <audio> element.
see the shortened code of the component:
class Player extends Component {
componentDidMount() {
if(this.player) {
this.player.addEventListener("timeupdate", e => {
this.props.update_time(e.target.currentTime)
this.props.set_duration(e.target.duration)
this.timeBar.value = e.target.currentTime
});
this.timeBar.addEventListener("change", e => {
console.log(this.player)
this.player.currentTime(e.target.value)
})
}
}
render() {
return (
<div className="player">
<input
type="range"
id="seek"
defaultValue={this.props.time}
max={this.props.duration}
ref={ref => this.timeBar = ref}
/>
<audio ref={ref => this.player = ref} >
<source
src={this.props.url}
type="audio/mpeg"
/>
</audio>
</div>
)
}
}
If I move the slider (thus triggering the "change" event), I get the following error:
TypeError: this.player.currentTime is not a function
However, if I try to set the currentTime value directly in the console on the object that is returned by console.log(this.player) it works perfectly fine.
I tried pretty much everything and ran out ouf ideas. The file is fully loaded, the server supports streaming, the this.player object is returned correctly, even inside the event listener.
Thanks for your help!

Related

How to trigger child element while clicking parent element on React?

So I am doing a drum machine project on Freecodecamp, where I am failing this condition.
"
When I click on a .drum-pad element, the audio clip contained in its child element should be triggered.
"
So how do I trigger child element(in this case the audio element,to play the audio) while clicking or pressing the parent element?
I wrote something like this
class Drumset extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this)
}
handleClick(e){
this.inputElement.click()
}
render() {
return (
<div>
<div id="drum-machine" class="containerx box-middle">
<div id="display">
<div class="container">
<div class="row">
<div class="col">
<button type="button" class="drum-pad" id="Heater-1" onClick={this.handleClick}>
Q
<audio
class="clip"
id="Q"
src="https://s3.amazonaws.com/freecodecamp/drums/Heater-1.mp3"
type="audio/mpeg"
></audio>
Well I tried putting the onClick event on inner element, which doesn't work either.
You can get your audio element by searching the DOM and then call .play() on it.
So your onClick handler becomes:
handleClick(e){
const audio = document.getElementById("Q");
audio.play();
}
If you have multiple buttons all with only 1 audio tag inside, you can use one general onClick handler and get the child audio element from the target of the event.
handleClick(e) {
const audio = e.target.children[0];
audio.play();
};
Hello write write this in inner click function
//innerclick function
Const Innerclick(e)=>{
e.stoppropagation()
// do the job here
}
Jsx code html here

How to set srcObj for a newly created video element in react dom? Also how can I play() this newly created video element?

I am trying to create a video element in my react component and then set a srcObject to it/ be able to play it after. So basically once the new video renders, I want to do this.video.srcObject = 'a stream' and this.video.play(). Is there something like componentDidMount but for the newly created video element?
class Video extends React.component{
renderPeers = () => {
return (<video ref={(video) => {this.video = video}}></video>)
}
render() {
return (
<div>
{this.renderPeers()}
</div>
)
}
}

Return Null after calling .getElementById in react code

I am new to react and HTML5 and I try to create a canvas that has 3 images one under the other. I am using react for that.
I create a function that renders an App component. This function returns something like this
return (
<div className="App">
<canvas className="canvas1" id="canvas1" width="70" height="300"></canvas>
{Game()}
</div>
);
The Game is the function that(lives inside the App function) I want to preload the images and do some initial changes to the canvas element. The implementation is something like this:
function Game() {
var c1 = document.getElementById("canvas1").getContext("2d");
//Here the code will do changes to the canvas element
this.draw(true);
}
The problem that I am facing is that the code above does not compile. I get error that getElementById returns Null.
TypeError: document.getElementById(...) is null
I can not understand what is going on. I suspect that something has not loaded correctly and there is nothing at c1 when I call getElementById but I can not find out how to check this possibility.
What can I do to start debugging it?
Thanks for your time.
When you want to operate on dom directly consider using refs, below is hooks implementation.
const App = () => {
const ref = React.useRef(null);
React.useEffect(() => {
const canvas = ref.current
const ctx = canvas.getContext("2d")
}, [])
return (
<div className="App">
<canvas className="canvas1" ref={ref} width="70" height="300"></canvas>
{Game()}
</div>
);
}
There is no reference to image
eg:
class Canvas extends React.Component {
render() {
return(
<div>
<canvas ref="canvas" width={640} height={425} />
<img ref="image" src={cheese} className="hidden" />
</div>
)
}
}
export default Canvas

Triggering a function via onClick with a React-Bootstrap ListGroup

Given two components, one just being the render of a react-bootstrap ListGroup, and the other being a function containing a console.log print out, I'm running into trouble seeing that printout when using onClick in the listgroup item. I'm building a music player and the list group contains songs to click on.
When I try to remove <Player Player = /> from the ListGroupItem, it becomes a span element instead of a button. I could print a console.log from within the div but I was going to use the handleClick function to start working on song switching, and as the function is listed as undefined I'm kinda stuck.
In player.js (where the actual music player component is contained)
handleClick = (e) => {
e.preventDefault();
console.log('hello');
}
In Songlist.js
class Songlist extends Component {
constructor(props) {
super(props);
}
Songlist = () => {
return(
<div className="listgroup">
<ListGroup>
<ListGroupItem onClick=<Player Player = {this.handleClick} />>
button text goes here
</ListGroupItem >
//more list group items go here
</ListGroup>
</div>
);
}
render() {
return (
<div className="Songlist">
{this.Songlist()}
</div>
);
}
}
export default Songlist;
The actual error message I'm getting is "Error: Expected onClick listener to be a function, instead got a value of object type." But since I'm using an arrow function nothing needs to be bound, and I'm pretty sure that's the syntax for passing a function to an onClick listener no?
You need to do the following because onClick expects a function, instead of the object you are giving it.
<ListGroupItem onClick=<Player Player = {this.handleClick} />>
should become
<ListGroupItem onClick={() => <Player Player = {this.handleClick} />}>

Set TimeOut to React Function

I have the following object list:
mediaList[
{id:1, url:"www.example.com/image1", adType:"image/jpeg"},
{id:2, url:"www.example.com/image2", adType:"image/jpg"},
{id:3, url:"www.example.com/video1", adType: "video/mp4"}
]
I need to create a slideshow that has a configurable duration (1s, 5, 10s).
So far I can generate a list of the media from the mediaList
renderSlideshow(ad){
let adType =ad.adType;
if(type.includes("image")){
return(
<div className="imagePreview">
<img src={ad.url} />
</div>
);
}else if (adType.includes("video")){
return(
<video className="videoPreview" controls>
<source src={ad.url} type={adType}/>
Your browser does not support the video tag.
</video>
)
}
}
render(){
return(
<div>
{this.props.mediaList.map(this.renderSlideshow.bind(this))}
</div>
)
}
What I want to do now is generate the media one at a time with configurable durations for autoplay.
I know I need to use some form of a setTimeout function like this example:
setTimeout(function(){
this.setState({submitted:false});
}.bind(this),5000); // wait 5 seconds, then reset to false
I'm just not sure how to implement it for this scenario. (I believe I'll need to use css for the fade transitions but I'm just stumped on how to create the transition in the first place)
If you want to change the media on every 5 seconds, you will have to update the state to re-render your components You can also use setInterval instead of setTimeout. setTimeout will be triggered only once, setInterval will be triggered every X milliseconds. Here what it may look like:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { activeMediaIndex: 0 };
}
componentDidMount() {
setInterval(this.changeActiveMedia.bind(this), 5000);
}
changeActiveMedia() {
const mediaListLength = this.props.medias.length;
let nextMediaIndex = this.state.activeMediaIndex + 1;
if(nextMediaIndex >= mediaListLength) {
nextMediaIndex = 0;
}
this.setState({ activeMediaIndex:nextMediaIndex });
}
renderSlideshow(){
const ad = this.props.medias[this.state.activeMediaIndex];
let adType = ad.adType;
if(type.includes("image")){
return(
<div className="imagePreview">
<img src={ad.url} />
</div>
);
}else if (adType.includes("video")){
return(
<video className="videoPreview" controls>
<source src={ad.url} type={adType}/>
Your browser does not support the video tag.
</video>
)
}
}
render(){
return(
<div>
{this.renderSlideshow()}
</div>
)
}
}
Basically what the code is doing is that every 5 seconds, will change the activeMediaIndex to the next one. By updating the state, you will trigger a re-render. When rendering the media, just render one media (you can also render the previous one and the next one like a classic slideshow). Thus way, every 5 seconds, you will render a new media.
Don't forget to clear the timeout to prevent memory leaks:
componentDidMount() {
this.timeout = setTimeout(() => {
...
}, 300);
}
componentWillUnmount() {
clearTimeout(this.timeout);
}

Categories