My javascript "slideshow" isnt working - javascript

Here is a small part of a website that I'm experimenting with. Anyway I'm learning Javascript and I decided to experiment by making an extremely simple "slideshow", but whenever I run this in the browser (Mozilla Firefox 42), the browser crashes. I'm wondering if anyone could tell me whats wrong or possibly give me a better way of making this(I'm sure there is but I couldn't find one). Also I know I can make this in jQuery but I want to try this out with pure Javascript.
I have also tried this on Chrome and Opera.
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
//I will use javascript to make the "slideshow" on mainSection
function loop() {
var BACKGROUND = document.getElementById("Introduction");
var SECTION1 = document.getElementById("mainSection");
var SECTION2 = document.getElementById("section2");
var SECTION3 = document.getElementById("section3");
var i = 1;
while (i <= 3) {
i = i + 0.0001;
SECTION1.style.display="block";
SECTION2.style.display="none";
SECTION3.style.display="none";
}
while (i <= 6) {
i = i + 0.0001;
SECTION1.style.display="none";
SECTION2.style.display="block";
SECTION3.style.display="none";
}
while (i <= 9) {
i = i + 0.0001;
SECTION1.style.display="none";
SECTION2.style.display="none";
SECTION3.style.display="block";
}
while (i = 9) {
i = 1;
}
}
</script>
</head>
<body onload="loop();">
<header id="Introduction">
<section id="mainSection">
<h1>Welcome to the Ipsum Hotel.</h1>
<p>This is where thought and precision goes<br />
towards the comfort of our customers.<br /></p>
</section>
<!--This will be used to make a slideshow-->
<section id="section2">
<p><strong>Free Wifi!</strong></p>
<p>Free wifi is included in all of our guest<br />
packages from Economy to Deluxe<br /></p>
</section>
<section id="section3">
<p><strong>Free Hot Breakfast</strong></p>
<p>Included with any of our guest packages is<br />
a hearty breakfast. You can also get a personalized<br />
omelet at our omelet bar.<br /></p>
</section>
</header>
</body>
</html>

Try using setInterval. As mentioned in the comments, a while loop will block all page operations and tax your CPU. setInterval is designed specifically for handling timing operations in a more resource-friendly manner.
var SECTION1 = document.getElementById("mainSection");
var SECTION2 = document.getElementById("section2");
var SECTION3 = document.getElementById("section3");
var sections = [SECTION1, SECTION2, SECTION3];
var activeSection = 0;
setInterval(function() {
// If we are past the end of list, reset to first element
if (activeSection >= sections.length) {
activeSection = 0;
}
// Mark only the activeSection as visible
sections.forEach(function(section, idx) {
section.style.display = activeSection === idx ? 'block' : 'none';
});
activeSection += 1;
}, 3000);
This will loop over each section every three seconds (at the least, see https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval for more info about how timers work in the browser). Once the last section is reached, it resets to the first section.

Related

Why chrome's rendering result is different when using breakpoints?

At first, i was trying to figuring how to implement a "line-by-line shows" effect, as far as i know, chrome will reflow the page when there is a dom insert action, so i just think the following code could work:
Click the "Click Me" span tag, there will be some lines of "-.-" span occur one-by-one.
<!DOCTYPE html>
<html lang="en">
<body>
<div>
<span id="clickme">Click Me</span>
</div>
<div id="container"></div>
<script>
const container = document.getElementById("container");
const clickme = document.getElementById("clickme");
clickme.addEventListener("click", () => {
for (let i = 0; i < 10; ++i) {
const child = document.createElement("div");
for (let j = 0; j < 20; ++j) {
const span = document.createElement("span");
span.textContent = "-.- ";
child.appendChild(span);
}
container.appendChild(child);
}
});
</script>
</body>
</html>
Unfortunately, chrome seems to apply all the dom change in one batch, so i used performance recording to check if it really does, and here the result just verified the guess:
I was really confused by this result, so i tried again to add a breakpoint at the for-loop entry to see what happened in this process, and the result is completely different from the normal process:
Here i uploaded two screenshots because the rest ones is just the same render result (line-by-line).
So my question is why breakpoints change the behavior of chrome's rendering process, and is there any way to implement the "line-by-line shows" effect only with JavaScript?
(My original purpose is a little more complicated than "line-by-line", and cannot be solved by CSS transition)
When Chrome hits a breakpoint it will enter the special "spin the event loop" algorithm (or a variation of it at least).
This algorithm allows to pause the currently active task and let the event loop process other duties, such as updating the rendering. Note though that no other scripts will run while in this state. So you will have CSS animations update, new DOM changes painted to screen, but you won't e.g have scroll events, or requestAnimationFrame callbacks fire.
Also, note that the browser does not trigger a reflow at each DOM insertion. It will try to trigger a reflow only at the last moment when required (i.e right before painting in most cases). Though some Web-APIs can force such a reflow (e.g all the position getters like HTMLElement#offsetTop.
You can use setInterval as shown in your amended code below. This repeatedly interrupts the processing thread, those allowing the browser to refresh the display. When the number of desired lines is reached, you should invoke clearInterval.
<!DOCTYPE html>
<html lang="en">
<body>
<div>
<span id="clickme">Click Me</span>
</div>
<div id="container"></div>
<script>
const container = document.getElementById("container");
const clickme = document.getElementById("clickme");
clickme.addEventListener("click", () => {
let lines = 0;
const intervalID = setInterval(() => {
const child = document.createElement("div");
for (let j = 0; j < 20; ++j) {
const span = document.createElement("span");
span.textContent = "-.- ";
child.appendChild(span);
}
container.appendChild(child);
if (++lines >= 10) {
clearInterval(intervalID);
}
}, 500);
});
</script>
</body>
</html>

Controlling MP4 Playback using javascript

I am trying to clone the following site:
https://www.apple.com/mac-pro/
I am still in the prototyping phase and the big ticket item I am trying to figure out is how they are playing their MP4 file backwards when you scroll up the page. If you scroll down the page a few steps and then back up, you will see what I mean.
So far I have tried the following techniques:
Tweening currentTime property of video element
Using requestAnimationFrame and using the timestamp in the callback to update the currentTime property to the desired value
Using the requestAnimationFrame technique, I am now getting a partially usable result in every browser other than Chrome. Chrome is ok if you want to rewind maybe .5 seconds, but any more than that and it will get jumpy.
I have also made the following discoveries:
Chrome hates trying to rewind an MP4 file
As much as Chrome hates rewinding MP4 files, also make sure that you don't have an audio track on your video file. It will make it even slower.
So I feel I have a pretty good understanding of the options available to me, but the one thing that makes me think I am missing something is that the Apple website functions ok in Chrome.
I have started debugging their code which is located at:
https://images.apple.com/v/mac-pro/home/b/scripts/overview.js
And from what I can tell they seem to be using requestAnimationFrame, but I can't understand why they are getting a better result.. Does anyone have any ideas on how to achieve this effect?
BTW - I understand that videos are not really meant to be played backwards and they will never play predictably backwards. I have even had occasions on the Apple website where the rewinding can be jerky. But they still have good 2-3 second rewind transitions and the result is definitely acceptable.
Here is my relevant javascript and HTML so far..
var envyVideo, currentVideoTrigger = 0,
currentIndicator, startTime, vid, playTimestamp, playTo, playAmount, triggeredTime, rewindInterval;
$(function() {
vid = document.getElementById("envy-video");
$("#play-button").click(function() {
vid.play();
});
$("#rewind-button").click(function() {
vid.pause();
playTo = parseFloat($("#play-to-time").val());
playAmount = playTo - vid.currentTime;
triggeredTime = vid.currentTime;
requestAnimationFrame(rewindToPointInTime);
});
});
function rewindToPointInTime(timestamp) {
if (!playTimestamp) playTimestamp = timestamp;
var timeDifference = (timestamp - playTimestamp) / 1000;
vid.currentTime = triggeredTime + (playAmount * (timeDifference / Math.abs(playAmount)));
if (vid.currentTime > playTo) {
requestAnimationFrame(rewindToPointInTime);
} else {
playTimestamp = null;
playAmount = null;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Rhino Envy</title>
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
<script src="./js/envy.js"></script>
<link rel="stylesheet" href="./css/envy.css">
</head>
<body;
<div id="envy-video-container">
<video id="envy-video" src="./videos/prototype_animation.mp4"></video>
</div>
<div id="video-controls">
<p id="video-current-time"></p>
<div class="video-control"><button id="rewind-button">rewind to</button><input type="text" id="play-to-time" placeholder="forward time" value="0"></div>
<button id="play-button">play</button>
</div>
<ul id="envy-steps">
<li id="envy-step-indicator-1"></li>
<li id="envy-step-indicator-2"></li>
<li id="envy-step-indicator-3"></li>
</ul>
<section id="envy-full-range">
<div id="envy-1-door-link"></div>
<div id="envy-2-door-link"></div>
<div id="envy-3-door-link"></div>
</section>
</body>
</html>
One solid way I can think of, would be to use two videos : one in normal direction, and the other one reversed.
You could then simply switch between which video is to be played, and only update the currentTime of the hidden one in the background.
With this solution, you can even rewind audio !
To reverse a video, you can use ffmpeg's command
ffmpeg -i input.mp4 -vf reverse -af areverse reversed.mp4
Note: You may feel some gaps at the switch, which could probably be leveraged by using a single visible video element, fed from the other's element streams, but I'll leave it for an update, I'm short on time r.n.
const vids = document.querySelectorAll('video');
vids.forEach(v => {
v.addEventListener('loadedmetadata', canplay);
v.addEventListener('timeupdate', timeupdate);
});
let visible = 0;
function timeupdate(evt) {
if (this !== vids[visible]) return;
let other = (visible + 1) % 2;
vids[other].currentTime = this.duration - this.currentTime;
}
document.getElementById('switch').onclick = e => {
visible = (visible + 1) % 2;
show(vids[visible]);
}
// waith both vids have loaded a bit
let loaded = 0;
function canplay() {
if (++loaded < vids.length) return;
hide(vids[1]);
show(vids[0]);
}
function show(el) {
el.muted = false;
const p = el.play();
el.style.display = 'block';
const other = vids[(visible + 1) % 2];
// only newest chrome and FF...
if (p && p.then) {
p.then(_ => hide(other));
} else {
hide(other);
}
}
function hide(el) {
el.muted = true;
el.pause();
el.style.display = 'none';
}
document.getElementById('pause').onclick = function(e) {
if (vids[visible].paused) {
this.textContent = 'pause';
vids[visible].play();
} else {
this.textContent = 'play';
vids[visible].pause();
}
}
<button id="switch">switch playing direction</button>
<button id="pause">pause</button>
<div id="vidcontainer">
<video id="normal" src="https://dl.dropboxusercontent.com/s/m2htty4a8a9fel1/tst.mp4?dl=0" loop="true"></video>
<video id="reversed" src="https://dl.dropboxusercontent.com/s/lz85k8tftj2j8x6/tst-reversed.mp4?dl=0" loop="true"></video>
</div>

html code working in IE/FireFox but not in chrome [duplicate]

This question already has answers here:
JS function named `animate` doesn't work in Chrome, but works in IE
(3 answers)
Closed 6 years ago.
This is really simple code that I wanted to try and get running because I wanted to learn some JavaScript basics. The code works in Internet Explorer and Firefox but not on chrome. I feel like I must be missing something really stupid.
var frame = 2;
function animate(){
if(frame == 1){
frame = frame + 1;
document.getElementById("animate").src = "walking1.png";
}
else if (frame == 2){
frame = frame + 1;
document.getElementById("animate").src = "walking2.png";
}
else{
frame = 1;
document.getElementById("animate").src = "walking3.png";
}
}
<p> clicking the button will change the image.</p>
<img id="animate" src="walking1.png">
<button onclick="animate()">click me to animate</button>
the pictures used are saved in the same folder.
animate is used both as a function name and an id, which cause it to not work in Chrome.
Also, mentioned in this post, a function alone with the name animate might also not work in Chrome, based on how it is implemented. (see notes below)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript">
var frame = 2;
function animatee(){
if(frame == 1){
frame = frame + 1;
document.getElementById("animate").src = "http://placehold.it/100";
}
else if (frame == 2){
frame = frame + 1;
document.getElementById("animate").src = "http://placehold.it/100/f00";
}
else{
frame = 1;
document.getElementById("animate").src = "http://placehold.it/100/00f";
}
}
</script>
</head>
<body>
<p> clicking the button will change the image.</p>
<img id="animate" src="http://placehold.it/100/0f0">
<button onclick="animatee();">click me to animate</button>
</body>
</html>
Notes:
An observation made by guest271314 shows that if the onclick handler is not attached inline, the issue is not present
window.onload = function() {
var frame = 2;
function animate() {
console.log("animate called")
if (frame == 1) {
frame = frame + 1;
document.getElementById("animatee").src = "http://placehold.it/100x100";
} else if (frame == 2) {
frame = frame + 1;
document.getElementById("animatee").src = "http://placehold.it/200x200";
} else {
frame = 1;
document.getElementById("animatee").src = "http://placehold.it/100/00f";
}
}
document.querySelector("button").onclick = animate;
}
<p> clicking the button will change the image.</p>
<img id="animatee" src="http://placehold.it/100/0f0">
<button>click me to animate</button>
To their defense Kaiido contributed with the specs: https://html.spec.whatwg.org/#named-access-on-the-window-object
Knu contributed with this bug report:https://www.w3.org/Bugs/Public/show_bug.cgi?id=11960

Errors writing image gallery

I'm trying to write a simply image gallery element for a website and I'm having trouble with the code for silly reasons. I've never gotton on with JavaScript and have always found it a headache. I've tried various other image galleries but can't for the life of me get them to actually work correctly either
My current HTML code is like this:
<!DOCTYPE html>
<html>
<head>
<title> Test of slider </title>
<script type="text/javascript" src="slider.js"></script>
</head>
<body>
<div class="slider" id="main">
<img src="#" class="mainImage" />
<div class="sliderImages" style="display: none;">
<img src="companion.jpg"/>
<img src="cookie.jpg" />
<img src="orange.jpg" />
<img src="orangeWhole.jpg" />
</div>
<div class="sliderButtons">
Previous
Next
</div>
</div>
</body>
</html>
with the javascript like this:
this.Slider = new function(){
// Stores the indices for each slider on the page, referenced by their ID's
var indices = {};
var limits = {};
var images = {};
// Call this function after a major DOM change/change of page
this.SetUp = function(){
// obtain the sliders on the page
// TODO restrict to those within body
var sliders = document.getElementsByClassName('slider');
// assign the indices for each slider to 0
for(var i = 0; i < sliders.length; i++){
indices[sliders[i].id] = 0;
var sliderImages = document.getElementsByClassName('sliderImages');
var imagesTemp = sliderImages[0].getElementsByTagName('img');
images[sliders[i].id] = imagesTemp;
limits[sliders[i].id] = imagesTemp.length;
}
}
// advances a certain slider by the given amount (usually 1 or -1)
this.Slide = function(id, additive){
if(indices && id){
indices[id] = indices[id] + additive;
// Check limits
if(indices[id] < 0){
indices[id] = limits[id] - 1;
}
if(indices[id] >= limits[id]){
indices[id] = 0;
}
// alter img to be the new index
document.getElementById(id).getElementsByClassName('mainImage')[0].src = images[id][indices[id]].src;
}
}
}
for(var slider in sliders)
{
// here slider is the index of the array. to get the object use sliders[slider]
}
then you can use 'getElementsByClassName' function
edit
U have included the slider.js on the top of the html. So first it loads the the js and tries to access the elements which are not yet created..Move the tag to the bottom of the page.
sliderImages is an array of divs with classname sliderImages. there is only one that satisfies.
var sliderImages // is a array with 1 item.
// to get the images use sliderImages[0].getElementsByTagName('img');
change
this.Slide = new function(id, additive){
to
this.Slide = function(id, additive){ // otherwise it will be called when the page is loaded with undefined values.
onclick of the link call with quotes
Slider.Slide('main', 1)
You are using a methode I do not know (or you forgot the "s")
var sliderImages = slider.getElementByClassName('sliderImages');
I would use something like the code below. It is important sliderImages will be an array of anything that is of the class "silderImages"
var sliderImages =slider.getElementsByClassName("sliderImages")

wanted: simple HTML file that does disclosure triangle <div> hide/reveal

I have a program that produces a text report. I want it to make an HTML report with multiple disclosure triangles, so that when you click a triangle more of the report shows or hides. I am okay with embedding JavaScript inside the file, but I really want it all in a single file, with no additional files. Is there an easy way to do this with modern browsers?
If you don't care about compatibility with Internet Explorer, you could use the html tag: http://www.w3schools.com/tags/tag_details.asp
Its a very quick way to prototype disclosure triangles.
For example:
<details>
<summary>The contents of the summary tag is always visible</summary>
<p>Everything else inside the details tag will be hidden in a disclosure triangle</p>
</details>
The simplest way is something like this:
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript"></script>
<style>.wrapper div { display:none;}</style>
<script>
$(function() {
$('.wrapper h2').click(function() { $(this).next().toggle();});
});
</script>
</head>
<body>
<div class="wrapper">
<h2>Example header 1</h2>
<div>bodytext 1</div>
</div>
<div class="wrapper">
<h2>Example header 2</h2>
<div>bodytext 2</div>
</div>
<div class="wrapper">
<h2>Example header 3</h2>
<div>bodytext 3</div>
</div>
</body>
</html>
I have made a simple working example here: http://jsfiddle.net/NXuQt/1/
It isn't pretty but should give you the simple template you need.
Note that in this solution, the entire header is click-able... I figure adding an image and changing it as part of the click event is something you can take care of yoruself, otherwise let me know :)
Note: The javascript is based on the inclusion of the jQuery library.
EDIT: I updated the answer to copy/paste ready working code, the reason you couldn't make it work as it was, was because i had only taken the essentials from the fiddle example. The fiddle automatically ran the click handler initialization at DOMready, which the updated example now has built in :)
With straight HTML, no. That's not what it's for. You will need to use a scripting language, either JavaScript or VBScript, most likely.
This is a script I've used in the past (not mine, but I don't have the URI of the original):
var timerlen = 5;
var slideAniLen = 250;
var timerID = new Array();
var startTime = new Array();
var obj = new Array();
var endHeight = new Array();
var moving = new Array();
var dir = new Array();
function slidedown(objname)
{
if(moving[objname])
return;
if(document.getElementById(objname).style.display != "none")
return; // cannot slide down something that is already visible
moving[objname] = true;
dir[objname] = "down";
startslide(objname);
}
function slideup(objname)
{
if(moving[objname])
return;
if(document.getElementById(objname).style.display == "none")
return; // cannot slide up something that is already hidden
moving[objname] = true;
dir[objname] = "up";
startslide(objname);
}
function startslide(objname)
{
obj[objname] = document.getElementById(objname);
endHeight[objname] = parseInt(obj[objname].style.height);
startTime[objname] = (new Date()).getTime();
if(dir[objname] == "down")
{
obj[objname].style.height = "1px";
}
obj[objname].style.display = "block";
timerID[objname] = setInterval('slidetick(\'' + objname + '\');',timerlen);
}
function slidetick(objname)
{
var elapsed = (new Date()).getTime() - startTime[objname];
if (elapsed > slideAniLen)
{
endSlide(objname)
}
else
{
var d =Math.round(elapsed / slideAniLen * endHeight[objname]);
if(dir[objname] == "up")
d = endHeight[objname] - d;
obj[objname].style.height = d + "px";
}
return;
}
function endSlide(objname)
{
clearInterval(timerID[objname]);
if(dir[objname] == "up")
obj[objname].style.display = "none";
obj[objname].style.height = endHeight[objname] + "px";
delete(moving[objname]);
delete(timerID[objname]);
delete(startTime[objname]);
delete(endHeight[objname]);
delete(obj[objname]);
delete(dir[objname]);
return;
}
function toggleSlide(objname)
{
if(document.getElementById(objname).style.display == "none")
{
// div is hidden, so let's slide down
slidedown(objname);
}
else
{
// div is not hidden, so slide up
slideup(objname);
}
}
You would assign a call to toggleSlide() to the onclick() event of the element you want to toggle.
CSS:
.hidden {
display: none;
}
Javascript:
function createSection(section, hidden) {
var triangle = section.children[0]; // assumes the triangle image is the first child of a section (see HTML)
var contents = section.children[1];
triangle.onclick = function() {
if (contents.className.indexOf("hidden") != -1) { // the section is hidden
contents.className = contents.className.replace("hidden", "");
} else { // the section wasn't hidden
contents.className += " hidden";
}
}
if (hidden) {
contents.className += " hidden";
}
}
// Create the sections when window loads
window.onload = function() {
createSection(document.getElementById("section1"));
createSection(document.getElementById("section2"), true);
}
HTML:
<div id="section1">
<img src="triangle.jpg"></img>
<div>This is the section content</div>
</div>
<div id="section2">
<img src="triangle.jpg"></img>
<div>this section is hidden by default</div>
</div>
Obviously you would have to change some things to your own html file
Well, after some fiddling around, I was able to make a file that does what I want using the switchcontent.js and switchicon.js javascript files I found at http://www.dynamicdrive.com/dynamicindex17/switchcontent2.htm
Here's my code, based on editing down theirs:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Dynamic Drive DHTML scripts- Switch Content Script II (icon based)</title>
<script type="text/javascript" src="switchcontent.js" ></script>
<script type="text/javascript" src="switchicon.js"></script>
<style type="text/css">
/* Specifies title style */
.iconspan{
margin: 3px;
cursor:hand;
cursor:pointer;
font-weight: bold;
}
</style>
</head>
<body>
<span id="faqtable1-title" class="iconspan"></span>
How hot is our Sun?<br/>
<div id="faqtable1" class="icongroup2">
The surface of the Sun is around 5800 Kelvin, while the core reaches over 15 million Kelvin.
</div>
<br>
<span id="faqtable2-title" class="iconspan"></span>
How big is our Sun in terms of mass? <br/>
<div id="faqtable2" class="icongroup2">
The contains more than 99.8% of the total mass of our Solar System.
</div>
<script type="text/javascript">
var faqtable=new switchicon("icongroup2", "div")
faqtable.setHeader('▼', '▶') //Set header HTML
faqtable.collapsePrevious(false) //Allow more than 1 content to be open simultanously
faqtable.setPersist(true, 7) //Enable persistence to remember last switch content states for 7 days
faqtable.init()
</script>
</body>
</html>
It looks like this when closed:
▶ How hot is our Sun?
▶ How big is our Sun in terms of mass?
And this when opened:
▼ How hot is our Sun?
The surface of the Sun is around 5800 Kelvin, while the core reaches over 15 million Kelvin.
▼ How big is our Sun in terms of mass?
The contains more than 99.8% of the total mass of our Solar System.

Categories