How to improve this script and better make a "character cycling" effect? - javascript

I made this script a while back to give my title this effect which works just not well. For a preview for the effect in question, see: https://ex0tic-python.github.io/
The code for it is rather bad and I haven't been able to find a workaround to using individual h1 tags to select each character individually. Is there a better way of doing this? The effect should look the same as it does currently, just wondering on how to touch up the current code. Any help is appreciated
This is the current code for the website you saw above:
HTML for the title
<header>
<h1 class="head_char_1">W</h1> <h1 class="head_char_2">e</h1> <h1 class="head_char_3">l</h1> <h1 class="head_char_4">c</h1>
<h1 class="head_char_5">o</h1> <h1 class="head_char_6">m</h1> <h1 class="head_char_7">e</h1> <h1 style="white-space: pre;"> </h1>
<h1 class="head_char_8">T</h1> <h1 class="head_char_9">o</h1> <h1 style="white-space: pre;"> </h1> <h1 class="head_char_10">M</h1>
<h1 class="head_char_11">y</h1> <h1 style="white-space: pre;"> </h1> <h1 class="head_char_12">W</h1> <h1 class="head_char_13">e</h1>
<h1 class="head_char_14">b</h1> <h1 class="head_char_15">s</h1> <h1 class="head_char_16">i</h1> <h1 class="head_char_17">t</h1>
<h1 class="head_char_18">e</h1> <h1 class="head_char_19">!</h1>
</header>
CSS that affects this HTML (Still in process of touching it up, ignore any weird stuff)
.flex_main {
display: flex;
flex-direction: column;
color: #03fc18;
background-color: #000000;
font-size: 17px;
font-family: Consolas;
min-height: 74vh;
margin: auto;
}
.flex_main header {
margin-left: 20px;
margin-right: 20px;
display: flex;
}
JS for the title
const titleTag = document.querySelector("header");
let charStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890~`!##$%^&*()-_=+[{]}|;:',<.>/?";
const charArr = Array.from(charStr);
function randRange(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
function cycChar(charClass, endChar, duration) {
function cyc() {
const char = titleTag.querySelector(charClass);
char.textContent = charArr[randRange(0, 101)];
}
function endCyc(inter) {
clearInterval(inter);
const char = titleTag.querySelector(charClass);
char.textContent = endChar;
}
const cycCharInter = setInterval(cyc, 100);
setTimeout(endCyc, duration, cycCharInter);
}
let finalCharsStr = "WelcomeToMyWebsite!";
const finalChars = Array.from(finalCharsStr);
const durationArr = [1, 1.2, 1.7, 2, 2.1, 2.7, 2.9, 3, 3.5, 4]
for (let i = 0; i <= 19; i++) {
cycChar(".head_char_" + String(i+1), finalChars[i], durationArr[randRange(0, 9)] * 1000)
}

Related

How to (if possible) copy the value of `:before` content to another element via CSS or JavaScript?

(Similar to How to refer a css property value from another element or How to get a DOM element's ::before content with JavaScript?)
I have a HTML page with headings (H1 to H3), and I've added the usual CSS rules to automatically prepend a hierarchical number to the headings.
That works nicely.
I also wrote some JavaScript that populates an empty span element with a table of contents derived from the page headings' textContent.
That works nicely, too.
However the table of contents lacks the numbers automatically assigned.
I saw some code to retrieve the :before part of an element using JavaScript, but that gives the style rules in my case (e.g. for a <H3>: counter(c_h1) "." counter(c_h2) "." counter(c_h3) " "), and not the value built from those style rules (e.g.: 1.3.1 ).
So I think that does not help me anything in JavaScript.
Is there any way to have the same heading numbers in the TOC created by JavaScript as those automatically added by CSS rules?
Example
.pp {
font-family: sans-serif
}
h1,
h2,
h3 {
font-family: sans-serif;
page-break-after: avoid;
break-after: avoid
}
/* counters */
:root {
counter-reset: c_h1 0 c_h2 0 c_h3 0 c_h4 0
}
h1:before {
counter-reset: c_h2 0 c_h3 0 c_h4 0;
counter-increment: c_h1;
content: counter(c_h1)" "
}
h2:before {
counter-reset: c_h3 0 c_h4 0;
counter-increment: c_h2;
content: counter(c_h1)"."counter(c_h2)" "
}
h3:before {
counter-reset: c_h4 0;
counter-increment: c_h3;
content: counter(c_h1)"."counter(c_h2)"."counter(c_h3)" "
}
/* table of contents */
span.toc:not(:empty) {
border-style: solid;
border-width: thin;
border-color: black;
padding: 1ex;
margin-top: 1em;
margin-bottom: 1em;
display: inline-block
}
div.toc-title {
font-family: sans-serif;
font-weight: bold;
padding-bottom: 1ex
}
div.toc-H1 {
padding-left: 1em
}
div.toc-H2 {
padding-left: 2em
}
div.toc-H3 {
padding-left: 3em
}
<!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="de-DE" xml:lang="de-DE">
<head>
<title>Example</title>
<script>
"use strict";
// Create table of contents from headings
function make_TOC(target_ID) {
const candidate = /^H[1-9]$/i; {
let list = document.getElementsByTagName("H1");
let toc = document.getElementById(target_ID);
let seq = 0;
var e;
if (list.length > 0) {
e = list.item(0);
let div = document.createElement("div");
div.setAttribute("class", "toc-title");
div.textContent = "Inhaltsverzeichnis";
toc.appendChild(div);
}
while (e !== null) {
let n = e.tagName;
if (n.search(candidate) !== -1) {
let div = document.createElement("div");
let link = document.createElement("a");
let id = e.id;
div.setAttribute("class", "toc-" + n);
div.appendChild(link);
link.textContent = e.textContent;
if (id === "") { // add id
id = "H." + ++seq;
e.id = id;
}
link.setAttribute("href", "#" + id);
toc.appendChild(div);
}
e = e.nextElementSibling;
}
}
}
</script>
</head>
<body>
<span class="toc" id="toc"></span>
<h1 class="pp">H1: D...</h1>
<h2 class="pp">H2: Z...</h2>
<p class="pp">D...</p>
<ul class="pp">
<li class="pp">B...</li>
<li class="pp">U...</li>
</ul>
<p class="pp">D...</p>
<h2 class="pp">H2: C...</h2>
<p class="pp">D...</p>
<p class="pp">A...</p>
<h2 class="pp">H2: E...</h2>
<h3 class="pp">H3: S...</h3>
<p class="pp">U...</p>
<h3 class="pp">H3: R...</h3>
<p class="pp">S...</p>
<h3 class="pp">H3: L...</h3>
<p class="pp">S...</p>
<h3 class="pp">H3: W...</h3>
<p class="pp">N...</p>
<script type="text/javascript">
make_TOC("toc")
</script>
</body>
</html>
So here is how the example should be displayed (indent is done via classes and CSS, my JavaScript knowledge is poor, also):
The Javascript that I used from the link you provided didn't return the counter values indeed.
From what I read I think there is no way you can access the css counter values: How can I read the applied CSS-counter value?
What you could do is recreate the outline of the document (TOC) with Javascript including the css numbering. I guess that's what you're asking, right?
Does this code fit the bill?
const elems = Array.from(document.querySelectorAll('body > *'))
let ch1 = 0
let ch2 = 0
let ch3 = 0
let s = ''
elems
.filter(el =>
el.tagName.toLowerCase() == 'h1'
|| el.tagName.toLowerCase() == 'h2'
|| el.tagName.toLowerCase() =='h3')
.forEach(el => {
if(el.tagName.toLowerCase() == 'h1') {
ch1++
ch2 = 0
ch3 = 0
}
if(el.tagName.toLowerCase() == 'h2') {
ch2++
ch3 = 0
}
if(el.tagName.toLowerCase() == 'h3') ch3++
if(ch2 == 0 && ch3 == 0) {
s += `<div>${ch1}. ${el.textContent}</div>`
}
else if (ch3 == 0) {
s += `<div>${ch1}.${ch2}. ${el.textContent}</div>`
}
else {
s += `<div>${ch1}.${ch2}.${ch3}. ${el.textContent}</div>`
}
})
let toc = document.createElement('div');
toc.innerHTML = s;
document.body.appendChild(toc)
<h1 class="pp">H1: D...</h1>
<h2 class="pp">H2: Z...</h2>
<p class="pp">D...</p>
<ul class="pp">
<li class="pp">B...</li>
<li class="pp">U...</li>
</ul>
<p class="pp">D...</p>
<h2 class="pp">H2: C...</h2>
<p class="pp">D...</p>
<p class="pp">A...</p>
<h2 class="pp">H2: E...</h2>
<h3 class="pp">H3: S...</h3>
<p class="pp">U...</p>
<h3 class="pp">H3: R...</h3>
<p class="pp">S...</p>
<h3 class="pp">H3: L...</h3>
<p class="pp">S...</p>
<h1 class="pp">H1: W...</h1>
<p class="pp">N...</p>
Well, that's not exactly the solution I was looking for, but instead of copying the original numbers from the source rendering, one can create a duplicate (possible, because the generated TOC has the correct classes assigned).
The solution just adds some more CSS rules (and counters):
span.toc:not(:empty) { counter-reset: c_t1 0 c_t2 0 c_t3 0 c_t4 0; border-style: solid; border-width: thin; border-color: black; padding: 1ex; margin-top: 1em; margin-bottom: 1em; display: inline-block }
span.toc div.toc-H1:before { counter-reset: c_t2 0 c_t3 0 c_t4 0; counter-increment: c_t1; content: counter(c_t1)" " }
span.toc div.toc-H2:before { counter-reset: c_t3 0 c_t4 0; counter-increment: c_t2; content: counter(c_t1)"."counter(c_t2)" " }
span.toc div.toc-H3:before { counter-reset: c_t4 0; counter-increment: c_t3; content: counter(c_t1)"."counter(c_t2)"."counter(c_t3)" " }
The resulting TOC looks like this for the example:
Just as an exercise and for the fun of it I made another version using recursion.
Anyway, user romellem made a good point about using css counters in the toc too so maybe that's a better solution.
const el = document.querySelector('section')
const createTocDiv = (el) => {
const buildTocArr = (el, tocEntries = []) => {
if(el == null) return
let tocEntry = null
if(el.tagName.toLowerCase() == 'section') {
// if element is a branch then start to look at first leaf
buildTocArr(el.children[0], tocEntries)
}
else if(el.tagName.toLowerCase().search('h[1-9]') == 0) {
// if element is a heading then make a new entry
tocEntry = {
level : parseInt(el.tagName.charAt(1)),
text : el.textContent
}
}
// accumulate entries
if(tocEntry != null) tocEntries.push(tocEntry)
buildTocArr(el.nextElementSibling, tocEntries)
return tocEntries
}
const tocLevelsArr = []
const tocLevelsArrEntry = []
buildTocArr(el).forEach(entry => {
while(tocLevelsArrEntry.length < entry.level) tocLevelsArrEntry.push(0)
tocLevelsArrEntry.length = entry.level
tocLevelsArrEntry[entry.level-1]++
tocLevelsArr.push(`${tocLevelsArrEntry.join('.')} ${entry.text}`)
})
const tocDiv = document.createElement('div');
tocDiv.innerHTML = `<div>${tocLevelsArr.join('</div><div>')}</div>`
return tocDiv
}
document.body.append(createTocDiv(el))
<section>
<h1 class="pp">H1: A...</h1>
<h2 class="pp">H2: B...</h2>
<p class="pp">D...</p>
<ul class="pp">
<li class="pp">B...</li>
<li class="pp">U...</li>
</ul>
<section>
<h1 class="pp">H1: C...</h1>
<section>
<h2 class="pp">H2: D...</h2>
<h3 class="pp">H3: E...</h3>
<h2 class="pp">H2: F...</h2>
<section>
<section>
<h5>H3: G...</h5>
</section>
</section>
</section>
</section>
<p class="pp">D...</p>
<h2 class="pp">H2: H...</h2>
<p class="pp">D...</p>
<p class="pp">A...</p>
<h2 class="pp">H2: I...</h2>
<h9>test</h9>
<p class="pp">N...</p>
<h3>test</h3>
<h3>test</h3>
<h1>test</h1>
</section>

VanillaJS - find middle element in the container

So I have a setup like this
<div class=“container”>
<div class=“segment segment1”></div>
<div class=“segment segment2”></div>
<div class=“segment segment3”></div>
.
.
.
<div class=“segmentN”></div>
</div>
Where N is an number defined by user so list is dynamical. For container I have applied styles to display it as grid, so EVERY time list has 3 items displayed, list is scrollable. My problem is, how can I via VanillaJS find element which is in the middle of container ? If there are 3 elements in the page, it should select 2nd one, when scrolling down it should select element which is in the middle of container every time to apply some styles to it in addition to grab it’s id. If there are 2 elements, it should select 2nd item as well. I was thinking about checking height of container, divide it by half and checking position of element if it’s in range. So far I was able to write this code in js
function findMiddleSegment() {
//selecting container
const segmentListContainer = document.querySelector(`.container`);
const rect = segmentListContainer.getBoundingClientRect();
//selecting all divs
const segments = document.querySelectorAll(`.segment`);
segments.forEach( (segment) => {
const location = segment.getBoundingClientRect();
if( (location.top >= rect.height / 2) ){
segment.classList.add(`midsegment`);
} else {
segment.classList.remove(`midsegment`);
}
});
}
But it doesn’t work. It finds element in the middle as should, but also applies style for every other element beneath middle segment. I’ve read some answers on stackoverflow, but couldn’t find any idea how to solve my problem.
EDIT
In addition to my problem I add additional function to show how I invoke it.
function handleDOMChange() {
findMiddleSegment(); //for "first run" when doc is loaded
const segmentListContainer = document.querySelector(`.container`);
segmentListContainer.addEventListener('scroll', findMiddleSegment);
}
A very easy way to do it is using the Intersection Observer:
const list = document.querySelector('ul'),
idDisplay = document.querySelector('p b');
const observer = new IntersectionObserver(
highlightMid,
{
root: list,
rootMargin: "-33.33% 0%",
threshold: .5
}
);
function makeList() {
list.innerHTML = '';
observer.disconnect();
const N = document.querySelector('input').value;
for (let i = 0; i < N;) {
const item = document.createElement('li');
item.id = `i_${++i}`;
item.textContent = `Item #${i}`;
list.append(item);
observer.observe(item);
}
};
function highlightMid(entries) {
entries.forEach(entry => {
entry.target.classList
.toggle('active', entry.isIntersecting);
})
const active = list.querySelector('.active');
if (active) idDisplay.textContent = '#' + active.id;
}
ul {
width: 50vw;
height: 50vh;
margin: auto;
padding: 0;
overflow-y: auto;
border: solid 1px;
}
li {
box-sizing: border-box;
height: 33.33%;
padding: .3em 1em;
list-style: none;
transition: .3s;
}
.active {
background: #6af;
}
<i>Make a list of:</i>
<input type="number" min="2" placeholder="number of items">
<button onclick="makeList()">make</button>
<p>Active id is <b>yet to set</b></p>
<ul></ul>
If container has only a list of segments inside, it's easer to count the element's children and find the mid element.
const segmentListContainer = document.querySelector(`.segmentListContainer`);
const midSegmentIndex = Math.floor(segmentListContainer.children.length / 2) - 1;
let midSegment = segmentListContaner.children[midSegmentIndex];
midSegment.classList.add('midsegment');
P.S.
The reason why your code adds 'mdsegment' to each element's class name after the real midsegment element is because of this conditional statement line you wrote.
if(location.top >= rect.height / 2){
segment.classList.add(`midsegment`);
}
Something like this. You can use Math.round, Math.ceil or Math.floor like I did. This works because querySelectorAll returns an array and you can use array.length to count the total number of items in the array then use a for loop to loop over all the segments and place the class based on the Math.(round, floor or ceil) based on your needs.
const container = document.querySelector(".container");
const segments = container.querySelectorAll(".segment");
const middleSegment = Math.floor(segments.length / 2);
for (let index = 0; index < segments.length; index++) {
segments[middleSegment].classList.add("middle-segment");
}
.middle-segment{
background-color: red;
}
<div class="container">
<div class="segment">segment</div>
<div class="segment">segment</div>
<div class="segment">segment</div>
<div class="segment">segment</div>
<div class="segment">segment</div>
<div class="segment">segment</div>
<div class="segment">segment</div>
</div>
You don't need javascript for this. CSS will do
.container {
width: 350px;
}
.container .segment {
width: 100px;
height: 100px;
float: left;
background-color: #EEE;
border: 1px dotted gray;
margin: 3px;
text-align: center;
color: silver;
}
.segment:nth-child(3n-1) {
background-color: aquamarine;
color: black;
}
<div class="container">
<div class="segment">segment</div>
<div class="segment">segment</div>
<div class="segment">segment</div>
<div class="segment">segment</div>
<div class="segment">segment</div>
<div class="segment">segment</div>
<div class="segment">segment</div>
<div class="segment">segment</div>
<div class="segment">segment</div>
<div class="segment">segment</div>
<div class="segment">segment</div>
<div class="segment">segment</div>
<div class="segment">segment</div>
<div class="segment">segment</div>
</div>

How to Delay a Javascript Function Until it is in the middle of web page?

Hello I have a number animation on my web page and I dont want the animation to start until it is in the middle of the web page. I tried to google onscroll and other options but I could not get this to work properly.
I prefer for the animation not to start until the visitor has scrolled down to 472px. As of right now as soon as the web page loads the number animation starts automatically. Any help I would really appreciate it.
// 472 px - Starts Yellow Counter Section
const counters = document.querySelectorAll('.counter');
const speed = 200; // The lower the slower
counters.forEach(counter => {
const updateCount = () => {
const target = +counter.getAttribute('data-target');
const count = +counter.innerText;
// Lower inc to slow and higher to slow
const inc = target / speed;
// console.log(inc);
// console.log(count);
// Check if target is reached
if (count < target) {
// Add inc to count and output in counter
counter.innerText = count + inc;
// Call function every ms
setTimeout(updateCount, 1);
} else {
counter.innerText = target;
}
};
updateCount();
});
.bg-yellow-white {
background: #f7c51e;
color: white;
}
.container {
max-width: 1404px;
margin: auto;
padding: 0 2rem;
overflow: hidden;
}
.l-heading {
font-weight: bold;
font-size: 4rem;
margin-bottom: 0.75rem;
line-height: 1.1;
}
/* Padding */
.py-1 {
padding: 1.5rem 0;
}
.py-2 {
padding: 2rem 0;
}
.py-3 {
padding: 3rem 0;
}
/* All Around Padding */
.p-1 {
padding: 1.5rem;
}
.p-2 {
padding: 2rem;
}
.p-3 {
padding: 3rem;
}
/* ======================== Red Block ======================== */
.red-block {
height: 472px;
width: 100%;
background-color: red;
}
/* ======================== PROJECS COMPLETED ======================== */
#projects-completed .container .items {
display: flex;
justify-content: center;
flex-wrap: wrap;
}
#projects-completed .container .items .item .circle {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
<div class="red-block">
<p>red block</p>
</div>
<section id="projects-completed" class="counters bg-yellow-white">
<div class="container">
<div class="items">
<div class="item text-center p-3">
<div class="circle">
<div class="counter l-heading" data-target="1750">500</div>
</div>
<h2 class="py-2">Projects Completed</h2>
</div>
<div class="item text-center p-3">
<div class="circle py-2">
<div class="l-heading counter" data-target="5">500</div>
</div>
<h2 class="py-2">Staff Members</h2>
</div>
<!-- <div class="item text-center p-3">
<div class="circle">
<h3 class="l-heading ">1750</h3>
</div>
<h2 class="py-2">Projects Completed</h2>
</div>
<div class="item text-center p-3">
<div class="circle py-2">
<h3 class="l-heading">5</h3>
</div>
<h2 class="py-2">Staff Members</h2>
</div> -->
</div>
</div>
</section>
wesbos has great video on this https://www.youtube.com/watch?v=uzRsENVD3W8&list=PLu8EoSxDXHP6CGK4YVJhL_VWetA865GOH&index=14&t=0s
Basically what you need to do is listen for scroll and check where user currently is compared to desired place in px
you can check code here and adjust it to your needs https://github.com/wesbos/JavaScript30/blob/master/13%20-%20Slide%20in%20on%20Scroll/index-FINISHED.html
Try getBoundingClientRect(). document.querySelector( 'some element' ).getBoundingClientRect() will give you the properties of the specific element
for Example if you want to start an animation when an element is visible to user on his screen ( in the visible viewport ), you can use this to call the function and start the animation
let calledStatus = 0; // some flag variable to remember if function is called
window.onscroll = function(){
element = document.querySelector( '.some element' );
clientRect = element.getBoundingClientRect();
if( clientRect.top < window.innerHeight && clientRect.top > ( clientRect.height * -1) && calledStatus == 0){
//call your function or do other stuff
console.log('called' )
calledStatus = 1;
}
}
By using jquery , first add this reference script above your js code or referenece script
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></scrip>
....
</head>
if you want the code to launch specifically after 472 px:
js
$(document).ready(function () {
Let initialScroll = true;
//you can decrease or increase 472 depending on where exactly
//you want your function to be called
$(document).scroll(function () {
if (($(document).scrollTop() > 472)&& initialScroll) {
//call your function here
console.log( "reached 472")
InitialScroll=false;
}
});
});
if you want your function to start after reaching the middle
of the document
you place a div where the middle of the html code is :
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
...
<div id="middle"></div>
...
</body>
</html>
js
$(document).ready(function () {
Let initialScroll=true
$(document).scroll(function () {
if (($(document).scrollTop() >=$('#middle').position().top)&&initialScroll) {
//call your function here
console.log( "reached middle")
InitialScroll=false;
}
});
});
There is a native javascript API for "listetning" where the user currently is on the page called Intersection Observer. Basically you set a callback which should execute once the desired content scrolls into view.
It's used for all those fancy page animations where cards appear once you start scrolling to the bottom of the page since it's far more efficient than listening on the scroll event.
Kevin Powell did a great video about this topic.
Hope it helps!
Here's a code copy pasted, but it should give you a clue on how it should work:
document.addEventListener("DOMContentLoaded", function() {
let lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));
let active = false;
const lazyLoad = function() {
if (active === false) {
active = true;
setTimeout(function() {
lazyImages.forEach(function(lazyImage) {
if ((lazyImage.getBoundingClientRect().top <= window.innerHeight && lazyImage.getBoundingClientRect().bottom >= 0) && getComputedStyle(lazyImage).display !== "none") {
lazyImage.src = lazyImage.dataset.src;
lazyImage.srcset = lazyImage.dataset.srcset;
lazyImage.classList.remove("lazy");
lazyImages = lazyImages.filter(function(image) {
return image !== lazyImage;
});
if (lazyImages.length === 0) {
document.removeEventListener("scroll", lazyLoad);
window.removeEventListener("resize", lazyLoad);
window.removeEventListener("orientationchange", lazyLoad);
}
}
});
active = false;
}, 200);
}
};
document.addEventListener("scroll", lazyLoad);
window.addEventListener("resize", lazyLoad);
window.addEventListener("orientationchange", lazyLoad);
});

Experimenting with writing Rock Paper Scissors game using HTML, css Bootstrap4 and JavaScript and cannot get my function to chose new image to work

In my class we were creating a program to provide images of dice and have them roll random numbers when the refresh button was clicked. We were practicing using querySelector and setAttribute. I thought it would be good for my learning to expand the lesson to do paper rock scissors so I could get more familiar with the coding. However, I cannot get my code to run through the functions that will display the picture of the paper, rock or scissors that is associated with randomNumber1 or randomNumber2. I tried using bootstrap4 and then just javascript and don't know what I'm doing wrong since I believe in my class we used almost the same code format for the random rolling of two dice, but only used js, css and html5 without using bootstrap.
First I tried writing the code using Bootstrap4, and everything appeared to be working correctly until I added in the function for randomizing the rolling of the dice by clicking refresh. Then I thought, maybe since I can't get bootstrap to work, I should try it in regular javascript and get it running. The code will not run there either. I am puzzled and have spent over a day trying to find answers to my problem. Below I have included the first set of code I created using bootstrap4 and the second set using just javascript. In both programs, the rock image that I just want to have take up space when the program is started, shows up so I know there is contact between the images folder and the program, but I don[t know why I cant't get the pictures to change once I click refresh.
First code using Bootstrap 4
<!-- css stylesheet -->
<link rel="stylesheet" href="stylesPractice2.css">
<!-- Google Fonts -->
<link href="https://fonts.googleapis.com/css?family=Kalam:400,700" rel="stylesheet">
</head>
<body>
<div class="container">
<h1 class="header">Rock, Paper, Scissors</h1>
<div class = "decision">
<h2 class = "subtopic">Need to make a decision???</h2>
</div>
<div class = "players">
<h2 class = "subtopic">Player 1</h2>
<img class = "responsive img1" src = "images/rockPaperScissors3.png">
</div>
<div class="players">
<h2 class = "subtopic">Player 2</h2>
<img class = "responsive img2" src = "images/rockPaperScissors3.png">
</div>
<div class = "whoWins">
<h2 class = "subtopic">And the winner is . . .</h2>
</div>
</div>
<script src = "rockPaperScissors.js" charset = "utf-8"></script>
</body>
JavaScript
// FIRST PLAYER CREATE RANDOM NUMBER
var randomNumber1 = (Math.floor(Math.random() * 3)) + 1;
// SECOND PLAYER CREATE RANDOM NUMBER
var randomNumber2 = Math.floor(Math.random() * 3) + 1;
function images1() {
if(randomNumber1 === 1) {
document.querySelector(".img1").setAttribute("src", "images/rockPaperScissors1.png");
}
else if(randomNumber1 === 2) {
document.querySelector(".img1").setAttribute("src", "images/rockPaperScissors2.png");
}
else if(randomNumber1 === 3) {
document.querySelector(".img1").setAttribute("src", "images/rockPaperScissors3.png");
}
}
and I stopped there because img1 was not randomizing and I could not find the answer in research.
2nd Programming No Bootstrap used.
<link rel="stylesheet" href="stylesPractice2.css">
<!-- Google Fonts -->
<link href="https://fonts.googleapis.com/css?family=Kalam:400,700" rel="stylesheet">
</head>
<body>
<div class="container">
<h1 class="header">Rock, Paper, Scissors</h1>
<div class = "decision">
<h2 class = "subtopic">Need to make a decision???</h2>
</div>
<div class = "players">
<h2 class = "subtopic">Player 1</h2>
<img class = "responsive img1" src = "images/rockPaperScissors3.png">
</div>
<div class="players">
<h2 class = "subtopic">Player 2</h2>
<img class = "responsive img2" src = "images/rockPaperScissors3.png">
</div>
<div class = "whoWins">
<h2 class = "subtopic">And the winner is . . .</h2>
</div>
</div>
<script src = "rockPaperScissors.js" charset = "utf-8"></script>
</body>
Both programs included the </html> tag
CSS
body {
font-family: 'Kalam', cursive;
background-color: #76fc23;
color: #2357fc;
}
.container {
width: 80%;
margin: auto;
text-align: center;
}
.title {
margin-top: 4.0rem;
}
.header {
font-weight: 700;
font-size: 5rem;
line-height: 1.5;
}
h2 {
font-size: 3rem;
}
.players {
display: inline-block;
margin: 0 2rem 3rem 2rem;
}
.player1 {
text-align: center;
margin-bottom: 3rem;
/* text-align: right;
padding-right: 5%; */
}
.player2 {
text-align: center;
margin-bottom: 3rem;
/* text-align: left;
padding-left: 5%; */
}
.img {
width: 100%;
height: auto;
}
JavaScript
// FIRST PLAYER CREATE RANDOM NUMBER
var randomNumber1 = Math.random();
randomNumber1 = Math.random() * 6;
randomNumber1 = (Math.floor(Math.random() * 6)) + 1;
// SECOND PLAYER CREATE RANDOM NUMBER
var randomNumber2 = Math.random();
randomNumber2 = Math.random() * 6;
randomNumber2 = Math.floor(Math.random() * 3) + 1;
function images1() {
if(randomNumber1 === 1) {
document.querySelector(".img1").setAttribute("src", "images/rockPaperScissors1.png");
}
else if(randomNumber1 === 2) {
document.querySelector(".img1").setAttribute("src", "images/rockPaperScissors2.png");
}
else{
document.querySelector(".img1").setAttribute("src", "images/rockPaperScissors3.png");
}
}
function images2() {
if(randomNumber2 === 1) {
document.querySelector(".img2").setAttribute("src", "images/rockPaperScissors1.png");
}
else if(randomNumber2 === 2) {
document.querySelector(".img2").setAttribute("src", "images/rockPaperScissors2.png");
}
else{
document.querySelector(".img2").setAttribute("src", "images/rockPaperScissors3.png");
}
}
images1();
images2();
In the javascript, I want my functions to change the images to the ones listed if randomNumber1 or randomNumber2 equal 1, 2 or 3. Instead, they just stay on the original image from html. I also need to use querySelector and setAttribute in my code.
You can't dynamically change an image's src attribute because img is a replaced element. This means that an img isn't part of the document, but an external resource that's inserted into the document. If the image doesn't exist in a real sense, then it can't be inserted.
You need to construct a new image via new Image() in your JS to be able to replace the existing image.

Using JavaScript to change text on the page every half-second

So, what I'm hoping to do is change the text inside a set of <p> tags every half-second. The set of tags in question is in this block of code in my body:
<div class="outerdiv" id="col2">
<p id="matrixText"></p>
</div>
Right below the above code I have the JavaScript that should call a function every half-second:
<script type="text/javascript">
setInterval("changeMatrixText()", 500);
</script>
I have the function changeMatrixText defined inside my head:
function changeMatrixText()
{
var newtext = "";
for (var i = 0; i < 1000; i++)
newtext += Math.floor((Math.random()*10)+1) % 2 ? "0" : "1";
document.getElementById("matrixText").value = newtext;
}
As you see, that's supposed to set the text to a random string of 0's and 1's. But it's not working. Any idea why?
Just in case you need to see my entire code .....
<html>
<head>
<title>Simple encrypt/decrypt</title>
<style type="text/css">
body
{
background-color: #A9F5F2;
width: 900px;
padding: 0px;
}
.outerdiv
{
margin: 5px;
border: 2px solid #FF8000;
background-color: #FFFFFF;
}
.outerdiv > p
{
margin: 5px;
word-wrap:break-word
}
.outerdiv > h1
{
margin: 5px;
}
#col1
{
width: 500x;
height: 800px;
float: left;
}
#col2
{
width: 295px;
height: 1500px;
float: right;
font-family: Courier New;
overflow: hidden;
}
#title1div
{
font-family: Arial;
width: 100%;
}
#insctdiv
{
font-family: Arial;
width: 100%;
}
#iptdiv
{
height: 400px;
width: 100%;
}
#buttonsdiv
{
text-align: center;
width: 100%;
}
#inputText
{
width: 100%;
height: 100%;
resize: none;
}
</style>
<script type="text/javascript">
function encrypt()
{
var text = document.getElementById("inputText").value;
newstring = "";
/* Make newstring a string of the bit representations of
the ASCII values of its thisCharacters in order.
*/
for (var i = 0, j = text.length; i < j; i++)
{
bits = text.charCodeAt(i).toString(2);
newstring += new Array(8-bits.length+1).join('0') + bits;
}
/* Compress newstring by taking each substring of 3, 4, ..., 9
consecutive 1's or 0's and it by the number of such consecutive
thisCharacters followed by the thisCharacter.
EXAMPLES:
"10101000010111" --> "10101401031"
"001100011111111111111" --> "0011319151"
*/
newstring = newstring.replace(/([01])\1{2,8}/g, function($0, $1) { return ($0.length + $1);});
document.getElementById("inputText").value = newstring;
}
function decrypt()
{
var text = document.getElementById("inputText").value;
text = text.trim();
text.replace(/([2-9])([01])/g,
function (all, replacementCount, bit) {
return Array(+replacementCount + 1).join(bit);
}).split(/(.{8})/g).reduce(function (str, byte) {
return str + String.fromCharCode(parseInt(byte, 2));
}, "");
document.getElementById("inputText").value = text;
}
function changeMatrixText()
{
var newtext = "";
for (var i = 0; i < 1000; i++)
newtext += Math.floor((Math.random()*10)+1) % 2 ? "0" : "1";
document.getElementById("matrixText").value = newtext;
}
</script>
</head>
<body>
<div id="col1">
<div class="outerdiv" id="title1div">
<h1>Reversible text encryption algorithm</h1>
</div>
<div class="outerdiv" id="insctdiv">
<p>Type in or paste text below, then click <b>Encrypt</b> or <b>Decrypt</b></p>
</div>
<div class="outerdiv" id="iptdiv">
<textarea id="inputText" scrolling="yes"></textarea>
</div>
<div class="outerdiv" id="buttonsdiv">
<button onclick="encrypt()"><b>Encrypt</b></button>
<button onclick="decrypt()"><b>Decrypt</b></button>
</div>
</div>
<div class="outerdiv" id="col2">
<p id="matrixText"></p>
</div>
<script type="text/javascript">
setInterval("changeMatrixText()", 500);
</script>
</body>
</html>
In essence, I'm trying to make the right column of my page keep printing inside a new string of 0's and 1's every half-second, kinda like on the computer screen on the movie The Matrix, if you catch my drift.
According to MDN, the elements with a value attribute include <button>, <option>, <input>, <li>, <meter>, <progress>, and <param>. You'll need to set the innerHTML instead.
document.getElementById("matrixText").value = newtext;
to
document.getElementById("matrixText").innerHTML = newtext;
and
setInterval("changeMatrixText()", 500);
to
setInterval(changeMatrixText, 500);
Working Demo
document.getElementById("matrixText").value = newtext;
.value is used for form fields instead use
document.getElementById("matrixText").innerHTML = newtext;
in your changeMatrixText function
Here's an example of how you can do this:
http://jsfiddle.net/35W4Z/
The main difference is that a <p> element doesn't have a .value attribute. Instead, use the innerHTML attribute (as shown in the JSFiddle example)
Hope this helps!
Well for fun, I stuck this in a fiddle: http://jsfiddle.net/jdmA5/1/
So two things, mostly:
1) You can't set the "value" of a div element. You have to set the .innerHTML:
document.getElementById("matrixText").innerHTML = newtext;
2) This could be due to the fact I built this out in fiddle, but setInterval is notorious for not running like you expect unless you give each iteration its own memory space. I did this by wrapping the call to changeMatrix in a anonymous function:
setInterval(function() {changeMatrixText();}, 500);
Check out the jsfiddle link to see it in action.
Have you tried changing the setInterval method to accept the first argument as the function itself (the name, minus the parentheses), rather than a string...
As you are not passing any parameters explicitly, you can invoke the function as follows:
setInterval(changeMatrixText, 500);
Should you have needed to supply some parameters, then the following would work:
setInterval(function() {
changeMatrixText(myParam1, myParam2); // etc, etc
}, 500);

Categories