I'm trying to add a new click handler to the #myDiv element below. I've tried something like this:
document.getElementById("myDiv").addEventListener("click", blah);
function blah() {
console.log("test");
}
But I keep getting a console error stating, "Cannot read properties of null (reading 'addEventListener'). Is this because the other event listener isn't being called yet? Any help would be greatly appreciated. Thanks!
<!DOCTYPE html>
<html lang="en">
<head>
<style>
[data-color="red"] { color: red; }
[data-color="blue"] { color: blue; }
[data-color="green"] { color: green; }
[data-color="orange"] { color: orange; }
[data-color="purple"] { color: purple; }
</style>
<script>
window.myHandler = function () {
console.log('Click!');
};
window.getRandomNumber = function (max) {
return Math.floor(Math.random() * max)
}
var colors = ['red', 'blue', 'green', 'orange', 'purple'];
window.changeHeadlineColor = function (croHeadline) {
var random = getRandomNumber(5000);
var randomString = random.toString();
setTimeout(() => {
var colorKey = (randomString.length < 4) ? 0 : parseInt(randomString.charAt(0));
croHeadline.setAttribute('data-color', colors[colorKey]);
changeHeadlineColor(croHeadline);
}, random);
};
</script>
<script>
////////////////////
/* YOUR CODE HERE */
////////////////////
</script>
</head>
<body>
<div id="myDiv">OMG Click me!</div>
<script>
document.querySelector('#myDiv').addEventListener('click', myHandler);
setTimeout(() => {
myDiv.insertAdjacentHTML('beforebegin', '<h1 id="cro-headline" data-color="red">Cro Metrics</h1>');
var croHeadline = document.querySelector('#cro-headline');
changeHeadlineColor(croHeadline);
}, getRandomNumber(5000));
</script>
</body>
</html>
Because document.getElementById("myDiv") is in the head tag which execute before the DOM loaded.
One solution is to add it in a script at the end of the body ( after the DOM loaded )
The second solution to make your script as module.
<!DOCTYPE html>
<html lang="en">
<head>
<style>
[data-color="red"] { color: red; }
[data-color="blue"] { color: blue; }
[data-color="green"] { color: green; }
[data-color="orange"] { color: orange; }
[data-color="purple"] { color: purple; }
</style>
<script>
window.myHandler = function () {
console.log('Click!');
};
window.getRandomNumber = function (max) {
return Math.floor(Math.random() * max)
}
var colors = ['red', 'blue', 'green', 'orange', 'purple'];
window.changeHeadlineColor = function (croHeadline) {
var random = getRandomNumber(5000);
var randomString = random.toString();
setTimeout(() => {
var colorKey = (randomString.length < 4) ? 0 : parseInt(randomString.charAt(0));
croHeadline.setAttribute('data-color', colors[colorKey]);
changeHeadlineColor(croHeadline);
}, random);
};
</script>
<script type="module">
window.blah = function () {
console.log('Click!');
};
document.querySelector('#myDiv').addEventListener('click', blah);
</script>
</head>
<body>
<div id="myDiv">OMG Click me!</div>
<script>
document.querySelector('#myDiv').addEventListener('click', myHandler);
setTimeout(() => {
myDiv.insertAdjacentHTML('beforebegin', '<h1 id="cro-headline" data-color="red">Cro Metrics</h1>');
var croHeadline = document.querySelector('#cro-headline');
changeHeadlineColor(croHeadline);
}, getRandomNumber(5000));
</script>
</body>
</html>
Related
I am attempting to switch stylesheets on my website using a button.
When I use this code, the stylesheet becomes nonexistent when I press the button and I cannot switch back unless I refresh the site. I do not know what is happening.
JavaScript
const stylesUrls = ["C:\Users\purrl\OneDrive\Desktop\Personal Coding\html\claires.css", "C:\Users\purrl\OneDrive\Desktop\Personal Coding\html\queen-of-hearts.css"];
window.onload = function(){
let switcherBtn = document.getElementById("themes");
switcherBtn.onclick = function() {
let style = document.getElementById('ss');
let stylesUrl = stylesUrls.shift();
style.href = stylesUrl;
stylesUrls.push(stylesUrl);
}
};
HTML
<link rel="stylesheet" href='C:\Users\purrl\OneDrive\Desktop\Personal Coding\html\claires.css' id="ss" type="text/css">
<button id="themes" type="button">Switch Theme</button>
Store the current index in a variable and change that instead.
const stylesUrls = ["foo", "bar"];
window.onload = function() {
const switcherBtn = document.getElementById("themes");
let currentIndex = 0;
switcherBtn.addEventListener("click", function() {
let style = document.getElementById('ss');
let stylesUrl = stylesUrls[currentIndex];
style.href = stylesUrl;
if (currentIndex === 0) {
currentIndex = 1;
} else {
currentIndex = 0;
}
});
}
If you're trying to do themes, you can change a class on the root html tag and include a style tag to both stylesheets and then use the corresponding class in each stylesheet.
Example
queen-of-hearts.css;
html.queen h1 {
color: red;
font-size: 20px;
}
html.queen div {
background-color: pink;
}
claires.css;
html.claire h1 {
color: blue;
font-size: 30px;
}
html.claire div {
background-color: green;
}
HTML;
<html class="queen"> <!-- Toggle between queen and claire using JavaScript -->
<head>
<link href="insert link to claires.css here" />
<link href="insert link to queen-of-hearts.css here" />
</head>
</html>
JS;
...
switcherBtn.addEventListener("click", function() {
if (document.documentElement.classList.contains("claire")) {
document.documentElement.classList.replace("claire", "queen");
} else {
document.documentElement.classList.replace("queen", "claire");
}
});
Demo
I build a small application to switch between random images in an iframe, I would like that after 10 or 20 images the user will get an image or source I want him to get and not a random one, and then return to the loop.
I have a problem with the count and if function, will appreciate any help. Thanks
<body>
<iframe id="img_main" src="https://www.example.com/img_4.jpg" width="600" height="800" frameborder="1" scrolling="no"></iframe>
<br>
<button id="H" type="button" onclick=(newImg(),clickCounter(),changeImg())>images</button>
<div id="result"></div>
<script>
function newImg(){
var myArray = [
"img_1.jpg",
"img_2.jpg",
"img_3.jpg",
"img_4.jpg"
];
var imgNew = "https://example.com/"
var randomItem = myArray[Math.floor(Math.random()*myArray.length)];
document.getElementById("img_main").src = "https://example.com/" + randomItem ;
}
function clickCounter() {
if (typeof(Storage) !== "undefined") {
if (localStorage.clickcount) {
localStorage.clickcount = Number(localStorage.clickcount)+1;
} else {
localStorage.clickcount = 1;
}
function changeImg(){
if (localStorage.clickcount = 10,20) {
document.getElementById("ins_main").src = "https://example.com/pre_defined_img.jpg";
}
}
</script>
</body>
the way I see that...
by simply use of the modulo (finds the remainder after division)
you don't need to use an iframe, img element is enough.
use an IIFE. as closure method
file : "myScript.js"
const imgBox = (function()
{
const refURL = 'https://i.picsum.photos/id/'
, imgName = [ '251/300/500.jpg', '252/300/500.jpg', '253/300/500.jpg', '254/300/500.jpg'
, '255/300/500.jpg', '256/300/500.jpg', '257/300/500.jpg', '258/300/500.jpg'
, '259/300/500.jpg', '260/300/500.jpg', '261/300/500.jpg', '146/300/500.jpg'
, '263/300/500.jpg', '264/300/500.jpg', '265/300/500.jpg', '266/300/500.jpg'
, '267/300/500.jpg', '268/300/500.jpg', '269/300/500.jpg', '270/300/500.jpg'
, '271/300/500.jpg', '272/300/500.jpg', '273/300/500.jpg', '274/300/500.jpg'
]
, imgZero = '250/300/500.jpg'
, imgSiz = imgName.length
, imgMain = document.getElementById('img-main')
;
var imgNum = 0, randomI = 0;
const obj =
{ setNext()
{
if (!(++imgNum % 10)) // each 10 times
{
imgMain.src = refURL + imgZero
}
else
{
randomI = Math.floor(Math.random() * imgSiz)
imgMain.src = refURL + imgName[randomI]
}
return imgNum
}
}
return obj
})()
const btImg = document.getElementById('bt-img')
, imgCount = document.getElementById('img-count')
, banner = document.getElementById('my-banner')
;
btImg.onclick =evt=>
{
let counter = imgBox.setNext()
imgCount.textContent = counter
if (!(counter %50)) // each 50 times..
{
changeBanner()
}
}
// changing banner function...
function changeBanner()
{
//.....
// do what ever you want to change your banner
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
<style>
img#img-main {
width: 300px;
height: 500px;
border: 1px solid grey;
padding: 2px;
}
</style>
</head>
<body>
<img id="img-main" src="https://i.picsum.photos/id/250/300/500.jpg" alt="" >
<p id="img-count">0</p>
<button id="bt-img">Image change</button>
<div id="my-banner">the banner</div>
<script src="myScript.js"></script>
</body>
</html>
I found this script online and it works great my biggest problem is it haves append in this script. And I notice its making copies every time I press the button so how
can I prevent that and I want to use toggle to that same button to show and hide. So is append really necessary for this script? If not then what can I use instead
to give the outcome of using a toggle button to show and hide the objects contents with out outputting additional copies because of append? Here's the code.
var data = { on_sale: [
{
name:"Red Mask",
price:"$500"
},
{
name:"Blue Cape",
price:"$200"
}
]};
$(document).ready(function() {
$("button").click(function(){
$.each(data.on_sale, function (i) {
$.each(data.on_sale[i], function (key, val) {
$("#x").append(val+"<br>").toggle();
});
});
});
});
#x {
color: red;
text-align: center;
}
.container{
background-color: gold;
width: 250px;
margin: auto;
}
<!DOCTYPE html>
<html>
<head>
<script src="https://code.jquery.com/jquery-1.10.2.js"></script>
</head>
<body>
<div id="programs-header"></div>
<div class="container">
<h2 id="x"></h2>
</div>
<button>Click</button>
</body>
</html>
var data = { on_sale: [
{
name:"Red Mask",
price:"$500"
},
{
name:"Blue Cape",
price:"$200"
}
]};
$(document).ready(function() {
var x = $('#x');
var output = '';
$.each(data.on_sale, function (index, element) {
output += element.name + '<br>' +element.price +'<br>';
});
console.log(output);
x.html(output);
$("button").click(function(){
x.toggle();
});
});
Instead of filling x on click, you can fill it on document ready, and then you can just toggle x with .toggle().
If you want it to be invisible on first load, add
display: none;
to your css for x
I modify your js ,have a look:
var data = { on_sale: [
{
name:"Red Mask",
price:"$500"
},
{
name:"Blue Cape",
price:"$200"
}
]};
var _html = '';
$(document).ready(function() {
$("button").click(function(){
if (!_html) {
$.each(data.on_sale, function (i) {
$.each(data.on_sale[i], function (key, val) {
_html+= val+"<br>";
});
});
$('#x').append(_html).show();
} else {
$('#x').toggle();
}
});
});
#x {
color: red;
text-align: center;
}
.container{
background-color: gold;
width: 250px;
margin: auto;
}
<!DOCTYPE html>
<html>
<head>
<script src="https://code.jquery.com/jquery-1.10.2.js"></script>
</head>
<body>
<div id="programs-header"></div>
<div class="container">
<h2 id="x"></h2>
</div>
<button>Click</button>
</body>
</html>
All you have to do is validate the data your appending if it was already appended once(if you want it to be appended once), in this case I created a toggle to check if the data was appended already.
var isAppended = false;
var data = { on_sale: [
{
name:"Red Mask",
price:"$500"
},
{
name:"Blue Cape",
price:"$200"
}
]};
$(document).ready(function() {
$("button").click(function(){
if(!isAppended){
$.each(data.on_sale, function (i) {
$.each(data.on_sale[i], function (key, val) {
$("#x").append(val+"<br>").toggle();
$("#x").show();
});
});
isAppended = true;
}else{
//insert method here that hides and show the appended element like this
var query = $('#x');
if (query.is(':visible')) {
query.hide();
} else {
query.show();
}
}
});
});
Another version, using Array.map() instead of Array.each().
For single key in data object:
var data = {on_sale: [{name: "Red Mask",price: "$500"},{name: "Blue Cape",price: "$200"}]};
$(document).ready(function () {
var clicked = false;
$("button").on("click", function () {
if (!clicked) //If button not clicked then add content to h2 tag
{
$("#x").html(data.on_sale.map(function(e) { return e.name + "<br/>" + e.price }).join("<br/>"));
clicked = true;
}
else
$("#x").toggle(); //toggle display
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<style type="text/css">
#x {
color: red;
text-align: center;
}
.container {
background-color: gold;
width: 250px;
margin: auto;
}
</style>
<div id="programs-header"></div>
<div class="container">
<h2 id="x"></h2>
</div>
<button>Click</button>
For multiple keys in data object:
var data = {
on_sale: [{ name: "Red Mask", price: "$500" }, { name: "Blue Cape", price: "$200" }],
on_sale1: [{ name: "Red Mask", price: "$500" }, { name: "Blue Cape", price: "$200" }]
};
$(document).ready(function () {
var clicked = false;
$("button").on("click", function () {
if (!clicked) {
var arr = [];
Object.keys(data).forEach(function (key) {
arr.push(data[key].map(function (e) { return e.name + "<br/>" + e.price }).join("<br/>"));
});
$("#x").html(arr.join("<br/>"));
clicked = true;
}
else
$("#x").toggle();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<style type="text/css">
#x {
color: red;
text-align: center;
}
.container {
background-color: gold;
width: 250px;
margin: auto;
}
</style>
<div id="programs-header"></div>
<div class="container">
<h2 id="x"></h2>
</div>
<button>Click</button>
In a html page with mathematical formulas using MathJax, I'm trying a smooth transition in the change from one formula to the another.
Here is the current code, that you can test here
<!DOCTYPE html>
<html>
<head>
<!-- https://jsfiddle.net/LnfL020r/2 -->
<title>math guided training</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
TeX: {
Macros: {
mgtMult: "",
mgtSelect: [ "\\bbox[10px,border:1px solid red]{#1}", 1],
}
}
});
</script>
<script type="text/javascript"
src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script>
<style>
.results {
display: flex;
height: 4cm;
position: relative;
}
#fadeBox,
#visibleBox {
width: 100%;
height: 100%;
position: absolute;
top: 0%;
left: 0;
}
</style>
</head>
<body>
<script>
var QUEUE = MathJax.Hub.queue; // shorthand for the queue
var math = null; // the jax element
var box = null; // the box math is in
var formula = "1+2x^3";
var SHOWBOX = function () {
var a = $('#box').html();
var dstDiv = $('#visibleBox');
dstDiv.html(a);
}
var SHOWBOX_FADE = function () {
var a = $('#box').html();
var dstDiv = $('#visibleBox');
var fadeDiv = $('#fadeBox');
fadeDiv.html(a);
fadeDiv.fadeIn(2000,function() {
dstDiv.html(a);
fadeDiv.hide();
});
}
var REFRESH = function () {
QUEUE.Push(
["Text",math,formula], // == math.Text(formula), [ method, object, args... ]
SHOWBOX
);
}
var REFRESH_FADE = function () {
QUEUE.Push(
["Text",math,formula], // [ method, object, args... ]
SHOWBOX_FADE
);
}
// Get the element jax when MathJax has produced it.
QUEUE.Push(
function () {
math = MathJax.Hub.getAllJax("box")[0]; // returns a MathJax.ElementJax
math.Text(formula);
SHOWBOX();
}
);
setTimeout(function(){
SHOWBOX();
}, 2000);
window.changeIt = function() {
formula = "1 + 2 { \\left( y + 4 \\right) } ^ 3 ";
REFRESH_FADE();
}
</script>
</head>
<body>
<div id="box" style="visibility:hidden; font-size: 500%; height:1px;">
\( \)
</div>
<div class="results">
<div id="visibleBox" style="font-size: 500%;">
Loading ...
</div>
<div id="fadeBox" style="font-size: 500%; display:none;">
</div>
</div>
<button onclick='changeIt()'/>click me</button>
</body>
</html>
The problem is:
The second formula has different height than the first one, due to the parenthesis. For this reason, the common part "1 + " of the second one is printed slightly down respect to its print in the first formula.
That produces an effect of borrow during the transition. I want that the "1 + " part, common to both formulas, doesn't moves when changing from first to second.
Any hint?
maybe this will work :
var SHOWBOX = function () {
var a = $('#box').html();
var dstDiv = $('#visibleBox');
// apply margin for the first equation
dstDiv.css({"margin-top":"2px"});
dstDiv.html(a);
}
var SHOWBOX_FADE = function () {
var a = $('#box').html();
var dstDiv = $('#visibleBox');
var fadeDiv = $('#fadeBox');
fadeDiv.html(a).fadeOut();
fadeDiv.fadeIn(500,function() {
dstDiv.html(a);
// remove the margin for the second equation
dstDiv.css({"margin-top":"0"});
fadeDiv.hide();
});
}
just applying a margin to counter the slight variance in top margin
Made little change in fadeIn and fadeOut
fadeDiv.html(a);
dstDiv.fadeOut(1000,function() {
dstDiv.html(a);
fadeDiv.fadeIn(1000);
});
}
Forked fiddle
Please comment if transition is not up to expection
EDIT
Updated With requirement
Fiddle link
window.onload=function(){
var QUEUE = MathJax.Hub.queue; // shorthand for the queue
var math = null; // the jax element
var mathdef = null; // the jax element
var box = null; // the box math is in
var defaultformula = "1+";
var formula = "2x^2";
var SHOWBOX = function () {
var a = $('#box').html();
var def = $('#defbox').html();
var fixedDiv = $('#fixed');
fixedDiv.html(def);
var dstDiv = $('#visibleBox');
dstDiv.html(a);
}
var SHOWBOX_FADE = function () {
var a = $('#box').html();
var dstDiv = $('#visibleBox');
var fadeDiv = $('#fadeBox');
fadeDiv.html(a);
dstDiv.fadeOut(1000,function() {
dstDiv.html(a);
fadeDiv.fadeIn(1000);
});
}
var REFRESH = function () {
QUEUE.Push(
["Text",math,formula], // == math.Text(formula), [ method, object, args... ]
SHOWBOX
);
QUEUE.Push(
["Text",mathdef,defaultformula], // == math.Text(formula), [ method, object, args... ]
SHOWBOX
);
}
var REFRESH_FADE = function () {
QUEUE.Push(
["Text",math,formula], // [ method, object, args... ]
SHOWBOX_FADE
);
}
// Get the element jax when MathJax has produced it.
QUEUE.Push(
function () {
math = MathJax.Hub.getAllJax("box")[0]; // returns a MathJax.ElementJax
mathdef = MathJax.Hub.getAllJax("defbox")[0]; // returns a MathJax.ElementJax
mathdef.Text(defaultformula);
math.Text(formula);
SHOWBOX();
}
);
setTimeout(function(){
SHOWBOX();
}, 2000);
window.changeIt = function() {
formula = "2 { \\left( y + 4 \\right) } ^ 2";
REFRESH_FADE();
}
}//]]>
.results {
display: flex;
height: 4cm;
position: relative;
}
#fadeBox,
#visibleBox {
width: 100%;
height: 100%;
position: absolute;
top: 0%;
left: 0;
}
<!DOCTYPE html>
<html>
<body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
TeX: {
Macros: {
mgtMult: "",
mgtSelect: [ "\\bbox[10px,border:1px solid red]{#1}", 1],
},
Macros: {
mgtMult: "",
mgtSelect: [ "\\bdefbox[10px,border:1px solid red]{#1}", 1],
}
}
});
</script>
<script type="text/javascript"
src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script>
<div id="defbox" style="visibility:hidden; font-size: 500%; height:1px;padding-top:10px">
\( \)
</div>
<div id="box" style="visibility:hidden; font-size: 500%; height:1px;padding-left:200px">
\( \)
</div>
<div class="results">
<div id="fixed" style="font-size: 500%;margin-top:50x">
Loading ...
</div>
<div id="visibleBox" style="font-size: 500%;padding-left:100px">
</div>
<div id="fadeBox" style="font-size: 500%; display:none;padding-left:100px">
</div>
</div>
<button onclick='changeIt()'>click me</button>
</body>
</html>
In This theme object i have created 2 properties.
I placed this.changeThemeTo(1); under the Event Listener, after that it worked. But i want to place it within if tag
But seems giving an error when put it within if
Uncaught TypeError: this.changeThemeTo is not a function
please help to fix this. Thanks..
var theme = {
changeThemeTo: function (theme_value) {
sessionStorage.removeItem('THEME'); // remove old theme from session storage
if (theme_value == 0) {
sessionStorage.setItem("THEME", 'dark');
} else if (theme_value == 1) {
sessionStorage.setItem("THEME", 'light');
}
document.body.className = sessionStorage.getItem("THEME");
},
init: function () {
document.body.classList.add("fade");
setTimeout(function () {
document.body.classList.remove("fade");
}, 100);
var themes = ['dark', 'light'];
themes.forEach(function (item) {
var button = document.querySelector("." + item);
if (button) {
button.addEventListener("click", function () {
if (item == "dark") {
this.changeThemeTo(0);
} else if (item == "light") {
this.changeThemeTo(1);
}
});
}
}, this);
}
}
window.onload = function () {
theme.init();
}
Here my html code
<!DOCTYPE html>
<html>
<head>
<title></title>
<style type="text/css">
.dark {
background-color: #191919;
color: #EEEEEE;
}
.light {
background-color: #EEEEEE;
color: #191919;
}
</style>
</head>
<body>
<div id="change-theme">
DARK
LIGHT
</div>
</body>
</html>
use self.changeThemeTo instead this.changeThemeTo. and define self = this as per the below code sample. also optimized some portion of the code.
var theme = {
changeThemeTo: function (theme_value) {
sessionStorage.setItem("THEME", theme_value);
document.body.className = theme_value;
},
init: function () {
document.body.classList.add("fade");
setTimeout(function () {
document.body.classList.remove("fade");
}, 100);
var themes = ['dark', 'light'];
var self = this;
themes.forEach(function (item) {
var button = document.querySelector("." + item);
if (button) {
button.addEventListener("click", function () {
self.changeThemeTo(item);
});
}
}, this);
}
}
window.onload = function () {
theme.init();
}
<style type="text/css">
.dark {
background-color: #191919;
color: #EEEEEE;
}
.light {
background-color: #EEEEEE;
color: #191919;
}
</style>
<div id="change-theme">
DARK
LIGHT
</div>