Clever work-around for my current script? - javascript

I'm currently utilizing JavaScript to allow users to watch multiple videos on a page. There is one big player at the top of the page, and below it are smaller thumbnails of more videos.
In theory, it works fine. However, there is one major problem: ALL videos load at the same time as the page loads.
See this page for reference. I have assigned JavaScript alerts to each individual videos to exemplify the problem (video1, video2, etc.).
Is there a easy fix, without having to rewrite the entire page, to have the videos load when they are clicked on, not when the page loads? Here's what the code looks like so far:
Javascript that calls the video:
function playVideo(cap, file, streamer, alertmsg) {
var so = new SWFObject('player/player-licensed_5_2.swf', 'ply1', '586', '330', '9');
so.addParam('displaywidth', '586');
so.addParam('displayheight', '330');
so.addParam('allowfullscreen', 'true');
so.addParam('allowscriptaccess', 'always');
so.addVariable('skin', 'player/skins/glow.zip');
so.addVariable('controlbar', 'over');
so.addVariable('plugins', 'captions-1');
so.addVariable('captions.file', cap);
so.addVariable('dock', 'true');
so.addVariable('image', 'landing_img/video.jpg');
so.addVariable('file', file);
so.addVariable('streamer', streamer);
so.addVariable('autostart', 'false');
so.write('player1');
window.alert(alertmsg);
}
The thumbnail for the video:
<div class="mini_player1"> <a href="#" class="vidpic" title="">
<!-- thumbnail-->
<img src="images/1_panda.jpg" alt="Video 1" class="vidpic" />
<span class="play-button"><img src="images/yt.png" alt="Play"></span>
</a>
</div>
<div class="content_mini_player1 cmp">
<script>
playVideo('<caption file>', '<videofile>', '<streamer>', 'video1');
</script>
</div>
The script that 'replaces' the content in bigplayer with the new selected video:
jQuery(function ($) {
$(".mini_player1, .mini_player2, .mini_player3, .mini_player4, .mini_player5, .mini_player6, .mini_player7, .mini_player8, .mini_player9, .mini_player10, .mini_player11, .mini_player12, .mini_player13, .mini_player14, .mini_player15, .mini_player16, .mini_player17, .mini_player18, .mini_player19, .mini_player20").click(function () {
var player_content_id = "content_" + $(this).attr("class");
var player_content = $("." + player_content_id).html();
$(".big_player").html('');
$(".big_player").html(player_content);
});
});
Any suggestions? Maybe consolidate playVideo and the jQuery function? Any help would be greatly appreciated.

Firstly, in the thumbnail code, there is a random closing </a> tag. Unless there is code you didn't post, I'd suggest removing it.
Secondly, your jQuery can be simplified much further. Currently, you are using the following selector:
$(".mini_player1, .mini_player2, .mini_player3, .mini_player4, .mini_player5, .mini_player6, .mini_player7, .mini_player8, .mini_player9, .mini_player10, .mini_player11, .mini_player12, .mini_player13, .mini_player14, .mini_player15, .mini_player16, .mini_player17, .mini_player18, .mini_player19, .mini_player20")
Wow! That is...wow.
Assign the players a single class they can all relate to across the board, select by that class and then run the each() method i.e.:
$(".mini-player").each(function() {
var player_content_id = "content_" + $(this).attr("class");
var player_content = $("." + player_content_id).html();
$(".big_player").html('');
$(".big_player").html(player_content);
});
Lastly, when you call a function in a script tag like you are doing:
<script>
playVideo('<caption file>', '<videofile>', '<streamer>', 'video1');
</script>
This WILL run the playVideo() function. Consolidating playVideo() and the jQuery code would be your best bet i.e. (using the same construct above):
$(".mini-player").each(function() {
var player_content_id = "content_" + $(this).attr("class");
var player_content = $("." + player_content_id).html();
$(".big_player").html('');
$(".big_player").html(player_content);
//Add event handler
$(this).on('click',function() {
playVideo('<caption file>', '<videofile>', '<streamer>', 'video1');
});
});

Your inline JavaScript function playVideo is going to be called when the page loads. You will want to remove it from there and do something like this:
<div onclick="playVideo()" class = "mini_player1">

Related

Why am I able to remove certain elements using document.getElementsByClassName() but not others?

I'm trying to create a simple script in ViolentMonkey to run on Letterboxd.com film pages - e.g. https://letterboxd.com/film/mirror/.
I'm aiming to remove certain elements from the page - namely, the average rating and the facebook share buttons.
My script looks like so:
window.addEventListener('DOMContentLoaded', (event) => {
console.log('DOM fully loaded and parsed');
var rating = document.getElementsByClassName('average-rating')
$(rating).remove()
console.log("Rating removed!")
var sidebar = document.getElementsByClassName('panel-share')
$(sidebar).remove()
console.log("Panel removed!")
});
The element with the class "panel-share" (facebook share buttons) is removed, no problem. However, the element with class "average-rating" is still there.
I tried to instead delete the section-level parent element it like so (it has two classes, "section" and "ratings-histogram-chart):
var chart = document.querySelector('.section.ratings-histogram-chart')
$(chart).remove()
console.log("Chart removed!")
But still no luck... In fact, querySelector does not work at all.
Can anyone help me with this? I feel I am doing something very obvious wrong, but I am quite new. Many thanks in advance.
EDIT: The HTML on which I'm trying to run it is:
var rating = document.getElementsByClassName('average-rating')
$(rating).remove()
console.log("Rating removed!")
var sidebar = document.getElementsByClassName('panel-share')
$(sidebar).remove()
console.log("Panel removed!");
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<span class="average-rating" itemprop="aggregateRating" itemscope="" itemtype="http://schema.org/AggregateRating"> 4.3 </span>
<li class="panel-share">
<div id="share-off" style="display: block;">Share</div>
<div id="share-on" style="display: none;"> <input type="text" class="field -transparent" value="https://boxd.it/28Q8" readonly="" spellcheck="false"> <a href="https://twitter.com/intent/tweet?text=Mirror%20%281975%29%20on%20%40letterboxd%3A%20https%3A%2F%2Fboxd.it%2F28Q8" class="replace popup-link tw-popup"
title="Tweet a link">Tweet</a> <a href="https://www.facebook.com/dialog/feed?app_id=173683136069040&link=https%3A%2F%2Fletterboxd.com%2Ffilm%2Fmirror%2F&picture=https%3A%2F%2Fa.ltrbxd.com%2Fresized%2Ffilm-poster%2F5%2F1%2F0%2F6%2F4%2F51064-mirror-0-230-0-345-crop.jpg%3Fk%3D8287891685&name=Mirror%20%281975%29%20on%20Letterboxd.com&caption=Directed%20by%20Andrei%20Tarkovsky&description=A%20dying%20man%20in%20his%20forties%20recalls%20his%20childhood%2C%20his%20mother%2C%20the%20war%20and%20personal%20moments%20that%20tell%20of%20and%20juxtapose%20pivotal%20moments%20in%20Soviet%20history%20with%20daily%20life.&message=&display=popup&redirect_uri=https://letterboxd.com/facebook-share"
class="replace popup-link fb-popup fbc-has-badge fbc-UID_1" title="Share to Facebook">Share</a> </div>
</li>
The problem here seems to be that the rating graph (incl. the average rating) is loaded asynchronous and thus is not present when your script runs.
In order to counter this, you could listen for changes of the parent .sidebar element and then remove it as soon as it's present:
$(function() {
$('.panel-share').remove();
// listen for changes of the .sidebar element
$('.sidebar').on('DOMSubtreeModified', function() {
if($('.average-rating').length) {
$('.average-rating').remove();
$(this).off('DOMSubtreeModified');
}
})
});
``
I added 2 sets of listeners, the first one(your own) for when DOMContentLoaded, the second one is a quick interval that waits for document.readyState to be equal to "complete"
window.addEventListener('DOMContentLoaded',()=>{
window.n=1 //as extra proof that it works, you can go later and check n on the inspect element's console
var i=setInterval(() => {
if(document.readyState!="complete"){window.n++;return;} //once document.readyState=="complete", the rest of the code loads(what you want to execute)
clearInterval(i)
console.log('DOM fully loaded and parsed');
var rating = document.getElementsByClassName('average-rating')
$(rating).remove()
console.log("Rating removed!")
var sidebar = document.getElementsByClassName('panel-share')
$(sidebar).remove()
console.log("Panel removed!")
},1);
})
As proof that my answer works, open a new tab/window, navigate to the url https://letterboxd.com/film/mirror/ to avoid cors errors, open inspect element and paste the code below
var myWindow=window.open("https://letterboxd.com/film/mirror/")
myWindow.eval(`
window.addEventListener('DOMContentLoaded',()=>{
window.n=1 //as extra proof that it works, you can go later and check n on the inspect element's console
var i=setInterval(() => {
if(document.readyState!="complete"){window.n++;return;} //once document.readyState=="complete", the rest of the code loads(what you want to execute)
clearInterval(i)
console.log('DOM fully loaded and parsed');
var rating = document.getElementsByClassName('average-rating')
$(rating).remove()
console.log("Rating removed!")
var sidebar = document.getElementsByClassName('panel-share')
$(sidebar).remove()
console.log("Panel removed!")
},1);
})
`)

jQuery / javascript How to load Script when a css class presents in the page

I have a page shows a list of rows and with a pagination, when you click on next page the next page loads in same page contains another list of rows. so basically next page loads a bunch of other rows without my exact page been reloaded
So the problem im haven is i use this script below while it works fine but when the next page loads in same window this script does not work for them.
I hope i make sense here :) what is a good solution for this?
$('.ltwitch').each(function () {
var tnick = $(this).data('tnick');
var span = $(this).next();
$.getJSON("https://api.twitch.tv/kraken/streams/" + tnick + ".json?callback=?", function (data) {
if (data.stream === null) {
span.html(
'<strong class="ipsOnlineStatus ipsOnlineStatus_offline"><i class="fa fa-circle"></i></strong><h4 class="ipsDataItem_title ipsType_break"> ' + tnick + ' </h4><div class="ipsDataItem_meta">Offline</div>');
} else {
var views = data.stream.viewers;
var game = data.stream.game;
span.html(
'<strong class="ipsOnlineStatus ipsOnlineStatus_online"><i class="fa fa-circle"></i></strong><h4 class="ipsDataItem_title ipsType_break"> ' + tnick + ' </h4><div class="ipsDataItem_meta"> ' + views + ' viewers </div><div class="ipsDataItem_meta">Playing: '+ game +' </div>');
}
$(function() {
var online = $('.ipsOnlineStatus_online').closest('.cCmsRecord_row');
var first = $('.cCmsRecord_row')[0];
online.each(function() {
$(first).before(this);
});
});
short of HTML
<ol class="List">
<li class="row">
<div class="main">
<a href="#" class="ease">
<div class="ltwitch" data-tnick="esl_lol"></div>
</a>
</div>
</li>
<li class="row">
<div class="main">
<a href="#" class="ease">
<div class="ltwitch" data-tnick="esl_lol"></div>
</a>
</div>
</li>
....
....
....
</ol>
<div data-role="tablePagination">
<ul class="ipsPagination">
<li class="ipsPagination_prev ipsPagination_inactive">Prev</li>
<li class="ipsPagination_next">Next</li>
</ul>
</div>
It sounds like you want to wrap your work in a named function so you can call it on subsequent paging. Your code right now is most likely in a document ready which is only called on initial page load. Turning all of that work into a function now allows it to be called many times.
function myFunction () {
...
};
myFunction();
Then where your pagination code is set up ( I figure an anchor click binding ) just call myFunction() again.
$('.ipsPagination_next').click(function(){
...
myFunction();
});
I would note that if these two areas are in different document ready functions or different files, ideas like scope and encapsulation might become problematic. We'd have to see more code to confirm if it would be an issue. If you are having problems you could put the function myFunction() onto the root which would be outside of any document ready, load, or whatever you might be using. Then it would be on the root or window level.
window.myFunction = function () {
...each(... your current work
};
This could be used if you aren't sure about encapsulation or how scope works.
Your $('.ltwitch') selector only captures the classes present in the first page. When a subsequent page is loaded, $('.ltwitch') is not executed again on the new classes.
I suggest that you somehow differentiate between classes corresponding to already loaded pages, and classes corresponding to new pages. You could do this by adding a class, e.g. .already-loaded to the classes as you load them.
$.getJSON("https://api.twitch.tv/kraken/streams/" + tnick + ".json?callback=?", function (data) {
$(this).addClass('already-loaded'); // Mark as loaded.
...
This way, replace your first line with this:
$('.ltwitch').not('.already-loaded').each(function () {
...
Not tested, but could work.

How can I properly use '$(this)' to call a DOM element that triggered the event?

I wrote the following script that resets the the Iframe Source, removes a class (.play) and adds an image placeholder when .b-close is clicked. I got it to work but the problem is that I have multiple modals and I would like only like to affect the modal that's clicked. I figured that I should use the '$(this)' DOM element in order to achieve this.
<script>
(function($){
var ivid = $('.pretty-embed iframe').attr('src');
$(document).ready(function() {
$(".b-close").click(function(e){
e.preventDefault();
var vidID = $(this).parent().find('.pretty-embed').attr('data-pe-videoid');
var vidImg = "//img.youtube.com/vi/"+vidID+"/maxresdefault.jpg";
var vidImgUrl = '<img src="'+vidImg+'" width="100%" alt="YouTube Video Preview">';
$('.pretty-embed').removeClass('play').empty();
$('.pretty-embed').html(vidImgUrl);
$('.b-modal').click(); /// Just trying to close modal..... $.modal.close();
});
});
})(jQuery);
</script>
Here's is the the Modal that I will be calling. Keep in mind that there will be multiple modals, so I would only like to affect the modal that's clicked
<div id="element_to_pop_up" display: block;">
<a class="b-close">x</a>
<h3 class="pop-hd">Header</h3>
<p>Test Video</p>
<div class="pretty-embed play" data-pe-allow-fullscreen="false">
<iframe width="330" height="186" style="border:none;" src="//www.youtube.com/embed/nGSfaMxCu-U?autoplay=1&rel=1"></iframe>
</div>
</div>
The problem is in your $('.pretty-embed') selector which selects all the embed elements in all modals If I understood your problem correctly. To fix that take the id of the modal and prepend it to the selectors like below:
(function($){
var ivid = $('.pretty-embed iframe').attr('src');
$(document).ready(function() {
$(".b-close").click(function(e){
e.preventDefault();
var vidID = $(this).parent().find('.pretty-embed').attr('data-pe-videoid');
var vidImg = "//img.youtube.com/vi/"+vidID+"/maxresdefault.jpg";
var vidImgUrl = '<img src="'+vidImg+'" width="100%" alt="YouTube Video Preview">';
var parent_id = $(this).parent().attr(id);
// Prepend the parent id before the .pretty-embed selector
$('#'+parent_id+' .pretty-embed').removeClass('play').empty();
$('#'+parent_id+' .pretty-embed').html(vidImgUrl);
$('#'+parent_id+' .b-modal').click(); /// Just trying to close modal... $.modal.close();
});
});
Also you can use the same way you did it in the previous lines:
$(this).parent().find('.pretty-embed').removeClass('play').empty();
You can get the current modal with
var current_modal = $(this).parent().find('.pretty-embed');
Then remove the class play, and add your image like this:
current_modal.removeClass('play').empty();
current_modal.html(vidImgUrl);
See your complete code in this jsfiddle

jQuery loads external HTML file but does not stay still after page refresh

I am trying to do something different without knowing if it is a good idea or not
I have a navigation menu as the following:
...
<li>Home</li>
<li>FAQ</li>
<li>Contact</li>
...
I do not want to use a server-side scripting because it takes more time to make db connection and define some configuration, and not want to multiply the pages for each one. So I made a master page index.php
in body section
there are two elements:
an h3 element to display the page title and a div to display the content which is called from another html source.
...
<div class="container">
<h3 id="pageTitle"></h3>
<div id="pageContent"></div>
</div>
...
I am using jQuery's click event to load the page into the div
$(function() {
$("a[href^='#m']").click(
function() {
$("#pageTitle").text($(this).text());
$("#pageContent").load($(this).attr("href").substring(1) + ".html"); //removing # char.
});
});
It works fine. But when I press F5 it returns the initial state as normal. How can I load the current page by referencing the address bar (I can see eg. sitename/#mfaq) when page is refreshed.
I think, first I need to detect if page is refreshing and load the corresponding html file in according to the #m**** on the addressbar.
$(function() {
$("a[href^='#m']").click( function(evt) {
// ------ This should work
// renamed parameter elem to evt like corrected in comment
evt.preventDefault();
$("#pageTitle").text($(this).text());
$("#pageContent").load($(this).attr("href").substring(1) + ".html");
});
});
Add to your DOM ready function:
if (window.location.hash != "") {
$("#pageTitle").text($("a[href='"+window.location.hash+"']").text());
$("#pageContent").load(window.location.hash.slice(1) + ".html");
}
I have made this. It works well. But I am not sure about performance issues:
$(function() {
var address = $(location).attr('href');
var hash = address.lastIndexOf("#");
var page = address.substring(hash+1);
if (hash < 1)
{
$("#pageContent").load("mhome.html");
$("#pageTitle").html("Default Page Title");
}
else
{
$("#pageContent").load(page + ".html");
$("#pageTitle").html($("a[href='" + address.substring(hash) + "']").text());
}
$("a[href^='#m']").click(
function() {
$("#pageTitle").text($(this).text());
$("#pageContent").load($(this).attr("href").substring(1) + ".html");
});
});

How can I reduce the redundancies in my jQuery code?

The size of my JavaScript file is getting out of hand because I have hundreds of links, and each one has its own jQuery function even though they all peform basically the same task.
Here's a short excerpt:
$("#link1").click(function ()
{
$(".myDiv").hide();
$("#myDiv1").toggle();
});
$("#link2").click(function ()
{
$(".myDiv").hide();
$("#myDiv2").toggle();
});
$("#link3").click(function ()
{
$(".myDiv").hide();
$("#myDiv3").toggle();
});
Would there be a way to abstract some of this logic so that I have only a single function instead of hundreds that do the same thing?
You can add a class to all the links that do the same thing and act with jQuery on that class.
<a href='whatever' id='link_1' class='toggler'>text</a>
<a href='whatever' id='link_2' class='toggler'>text</a>
jQuery code will be:
$(".toggler").click( function(){
// toggle the divs
var number = $(this).attr("id").split('_')[1];
$(".myDiv").hide();
$("#myDiv"+ number).toggle();
});
The general approach that I use is to use the traversal methods to find related elements rather than using absolute selectors. This will allow you to apply the same code to elements that are similarly configured without any complicated dependencies on the format of the ids, etc. Done correctly it's also reasonably robust against minor changes to the mark up.
For example, say I have a series of links, each followed by a div that will be toggled by clicking on that link. The links each have a particular class so they can easily be referenced.
Toggle
<div>
Some content...
</div>
Toggle
<div>
Other content
</div>
I would then find all the links by class, then use the next method to find the associated div and toggle it's visibility. Note that this is a simple example. You may need to use more complicated traversal mechanisms and filter by element type or class, too, depending on your exact mark up.
$('.linkClass').click( function() {
$(this).next().toggle();
});
What about adding the ID of your target into the href of the link?
<a id="link1" href="#myDiv1" class="toggle">Toggle 1</a><br/>
<a id="link2" href="#myDiv2" class="toggle">Toggle 2</a><br/>
<a id="link3" href="#myDiv3" class="toggle">Toggle 3</a><br/>
Then you could write a single function like so:
$(".toggle").click(function(e) {
e.preventDefault();
$(".myDiv").hide();
$($(this).attr('href')).toggle();
});
Or another approach I've used:
$(".toggle").each(function(i) {
$(this).click(function(e) {
e.preventDefault();
$(".myDiv").hide();
$(".myDiv:eq("+i+")").toggle();
});
});
This one is in the same vein as tvanfosson's idea, using some sort of DOM relationship to link the elements, in this case by assuming that the link elements and the div elements are in the same order on the page.
You can just have each click call an external function and pass in a parameter for the number string.
Ex:
$("#link1").click(toggle("1"));
$("#link2").click(toggle("2"));
function toggle(number) {
$(".myDiv").hide();
$("#myDiv"+number).toggle();
}
function makeToggler(number) {
$('#link' + number).click(function() {
$('.myDiv').hide();
$('#myDiv' + number).toggle();
});
}
makeToggler(1);
makeToggler(2);
makeToggler(3);
You can adapt this to meet your naming standards.
Depending on the structure of your divs and links, there are better ways to do it. If you post the structure of your elements, I'll show you one.
I think this is a simple refactoring
you could define a function as such
function doSomethingTo(thisDiv)
{
$(".myDiv").hide();
$(thisDiv).toggle();
}
and then just reuse it where you need it
$("#link1).click(doSomethingTo(thisDiv));
$("#link2).click(doSomethingTo(thisDiv));
Building on Craig's solution:
$("#link1, #link2").click(toggle(this));
function toggle(obj) {
$(".myDiv").hide();
$("#myDiv" + $(obj).attr("id").replace('link','')).toggle();
}
I change the link become like this (i rename the id to just a number)
<a href='#test1' id='1' class='link'> ... </a>
<a href='#test2' id='2' class='link'> ... </a>
<a href='#test3' id='3' class='link'> ... </a>
and then on js:
$(document).ready(function(){
$('.link').click(function(){
$('.myDiv').hide();
var id = $(this).attr('id'); // take the id
$('#myDiv'+id).toggle();
});
});
throw your makeToggle into a loop?
function makeToggler(number) {
$('#link' + number).click(function() {
$('.myDiv').hide();
$('#myDiv' + number).toggle();
});
}
for(i=1;i>=#;i++) {makeToggler(i);}
then you could even have it count your links for you, something link this?:
function countElementsByClass(className){
var count = 0;
var o = document.getElementsByTagName("a").className;
for(var i=0;i<o.length;i+){
if(o[i].className == "accordion/whatever")
count ++;
}
return count;
}
credit: building on SLaCKS solution

Categories