Fork me on GitHub

has anyone had to deal with a customising an audio tag/player in reagent? I'm having a bit of trouble translating standard js ways into anything clojureish


i haven’t dont it myself but i helped someone work through this


anything you do in in js is possible in cljs @jcb


yeah I'm not that well versed in it, since I can't find any cljs examples that don't use an extern I'm trying to translate myself and it's getting a bit dicey


let me know what you’re trying to do and i can try to help


thanks, each page has multiple audio files and I need to style a very generic audio player - show duration, playback position etc


I've not dealt with extracting multiple values from eventlisteners before, mainly just simple on-clicks


so I'm passing into a component an array of maps which contains general view data (title/url etc) then for each need to build a player


assigning an unique id to that I can get the element


hang on, is the audio player all reagent code around an audio tag or is this a npm library?


all reagent


so really I guess the abstract question is about adding and using event listeners


with atoms


ok. what does this mean: assigning an unique id to that I can get the element?


do you mean get the underlying dom element?


yes, this is where it feels very js


rather than reagent


first off, you should just use a ref callback and assign it to a regular atom rather than looking the element up. this is how you would do it even in javascript with plain react


that’s not your issue though


i’m not sure i understand where the event listeners come into play


thanks that's super useful already.


in the js examples I'm using they use event listeners to get feedback from the file of duration and current time


as a general matter, when you’re dealing with the dom apis that require you to make a side effect when something changes (i.e. start playback), the cleanest strategy is to separate all of that code from the presentation of the buttons and so forth. you make a component that has no dom representation and just looks to see when its props change using a form-3 component and causes the proper side effects in the dom


then you render the side-effecting component as a sibling to the presentation and hook it all up with some kind of atom.


if you past the examples, it should be straightfoward to translate


ok, I'll have to look into this as I'm not sure I completely understand, the js example is quite long


var music = document.getElementById('music'); // id for audio element var duration = music.duration; // Duration of audio clip, calculated here for embedding purposes var pButton = document.getElementById('pButton'); // play button var playhead = document.getElementById('playhead'); // playhead var timeline = document.getElementById('timeline'); // timeline // timeline width adjusted for playhead var timelineWidth = timeline.offsetWidth - playhead.offsetWidth; // play button event listenter pButton.addEventListener("click", play); // timeupdate event listener music.addEventListener("timeupdate", timeUpdate, false); // makes timeline clickable timeline.addEventListener("click", function(event) { moveplayhead(event); music.currentTime = duration * clickPercent(event); }, false); // returns click as decimal (.77) of the total timelineWidth function clickPercent(event) { return (event.clientX - getPosition(timeline)) / timelineWidth; } // makes playhead draggable playhead.addEventListener('mousedown', mouseDown, false); window.addEventListener('mouseup', mouseUp, false); // Boolean value so that audio position is updated only when the playhead is released var onplayhead = false; // mouseDown EventListener function mouseDown() { onplayhead = true; window.addEventListener('mousemove', moveplayhead, true); music.removeEventListener('timeupdate', timeUpdate, false); } // mouseUp EventListener // getting input from all mouse clicks function mouseUp(event) { if (onplayhead == true) { moveplayhead(event); window.removeEventListener('mousemove', moveplayhead, true); // change current time music.currentTime = duration * clickPercent(event); music.addEventListener('timeupdate', timeUpdate, false); } onplayhead = false; } // mousemove EventListener // Moves playhead as user drags function moveplayhead(event) { var newMargLeft = event.clientX - getPosition(timeline); if (newMargLeft >= 0 && newMargLeft <= timelineWidth) { = newMargLeft + "px"; } if (newMargLeft < 0) { = "0px"; } if (newMargLeft > timelineWidth) { = timelineWidth + "px"; } } // timeUpdate // Synchronizes playhead position with current point in audio function timeUpdate() { var playPercent = timelineWidth * (music.currentTime / duration); = playPercent + "px"; if (music.currentTime == duration) { pButton.className = ""; pButton.className = "play"; } } //Play and Pause function play() { // start music if (music.paused) {; // remove play, add pause pButton.className = ""; pButton.className = "pause"; } else { // pause music music.pause(); // remove pause, add play pButton.className = ""; pButton.className = "play"; } } // Gets audio file duration music.addEventListener("canplaythrough", function() { duration = music.duration; }, false); // getPosition // Returns elements left position relative to top-left of viewport function getPosition(el) { return el.getBoundingClientRect().left; }


it's all basically the same form - add an event listener then extract a value


this example is a plain javascript version way of doing this. it will work, but you might have better luck importing a react library that already wraps all of this stuff up for you.


you can do any of this stuff in cljs if you want, though


the react examples are very similar but use this a lot


is that what you were referring to with the callback ref



music.addEventListener("canplaythrough", function() {
   duration = music.duration;
}, false);
turns into
(.addEventListener music "canplaythrough" (fn [] (reset! duration (.-duration music))) false)
or something like that


refs are a mechanism in react of getting the underlying dom element. useful for wrapping things like canvas or audio where you have to call a method on the dom element


from the react example -


var Timestamps = React.createClass({ render: function() { return ( <div className="Timestamps"> <div className="Time Time--current">{this.props.currentTime}</div> <div className="Time Time--total">{this.props.duration}</div> </div> )} });


I'm not sure how to translate this


That example is using a very ancient style for what it is worth. It would look something like this:

(defn timestamps [current-time duration] 
     [:div {:class "Time Time--current"} (str current-time)] 
     [:div {:class "Time Time--total"} (str duration)]])


sorry, it's only from last year!


I guess things move quickly over there


yea react jumped on the es6 bandwagon quickly


at any rate, that’s roughly how you’d do it in reagent


so the example uses .focus in the ref, would it be a key in the ref atom per event?


i’m not following your question


sorry from the form-3 example you posted


the ref atom will contain the dom element. so this (some-> @!ref .focus) is just calling the focus() method on whatever dom element the top-level element of list-view renders to


I see, so there would be a "glue" atom to store values?


.focus is a method on HTMLElement so no matter what list-view renders to this particular method will happen to work


sorry, just dealing with a new concept


the way react ref callbacks work is that once the dom element mounts, the ref callback is called with the dom element as the argument. you can do whatever you want with it, but the typical approach is to use a normal atom and store it there. then in the component-did-mount and component-did-update lifecycle methods you can do whatever dom manipulation you want.


you can also use it in render which sometimes is the better choice


if you only need it in render then you can get away with a form-2 component


ok thanks!