How to reoder video tiles in Jitsi? - javascript

Last year, I wrote a bookmarklet that allowed to reorder Jitsi video tiles in alphabetic order.
Unfortunately, the script does not seem to work with the recent Jitsi versions anymore.
I have already adjusted the access to the required elements, this is working fine again.
However, the order: n attribute of the elements is ignored know, even when I set the parent to display:flex, remove the position: absolute and the left|top:n px.
I played around with the CSS and looked into documentation, but given I am no frontender, I am currently stuck.
How can I change the CSS, so that the tiles are reordered?
As a clarification, I am only looking for where I need which changes in the CSS, no scripted solution. I can take care of the scripting. Achieving a swap of two video tiles while not breaking the look would be sufficient probably.
The below information are not necessary, but might be helpful to answer the question.
Below is a screenshot showing the position in the DOM tree:
DOM tree leading to the video tiles (seems I need more reputation to embed the image)
And here is the the javascript that allows to directly access the elements:
var numberOfVideos;
function getNumberOfParticipants(){
// works
}
function getNameOfParticipant(i){
var container = $('#remoteVideos')[0];
var jChildren = $(container).children();
var jChildren2 = jChildren[1].firstChild.children;
return [jChildren2[i].getElementsByClassName("displayname")[0].innerHTML, jChildren2[i] ];
}
function sortParticipants(){
numberOfVideos = getNumberOfParticipants();
var names = new Array();
//only applicable in tiles mode!
for(i=0; i<numberOfVideos; i++) {
names[i] = new Array (2);
names[i] = getNameOfParticipant(i);
}
//sort Array
names.sort((a, b) => a[0].localeCompare(b[0]));
//reorder the tiles
for(i = 0; i < numberOfVideos; i++){
$(names[i][1]).css('order', i); //this is the line that worked in 2021, but the `order` attribute is now being ignored
}
}
https://github.com/kaiyazeera/jitsi-bookmarklets/blob/master/alphabeticSort-auto0.js

The problem could be solved by modfying the global CSS:
var css = document.createElement("style");
css.type = "text/css";
css.innerHTML = ".videocontainer { position:relative !important; top: unset !important; left: unset !important; text-align:center;overflow:hidden; }"
var css2 = document.createElement("style");
css2.type = "text/css";
css2.innerHTML = ".tile-view .remote-videos > div {flex-wrap:wrap;}"
document.body.appendChild(css)
document.body.appendChild(css2)

Related

Javascript – PNG not showing when adding dynamically

I’m a beginner JS coder and I’m struggling with the following – can anyone please help?
I’m trying to add a series of PNGs to a page using a function which will allow the placement of multiple copies of the same image and also assign a unique reference to each copy of the image.
The images are not showing in the page, plus the console.log() shows that the 2 images created by the code below both have the same position on the page.
var imgSrc = 'arrow_red.png';
function generateArrow(numArrows) {
var img = document.createElement('img');
img.src = imgSrc;
for (i = 1; i <= numArrows; i++) {
window['arrow'+i] = img;
}
}
generateArrow(2);
arrow1.style.position = 'absolute';
arrow1.style.top = '50px';
arrow1.style.left = '50px';
arrow2.style.position = 'absolute';
arrow2.style.top = '100px';
arrow2.style.left = '100px';
console.log(arrow1);
console.log(arrow2);
Why are the images not showing in the page and why does the console.log() show that the 2 images created are both using the same positional co-ordinates?
When you create a new element, it only exists in memory - - it hasn't been added to the document that the browser is currently rendering. So, it's not enough to create new elements and configure them. You must then inject them into the DOM with parentElement.appendChild(newChild).
Here's an example:
let newChild = document.createElement("img");
newChild.src = "https://static.scientificamerican.com/sciam/cache/file/D14B1C22-04F8-4FB4-95937D13A0B76545.jpg?w=590&h=393";
let parent = document.querySelector(".parent");
parent.appendChild(newChild); // <-- Now, inject the new element
img { width: 400px; }
<div class="parent"></div>
Now, in your particular case, you've got more issues than just this to work on. You are only creating a new image element one time because your line that does that is not inside of your loop. Also, the way you are referring to arrow1 and arrow2 in your code and with window['arrow' + i] indicates that you have img elements with ids already set up in your HTML, which is not an ideal approach. Next, it's much simpler to set up the CSS you'll want to work with as pre-made classes ahead of time, rather than setting up the CSS as inline styles in the script.
As my answer above indicates, you need to have a parent element that will contain the new element(s) that you create, so your solution would really look something like this:
var imgSrc = 'https://icon2.kisspng.com/20180320/rle/kisspng-arrow-computer-icons-clip-art-red-arrow-line-png-5ab19d059bfa98.5843437015215895096389.jpg';
// You can pick any pre-existing element to be the "parent"
var parent = document.getElementById("parent");
function generateArrow(numArrows) {
for (i = 1; i <= numArrows; i++) {
// The creation of the elementt and it's configuration
// need to be inside of the loop to make several of them
var img = document.createElement('img');
img.classList.add("position" + i); // Add pre-made CSS classes
img.src = imgSrc;
parent.appendChild(img); // Inject the new element inside of the parent
}
}
generateArrow(5);
/*
Instead of setting inline styles, use pre-made CSS classes
that you can just connect or disconnect to/from
*/
/* All the injected images get this: */
#parent > img { width:40px; position:absolute; }
/* These get assigned individually */
.position1 { top:50px; left:50px; }
.position2 { top:100px; left:100px; }
.position3 { top:150px; left:150px; }
.position4 { top:200px; left:200px; }
.position5 { top:250px; left:250px; }
<div id="parent"></div>
You usually add elements to DOM using document.appendChild(element);, or in your case: document.appendChild(img);. (Or any preferred parent instead of document)
Edit: removed second part addressing variable declaration, since I didn't notice the window["arrow" + i] = img.
You need to add the generated element to the DOM using the appendChild() method.
Furhermore you're actually just generating a single instance of the image because it's happening once outside of the for-loop. This is why the console shows identical screen positions for 'both' images because actually you're referring to the same image instance.
Try this:
function generateArrow(numArrows) {
var img;
for (i = 1; i <= numArrows; i++) {
img = document.createElement('img');
img.src = imgSrc;
document.body.appendChild(img);
window['arrow' + i] = img;
}
}

Placing elements within a container div into an array - jQuery or JavaScript

I have a div that contains a number of Instagram images, produced by the instafeed.js plugin. After running the plugin, the resultant HTML looks like this:
<div id="instafeed">
<a><img /></a>
<a><img /></a>
<a><img /></a>
etc...
</div>
I am trying to find a way to load the contents of this div into an array; I believe that the easiest way would be to just take the tags, which is fine.
I'm pretty inexperienced with both JS and jQuery, which is why I'm having difficulty achieving this and I've not been able to find any forum posts that quite do what I'm hoping to achieve.
So far, all I'm trying to do is load the contents of the div into an array and print it back out to the document, which should (in my mind anyway) add the tags back into the HTML. I'm trying with both JavaScript and jQuery and having little success with either. I'd appreciate any thoughts:
JS:
var containerDiv = document.getElementById('instafeed');
var pics = containerDiv.getElementsByTagName('img');
console.log(pics); //Tells me at least that I have an array of img
for (var i = 0; i < pics.length; i++) {
document.write(pics[i]);
} //Seemingly does nothing
jQuery:
(I'm really sorry if this code is just all wrong, I really don't know jQuery very well at all)
$(document).ready(function() {
var pics = [];
$('#instafeed').find('img').each(function() {
pics.push($(this));
});
for (i = 0; i < pics.length; i++) {
console.log(pics[i]);
}
});
Any thoughts, tips or pointers would be much appreciated.
Edit:
Just to add a little background to my problem, to avoid causing any more confusion.
I'm trying to pull four random images from a user-specific Instagram feed for display on a website. instafeed.js can pull just four images and it can randomise the images, but Instagram itself always sends the four most recent images, so the plugin is just randomising the order of the same four pictures each time.
I'm trying to let the plugin send through every picture, which will go into the div instafeed. From here I want to load all of the contained images into an array so that I can randomly pick four images for display on the site.
JQuery code that you write is correct. Only you need the div where you need to put the images.
$(document).ready(function() {
var pics = [];
$('#instafeed').find('img').each(function() {
pics.push($(this));
});
for (i = 0; i < pics.length; i++) {
$('div#yourDiv').append(pics[i]);
}
});
See the line of the for()
You can extract only the SRC of the images and then make like you want
$('#instafeed').find('img').each(function() {
pics.push($(this).attr('src'));
});
console.log(pics); // returns an array of src.
Thank you to everyone who has tried to help me along with this. It turns out that the problem I was having stemmed from my query attempting to run before instafeed.js had been able to pull the images through from Instagram, and so there was nothing for it to find in the div. I've managed to fix this with a setTimeout.
For anyone who is interested, and just in case anyone else might come across this in future with a similar problem, here is my complete code (it's a little inelegant I'm sure, but I'm still a relative novice at JS.)
function snagImages() {
var pics = [];
$('div#instafeed').find('img').each(function() {
pics.push($(this).attr('src'));
});
reduceGallery(4, pics);
}
function reduceGallery(limit, pics) {
if (limit === undefined) {
limit = 4;
}
var gallery = [];
while (gallery.length < limit) {
var j = Math.floor(Math.random() * pics.length);
if ( gallery.indexOf(pics[j]) > -1) {
continue;
}
gallery.push(pics[j]);
}
displayPics(gallery);
}
function displayPics(gallery) {
for (var i = 0; i < gallery.length; i++) {
document.getElementById('gallery').innerHTML += '' + '<img src="' + gallery[i] + '" alt="Gallery Image" />' + '';
}
}
var userFeed = new Instafeed( {
options
});
userFeed.run();
setTimeout(function() { snagImages() }, 500);

JavaScript append gone crazy

I am trying to build a simple 3 grid gallery, the images inside it are all with different heights and I was after a tiled gallery... So I have made one that works with a bit of JavaScript.
However, when I tried to add another gallery in the same page, something weird happened, live preview here: http://loai.directory/test/gallery
The HTML, CSS and JS can be found fully placed in this jsfiddle: http://jsfiddle.net/aY5Gu
As you can see in the live preview, all the gridElemetns in grid3 are appending to grid3 in all the threeGrid galleries! After trying to debug, I have concluded that the problem is from the JS:
//Three Grids System
var gridElement = $(".gridElement", ".grid3");
GalleryGrid(); $(window).resize(GalleryGrid);
function GalleryGrid() {
var grid3 = $('.threeGrids .grid3');
var width = $(window).width();
if (width < 1024 && width > 770) {
var grid1 = $('.threeGrids .grid1');
var grid2 = $('.threeGrids .grid2');
for (var i = 0; i < gridElement.length; i++) {
if (i < gridElement.length / 2) {
grid1.append(gridElement[i]);
} else {
grid2.append(gridElement[i]);
}
}
} else {
grid3.append(gridElement);
}
}
What is going wrong? I can't seem to be able to go figure out what is wrong from there.
That's because .threeGrids and .grid1... appear more than one time on your page. Therefore jquery automatically appends things to all of them.
Try selecting by something like:
$('.wrapper').each(
function(){
var grids = $(this).find('.threeGrids');
(...do manipulation with grids...)
}
);
This way you enter each .wrapper separately and deal with only elements that are inside it.

Simple image rotation with pure javascript, no jQuery

trying to come up with very simple image rotation using pure javascript without jQuery.
Something that I could call like that and it could place the image in same spot rotating it one by one.
rotator('<img src="image1.gif"/ >','<img src="image1.gif"/ >');
maybe someone could suggest a way of doing it? thank you.
UPDATE: By Rotation I meant, one disappears, another appears. Not angle rotation.
This sort of steps beyond the call of duty and probably isn't the best solution but nonetheless. A full Javascript function (handles by tag not by image)
<html>
<head>
<script>
/*rotate
desc: Rotate a set of first level child objects based on tag name
params:
id = Rotate elements container id
tag = Tag (nodeName - see textNode issue) of DOM objects to be cycled
*/
function rotate(id, tag)
{
/*Normalise string for later comparison*/
tag = tag.toLowerCase();
/*Get container DOM Object*/
var el = document.getElementById(id),
visibleIdentified = false;
hasBeenSet = false,
firstMatchingChild = false;;
/*Iterate over children*/
for(i = 0; i < el.childNodes.length; i++){
/*Set child to var for ease of access*/
var child = el.childNodes[i];
/*If element has the correct nodeName and is a top level chlid*/
if(child.parentNode == el && child.nodeName.toLowerCase() == tag){
/*Set first matching child in case the rotation is already on the last image*/
if(!firstMatchingChild)
firstMatchingChild = child;
/*If child is visible */
if(child.style.display == "block"){
/*Take note that the visible element has been identified*/
visibleIdentified = true;
/*Toggle its visibility (display attribute)*/
child.style.display = "none";
/*Once the visibile item has been identified*/
}else if(visibleIdentified){
/*If the next item to become visible has been set*/
if(hasBeenSet){
/*Toggle visibility (display attribute)*/
child.style.display = "none"
}
/*Catch the next item to become visible*/
else{
/*Toggle visibility (display attribute)*/
child.style.display = "block";
/*Take note that the next item has been made visible*/
hasBeenSet = true;
}
}
}
}
/*If the hasBeenSet is false then the first item is to be made visible
- Only do so if the firstMatchingChild was identified, more or less redundant
exception handling*/
if(!hasBeenSet && firstMatchingChild)
firstMatchingChild.style.display = "block";
}
/*Declare cycle*/
setInterval("rotate('test','div')",1000);
</script>
</head>
<body>
<!-- Example container -->
<div id="test">
<div style="display:block">fire</div>
<div style="display:none">water</div>
<div style="display:none">shoe</div>
<div style="display:none">bucket</div>
</div>
</body>
</html>
Your specific question I haven't seen before, but your basic premise has been asked many times. The reason why you can't find a call like
rotate('<img src="1" />', '<img src="2" />');
is because it is a bad idea according to programming practices. You are mixing content with script. Client-side web design relies on sandboxing certain features to speed development and make debugging easier. Content, Styling and Scripting are the major areas. Here you mix content (images) with scripting. You should really use one of the many existing image rotation scripts that rely on taking existing markup and rotating them.
<img src="a" />
<img src="b" />
<script>rotateImages();</script>
If you want to do it your way then you will need to parse your strings and then create element nodes based on them. Honestly I don't think its worth the time to code one up in that format unless this is for curiosity's sake.
I am going to answer my own question as I came up with solution by my own. Sorry if I did not explain well and I hope it could be useful to someone as well. I had to avoid jQuery for a special reason, sometimes its just has to be that way. Here is the code, feel free to comment and improve... a working version is here http://jsbin.com/oxujuf/3
function rotator(options) {
var a = options.delay;
var b = options.media;
var mediaArr = [];
for(var i = 0, j = b.length; i < j; i++) {
mediaArr.push(b[i].img);
}
document.write('<div id="rotatorContainer"></div>');
var container = document.getElementById('rotatorContainer');
var Start = 0;
rotatorCore();
function rotatorCore() {
Start = Start + 1;
if(Start >= mediaArr.length)
Start = 0;
container.innerHTML = mediaArr[Start];
setTimeout(rotatorCore, a);
}
}
And then later you may call it like that, with a simple API.
rotator({
delay : 3500,
media : [{
img : '<img src="http://lorempixel.com/output/abstract-h-c-149-300-7.jpg" width="149" height="300" border="0" />'
}, {
img : '<img src="http://lorempixel.com/output/abstract-h-c-149-300-2.jpg" width="149" height="300" />'
}]
});
This is covered on many older forums and blogs.
Here are a couple links:
http://www.go4expert.com/forums/showthread.php?t=1012
http://www.reachcustomersonline.com/2008/03/19/09.38.04/?doing_wp_cron=1326819656

Image animation keeps requesting the images

I test the code in IE7, FF, Chrome, Safari and this problem occurs in Firefox only.
I have checked that the problem only occurs in FF 3.5.x, but not FF 3.0.x.
I want to make an image animation which has 10 images in total.
Currently, I use the following code to do it:
for (var i=1;i<=10;i++){
img[i] = new Image();
img[i].src = "images/survey/share_f"+i+".jpg"
}
var cornerno = 0;
function imganimate(){
$("#surveyicon").attr("src",img[cornerno].src);
//some logic to change the cornerno
setTimeout("imganimate()",1000);
}
And then change an element's src to loop through the array "img".
however, firefox keeps requesting the images continuous (I expect it only requests each unique image just once).
What should I do?
img is undefined. just add a line "var img = new Array();" before "for (var i=1;i<=10;i++){"
var img = new Array();
for (var i=1;i<=10;i++){
img[i] = new Image();
img[i].src = "images/survey/share_f"+i+".jpg";
}
var cornerno = 0;
function imganimate(){
cornerno %= 10;
cornerno++;
$("#surveyicon").attr("src",img[cornerno].src);
setTimeout("imganimate()",1000);
}
imganimate();
Try composing the images into a single image file like a sprite map and then use CSS positioning to shift the image around as a background image. It will be much faster and avoid any reloads.
You've already created ten DOM nodes, set their src and loaded the images. Why would you set the src again? You want to rotate those ten nodes in and out now. You can either toggle style.display or remove and insert the nodes.
Here's my suggestion. I'm not well versed in JQuery so there may be a few additional shortcuts I've overlooked:
var imgAmt = 10;
img = [];
for (var i=1;i<=imgAmt;i++){
img[i] = document.createElement("img");
img[i].src = "images/survey/share_f"+i+".jpg"
img[i].style.display = "none";
$("#surveyicon").appendChild(img[i]);
}
imganimate();
var cornerno = 0;
function imganimate(){
cornerno++;
cornerno = cornerno > imgAmt ? 1 : cornerno;
for (var i=1;i<=imgAmt;i++){
// hide all images but the index that matches cornerno:
img[i].style.display = i==cornerno ? "" : "none";
}
setTimeout(imganimate,1000);
}
This seems a bug in FF3.5.x.
Not sure whether the bug has already been fixed.

Categories

Resources