For this html:
<html>
<div id="vids">
<video id="vid-1" src="vid1.mp4">
</video>
<video id="vid-2" src="vid2.mp4">
</video>
</div>
</html>
The videos show up correctly.
I want to add an overlay text (caption) on top of each video like:
"Video 1 - Greenery"
"Video 2 - Island"
How can I do this using Javascript alone without modifying the HTML?
If not overlay, how do I display caption right above or below each video.
I have tried this:
var vid = document.getElementById("vid-1");
vid.insertAdjacentHTML("aftereend","Video 1 - Greenery");
But the text "Video 1 - Greenery", appears on right side of video.
If it appeared directly above or below video that would work too.
TL;DR:
Prefer to use <figcaption> (and <figure>).
"Placing above" is solved with CSS; see e.g. position: absolute.
We can map video IDs to captions. Then for all entries, we can create and insert the captions before their corresponding videos:
const idToCaption = new Map();
idToCaption.set("vid-1", "Video 1 - Greenery");
idToCaption.set("vid-2", "Video 2 - Island");
for (const [id, caption] of idToCaption.entries()) {
const p = document.createElement("p");
p.textContent = caption;
const video = document.getElementById(id);
// Assign caption as label
p.id = `${id}-label`;
video.setAttribute("aria-labelledby", p.id);
video.parentElement.insertBefore(p, video);
}
<div id="vids">
<video id="vid-1" src="vid1.mp4"></video>
<video id="vid-2" src="vid2.mp4"></video>
</div>
Note:
To place the captions after their videos, reference the video's next sibling.
For accessibility reasons we should declare the captions to label the videos, e.g. via aria-labelledby. (Captions labelling content is done implicitly when using <figcaption> as below.)
Without styling, the captions will come before their videos. To place them above their videos we need styling (CSS).
Placing elements above others can be achieved by taking them out of the flow, e.g. with position: absolute.
We want to position the captions relative to the videos. But the captions are siblings and not children of the videos, which makes relative positioning more difficult.
We can wrap each caption and its video in another element, which will effectively represent the video. That way, the caption is a child of the "video" (the wrapper), which makes positioning it correctly easier:
const idToCaption = new Map();
idToCaption.set("vid-1", "Video 1 - Greenery");
idToCaption.set("vid-2", "Video 2 - Island");
for (const [id, caption] of idToCaption.entries()) {
const p = document.createElement("p");
p.textContent = caption;
const video = document.getElementById(id);
p.id = `${id}-label`;
video.setAttribute("aria-labelledby", p.id);
// New code starts here
const wrapper = document.createElement("span");
wrapper.classList.add("wrapper");
p.classList.add("caption");
video.replaceWith(wrapper);
wrapper.append(p, video);
}
.caption {
position: absolute;
/*Example: Position at top-left*/
top: 0;
left: 0;
}
.wrapper {
/* `position:absolute` elements are relative to
* first non-`static` ancestor.
* Make wrapper non-`static`. */
position: relative;
/* `display:inline-block` makes
* `.wrapper` behave like <video>,
* but contain it (here: inherit its size). */
display: inline-block;
}
/*Ignore; presentational*/
.caption {
margin: 0;
color: white;
background-color: black;
}
video {border: 1px solid black}
<div id="vids">
<video id="vid-1" src="vid1.mp4"></video>
<video id="vid-2" src="vid2.mp4"></video>
</div>
There already exist elements for captions and the wrapper: <figcaption> and <figure>. You should prefer these where applicable, e.g. here:
const idToCaption = new Map();
idToCaption.set("vid-1", "Video 1 - Greenery");
idToCaption.set("vid-2", "Video 2 - Island");
for (const [id, caption] of idToCaption.entries()) {
const figcaption = document.createElement("figcaption");
figcaption.textContent = caption;
const video = document.getElementById(id);
/* Notice that <figcaption> automatically applies to its
* <figure>'s other content. No "aria-labelledby" required anymore! */
const figure = document.createElement("figure");
video.replaceWith(figure);
figure.append(figcaption, video);
}
figcaption {
position: absolute;
top: 0;
left: 0;
}
figure {position: relative}
/*Ignore; presentational*/
figcaption {
margin: 0;
color: white;
background-color: black;
}
video {border: 1px solid black}
<div id="vids">
<video id="vid-1" src="vid1.mp4"></video>
<video id="vid-2" src="vid2.mp4"></video>
</div>
Note: The <figure> element is display: block whereas <video> is display: inline by default. The stylesheet should fix this inconsistency.
// Get the video element
var vid1 = document.getElementById("vid-1");
var vid2 = document.getElementById("vid-2");
// Create a new element for the caption
var caption1 = document.createElement("p");
var caption2 = document.createElement("p");
// Set the text for the caption
caption1.innerHTML = "Video 1 - Greenery";
caption2.innerHTML = "Video 2 - Island";
// Position the caption on top of the video
caption1.style.position = "absolute";
caption2.style.position = "absolute";
caption1.style.top = vid1.offsetTop + "px";
caption1.style.left = vid1.offsetLeft + "px";
caption2.style.top = vid2.offsetTop + "px";
caption2.style.left = vid2.offsetLeft + "px";
// Add the caption to the document
document.body.appendChild(caption1);
document.body.appendChild(caption2);
var customStyle="z-index: 2; background: red; position: absolute; top: 50%; right: 50%;";
var vid = document.querySelector("video");
vid.insertAdjacentHTML("afterend",`<p style="${customStyle}">Video 1 - Greenery</p>`);
Style is just for example purposes.
Naturallly better is to use file styles.
insertAdjacentHTML renders an Element, so if You put a raw text, then it creates a textNode.
Better to wrap it in some tag - then style has access to it.
using ${customStyle} You make it easier to maintain.
It's easier to set dynamic elements with it's inline styes than to operate on window stylesheets.
This solution makes styling Your text possible.
Related
I added an image to the Trix editor, generating the following code:
<figure
data-trix-attachment="{lots of data}"
data-trix-content-type="image/jpeg"
data-trix-attributes="{'presentation':'gallery'}"
class="attachment attachment--preview attachment--jpg">
<img src="http://myhost/myimage.jpg" width="5731" height="3821">
<figcaption class="attachment__caption">
<span class="attachment__name">cool.jpg</span> <span class="attachment__size">4.1 MB</span>
</figcaption>
</figure>
When I display the generated HTML from the editor on my Bootstrap-based page, the image obviously extends the screen (see the width and height) and I'd like to remove these props and also assign the img-fluid class to it.
So basically I thought to use the config:
Trix.config.css.attachment = 'img-fluid'
But that does a) not change the attachment class to img-fluid and it also would not apply the changes to the image but the figure.
I would like to avoid using jQuery each time I display the content and traverse all figures and then manipulate the image's properties at runtime.
Isn't there a solution to define these styles when adding the attachment?
Trix does not have any kind of support to change the image element inside the attachment. One way to do it is by using MutationObserver to check for mutations inside Trix editor that apply to attributes, childList and subtree.
If we have a width or height attributes mutation to an img target node with a figure parent node, then we remove those attributes and we can apply the class img-fluid to the first attribute mutation, for example width.
Run code snippet and try to add some image attachments to see or inspect the HTML
Please read inline comments
// Listen to trix-attachment-add event so we'll get rid of the progress bar just for this demo
// Here we should upload the attachment and handle progress properly
document.addEventListener("trix-attachment-add", event => {
const { attachment } = event.attachment;
// Get rid of the progress bar
attachment.setUploadProgress(100)
});
// Get the Trix editor
const editor = document.querySelector('trix-editor');
// Instantiating an observer
const observer = new MutationObserver(function (mutations) {
mutations.forEach(({ type, target, attributeName }) => {
// If the parent is a figure with an img target
if (target.parentNode.tagName === 'FIGURE' &&
target.nodeName === 'IMG')
{
if (type === 'attributes') {
switch(attributeName) {
// If we have attribute width
case 'width':
// Remove attribute width
target.removeAttribute('width');
// Add img-fluid only once
target.classList.add('img-fluid');
break;
// If we have attribute height
case 'height':
// Remove attribute height
target.removeAttribute('height');
break;
}
}
// Render images HTML code
renderHtmlOutput();
}
});
});
// Observing Trix Editor
observer.observe(editor, {
attributes: true,
childList: true,
subtree: true
});
// Function to render every figure > img HTML code
function renderHtmlOutput() {
const images = editor.querySelectorAll('figure > img');
let output = '';
for(const image of images) {
output += image.outerHTML.replace(/ /g, "\n ") + "\n";
}
document.getElementById('output-html').textContent = output;
}
body {
height: 100vh;
margin: 0;
flex-direction: column;
display: flex;
}
#main {
display: flex;
flex-direction: row;
flex: 1;
margin: 10px;
}
#editor-container {
flex: 3;
}
#output-container {
flex: 2;
margin-left: 20px;
border-left: 1px solid lightgray;
overflow: auto;
}
#output-html {
margin: 0;
padding: 10px;
font-size: small;
color: blue;
}
/* Hide some Trix buttons to free horizontal space */
.trix-button--icon-increase-nesting-level,
.trix-button--icon-decrease-nesting-level,
.trix-button--icon-bullet-list,
.trix-button--icon-number-list { display: none; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/trix/1.2.1/trix.js" integrity="sha256-2D+ZJyeHHlEMmtuQTVtXt1gl0zRLKr51OCxyFfmFIBM=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/trix/1.2.1/trix.css" integrity="sha256-yebzx8LjuetQ3l4hhQ5eNaOxVLgqaY1y8JcrXuJrAOg=" crossorigin="anonymous"/>
<section id="main">
<div id="editor-container">
<form>
<input id="editor" value="Editor content goes here" type="hidden" name="content">
<trix-editor input="editor"></trix-editor>
</form>
</div>
<div id="output-container">
<pre id="output-html"></pre>
</div>
</section>
I have a set of divs with background images. Im trying to get the image onclick then display it in a lightbox. When the lightbox pops, the image i get in undefined. I can get the image url to display correctly through console. I know lbImg.src = galImg.src; is incorrect but i am unsure how to fix it.
HTML
<div id="lightbox">
<span class="closeBTN">×</span>
<img class="lbContent" src="">
</div>
<div class="imgCon">
</div>
CSS
.imgCon
{
height: 500px;
width: 15%;
min-width: 200px;
background-image: url(../images/01.jpg);
background-position: center;
background-size: auto 500px;
margin-bottom: 66px;
}
JS
var lightbox = document.querySelector('#lightbox')
img = document.querySelector('.imgCon')
span = document.querySelector('.closeBTN')
lbImg = document.querySelector('.lbContent')
img.onclick = (function(){
lightbox.style.display = "block";
var galImg = window.getComputedStyle(this,
null).getPropertyValue("background-Image");
lbImg.src = galImg.src;
//console.log(galImg);
})
span.onclick = (function(){
lightbox.style.display = "none";
});
You get the background-image itself in galImg, so you need to set it directly (it is a string and does not have a .src property).
But, the value of galImg we obtain from getComputedStyle is a string like as: "url("file:.../Desktop/1.png")". We have to remove the url( part and also the quotation marks ("...") from it:
lbImg.src = galImg.slice(4, -1).replace(/"/g, "");
Can you please try lbImg.setAttribute('src', galImg.src).
I have been tasked to take some pre-existing code which has a float event on an org chart. When you hover over an individual on the org chart a picture pops up and displays all the employees under them.
They don't like that and would like it changed to a click event. I've listed the code for the float event but what would be an example of taking the contentFloating and making it a click event? Looking for the proper syntax please.
}
.orgContainer .contentStable .contentFloating img {
height: 720px;
width: 960px;
}
/* Float content */
.orgContainer .contentFloating {
border: 2px solid black;
display: none;
position: fixed;
}
.orgContainer .contentStable:hover .contentFloating {
display: block;
top: 7%;
left: 15%;
}
<div class="contentStable">
<img title="Scott Plemmons Director Supplier Quality Compliance" src="/sites/scm/utas_supp_qual/Home_Pictures/scott_plemmons.jpg" />
<p>
Scott Plemmons<br/> Director
<br/> Supplier Quality
</p>
<div class="contentFloating">
<img src="/sites/scm/utas_supp_qual/Home_Pictures/Org_Charts/Scott_Plemmons_Chart.jpg" />
</div>
</div>
This is an example of how to add a click event handler to a dom element, and then make another div appear when you click on it.
html
<div class="orgchart">click for details</div>
<div id="details">details</div>
css
#details {
position:absolute;
}
javascript
let elements = document.getElementsByClassName("orgchart");
for(var i = 0; i < elements.length; i++)
{
elements[i].addEventListener("click", function(e){
// get the position of the element that was clicked
let clickedItem = e.target;
var rect = clickedItem.getBoundingClientRect();
// set the details div to that position and fill it with content
let details = document.getElementById("details");
details.innerHTML = "Joe Average";
details.style.left = (rect.left + 10) + "px";
details.style.top = (rect.top + 20) + "px";
});
}
Check a jsfiddle example here
So basically I found a really cool example for cycling background images using JavaScript and jQuery.
What would be the best approach to adding an overlay text description effect for each slide?
So for instance, each slide will also have a text description overlayed somewhere on each image with it's own style . Would it be possible to have this text also come in with it's own effects. So the image fades in, and then the text description slides in from the left, and so on
HTML
<body>
<div id="background_cycler">
<img class="active" src="https://upload.wikimedia.org/wikipedia/commons/thumb/c/c2/View_from_a_ridge_between_Segla_and_Hesten%2C_Senja%2C_Norway%2C_2014_August.jpg/1920px-View_from_a_ridge_between_Segla_and_Hesten%2C_Senja%2C_Norway%2C_2014_August.jpg" width="1000px" height="1000px" alt="" />
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/8/87/Amanhecer_no_Hercules_--.jpg/1920px-Amanhecer_no_Hercules_--.jpg" alt="" width="1000px" height="1000px" />
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/f/fc/Giant_Manta_AdF.jpg/1920px-Giant_Manta_AdF.jpg" alt="" width="1000px" height="1000px" />
</div>
JQUERY
$('#background_cycler').hide();
window.cycleImages = function() {
var $active = $('#background_cycler .active');
var $next = ($('#background_cycler .active').next().length > 0) ? $('#background_cycler .active').next() : $('#background_cycler img:first');
$next.css('z-index', 2); //move the next image up the pile
$active.fadeOut(1500, function() { //fade out the top image
$active.css('z-index', 1).show().removeClass('active'); //reset the z-index and unhide the image
$next.css('z-index', 3).addClass('active'); //make the next image the top one
});
}
$(window).load(function() {
$('#background_cycler').fadeIn(1500); //fade the background back in once all the images are loaded
// run every 7s
setInterval('cycleImages()', 7000);
})
CSS
#background_cycler {
padding: 0;
margin: 0;
width: 100%;
position: absolute;
top: 0;
left: 0;
z-index: -1;
}
#background_cycler img {
position: absolute;
left: 0;
top: 0;
width: 100%;
z-index: 1;
}
#background_cycler img.active {
z-index: 3;
}
Here's a solution like Andy's, but keeping the img tags:
https://jsfiddle.net/2y8fo13d/
Instead of using img tags, you can use more generic div tags to act as the slides. Then you can fill them with whatever content you wish, check out this as an example:
https://jsfiddle.net/00gow9Lt/1/
Here I have used div tags with some simple text and the original images as backgrounds.
You can style each one's content separately with css for a different look, but to have sliding effects on the text itself would require significantly more code. For that I would suggest reading up on jQuery animations in more detail.
I am creating a slideshow using JavaScript. I am positioning the controls (the next and previous buttons) with JS.
The HTML, when the buttons are added via JS, looks like this:
<div id="slideshow">
<figure>
<img src="https://c4.staticflickr.com/8/7495/16322256485_08ee0ee36f_z.jpg">
<figcaption>This is an example of a really long caption. Here I go. Do I wrap to a second line? Wrap wrap wrap wrap. Wrap Wrap Wrap Wrap Wrap Wrap Wrap Wrap Wrap Wrap Wrap</figcaption>
</figure>
<figure class="hide">
<img src="https://c1.staticflickr.com/9/8584/16136057529_e7b64928d0_z.jpg">
<figcaption>Insert caption</figcaption>
</figure>
<figure class="hide">
<img src="https://c2.staticflickr.com/8/7474/16120961661_8dc12962dd_z.jpg">
<figcaption>Insert caption</figcaption>
</figure>
<span id="next"><p>>></p></span>
<span id="previous"><p><<</p></span>
</div>
<!-- end slideshow -->
The slide is made up of a figure element with an image and a caption.
I want the controls (two spans) to be positioned vertically in the middle of the image (not taking the height of the caption into account, which could vary).
The spans are positioned like this:
#previous,#next {
height:0;
padding-bottom:65.9%;
/* Calculate this with JS so can adjust based on img height */
position:absolute;
top:0
}
#previous p,#next p {
margin:0;
display:table;
position:absolute;
/* positioned in relation to the #previous and #next spans */
top:50%;
-moz-transform:translateY(-50%);
-webkit-transform:translateY(-50%);
-o-transform:translateY(-50%);
-ms-transform:translateY(-50%);
transform:translateY(-50%)
}
#previous p {
left:0;
border-radius:0 5px 5px 0;
}
#next p {
right:0; /*moves button to right corner of span instead of left */
border-radius:5px 0 0 5px;
}
#previous {
left: 0;
}
#next {
right: 0;
}
The #previous and #next spans have a padding-bottom that extends it to the height of the image only (does not include the captions) and then the actual text of the buttons are positioned in relation to the span.
I need to calculate the padding-bottom based on the image height. But with different slideshow uses, the image height may change. Another slideshow may use images of a different dimension.
So I want JavaScript to calculate this padding and insert it into the stylesheet. I have the JS for the calculation:
// Calculate position of buttons
var getImage = photos[0].firstElementChild;
var imageHeight = (getImage.height);
var imageWidth = (getImage.width);
var paddingTop = (imageHeight / imageWidth) * 100;
paddingTop = +paddingTop.toFixed(2);
var percent = paddingTop + "%";
I just can't figure out how to add the percent variable to the stylesheet so that the rule is:
#previous,#next {
padding-bottom:[insert % from JS];
}
Here is my JSFiddle: http://jsfiddle.net/amykirst/17rbku86/
Use this:
document.getElementById("previous").style.paddingBottom = percent;
document.getElementById("next").style.paddingBottom = percent;
FIDDLE: https://jsfiddle.net/lmgonzalves/17rbku86/3/