I have created a JavaScript modal that opens on click it's isolated in one file and I want to reuse it on other pages, it works fine on the index.html page, but when I want to use it on another page it gives me Cannot read property 'addEventListener' of null I tried wrapping my js modal code in a window.onload = function() {} because I believed that the DOM had not been loaded fully but again it didn't work, how can I make it work on every page?
here is the index.html content:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" type="text/css" href="./css/main.css">
<link rel="stylesheet" type="text/css" href="./css/modal.css">
<title>Word Spelling Game</title>
</head>
<body>
<div class="menu">
<audio controls autoplay loop>
<source src="./sounds/menu-song.mp3" type="audio/mp3">
Your browser does not support the audio element.
</audio>
<a href="./pages/game-menu.html">
<img src="./images/choose-game-sign.png" class="board">
</a>
<div>
<img src="./images/help-sign.png" class="board trigger">
<div class="modal">
<div class="modal-content">
<span class="close-button">×</span>
<h1>Welcome!</h1>
<article>
This is a collection of interactive games designed for children, each game
aims to further develop the childs skill set in a variety of tasks that involve
spelling, writing and simple math. It is designed in a fun way so that your kid will never become bored!
There are different levels of difficulty for different ages. To select and play
a level click on the Choose level tab above this one. You can see each game's rules
by clicking on the rules tab that is located on the right corner on each level. The instructions are written in
a way that every kid can understand in case that he get's stuck at some point.
</article>
</div>
</div>
</div>
</div>
<script src="./scripts/help-modal.js"></script>
</body>
</html>
The other page on which I want to use it that the user reaches after he clicks the Choose Game link:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="../css//game-menu.css">
<link rel="stylesheet" type="text/css" href="../css/modal.css">
<script src="../scripts/help-modal.js"></script>
<title>Levels</title>
</head>
<body>
<a href="../index.html">
<img src="../images/back-sign.png">
</a>
<div class="help">
<p class="question-mark">?</p>
<div class="modal">
<div class="modal-content">
<span class="close-button">×</span>
<h1>Welcome!</h1>
<article>
This is a collection of interactive games designed for children, each game
aims to further develop the childs skill set in a variety of tasks that involve
spelling, writing and simple math. It is designed in a fun way so that your kid will never become bored!
There are different levels of difficulty for different ages. To select and play
a level click on the Choose level tab above this one. You can see each game's rules
by clicking on the rules tab that is located on the right corner on each level. The instructions are written in
a way that every kid can understand in case that he get's stuck at some point.
</article>
</div>
</div>
</div>
</body>
</html>
and my help-modal.js file:
var modal = document.querySelector(".modal");
var trigger = document.querySelector(".trigger");
var closeButton = document.querySelector(".close-button");
function toggleModal() {
modal.classList.toggle("show-modal");
}
function windowOnClick(event) {
if (event.target === modal) {
toggleModal();
}
}
trigger.addEventListener("click", toggleModal);
closeButton.addEventListener("click", toggleModal);
window.addEventListener("click", windowOnClick);
On your second HTML file you seem to not have any element with trigger class. This is the reason for your error in Console.
Including your JavaScript files in the end of body(just how you did in your intex.html) is also a good practice, in order to make sure that JavaScript will run after all HTML elements were loaded.
1) On the "other" page, move the <script> tag to the bottom of the markup, like it is in the "index" page, or add a window.onload = function() {} wrapper around the code in the .js file.
The reason for this is that currently in the "other" page, the script is loading first. When it loads, it is immediately executed by the browser. So it immediately runs var modal = document.querySelector(".modal");. However, since the script was loaded before any of the HTML in the <body>, there is no element available which matches the selector .modal. So nothing is selected, and consequently the event listeners are not attached to any element, and so are never triggered.
2) Added to that, your "other" page doesn't contain any element with the class "trigger". So even once you fix the loading issue, the line var trigger = document.querySelector(".trigger"); will still not select anything, and you'll still get a similar error when it tries to attach an event handler to nothing. So you need to correct that too. (This will be why your earlier attempt at window.onload = function() {} failed).
In summary - the HTML elements you're targeting must exist before the JavaScript you're using to target them is executed.
Related
I am using js-cloudimage-360-view.min.js to get a 360 view of images. I am getting the images perfectly, but I am unable to change the images by clicking a button.
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class = 'test' style="width: 400px; margin: 100px auto;">
<div
class="cloudimage-360"
data-folder="images/"
data-filename="{index}.jpg"
data-amount="22"
>
</div>
<div>
<button>Click</button>
</div>
<script src="main.js"></script>
<script src="js-cloudimage-360-view.min.js"></script>
</body>
</html>
main.js
const img_box = document.querySelector('.cloudimage-360')
const btn = document.querySelector('button')
btn.addEventListener('click',()=>{
img_box.dataset.folder = 'temp_images/'
img_box.dataset.filename = '{index}.png'
console.log(img_box.dataset)
})
console.log(img_box.dataset)
Here is the folder structure
images
temp_images
index.html
js-cloudimage-360-view.min.js
main.js
Now in order for the 360 view to work, I have to specify the data-folder, data-filename and data-amount correctly inside the div tag and it is working fine. But I want to change the data-folder = 'temp_images/' and data-filename = '{index}.png' when the button is clicked to show a different set of images. I can see all the changes have been done correctly in the console, but the image remains the same. How can I get the images from different folders after clicking the button?
Here is the Github link
https://github.com/scaleflex/js-cloudimage-360-view
Here is a youtube tutorial
https://www.youtube.com/watch?v=uEl4Q6dG7BA&t=111s
I suspect you'll need to destroy the component, change the attributes, and then init it again.
window.CI360.destroy();
img_box.dataset.folder = 'temp_images/';
img_box.dataset.filename = '{index}.png';
window.CI360.init();
If it still doesn't work, then it would probably be best to ask the author(s) directly.
I found a solution like MV Agusta did on his site. Call the 360 views of two different colors to the page at the same time and when the button is clicked, set the opacity of one (which you want to show) to 1 while the other (which you want to hide) to 0.
So i managed to put together a script for my when i click on my image, it shows a popup and when i swipe left or right on the image, it shows the hidden images in my javascript. However when i got to my index page and i go to the house html. When i click on the photo, the swipe functions do not work before i have to go into my javascript file and basically rewrite the swipe function before it works again, but then break after i go back to my index page.
here is the index page to my site: http://titan.dcs.bbk.ac.uk/~aaldib01/mad/madfma/index.html
Then Houses > 2 Bedroom terraced > house1.html
Is there a way for me to either fix the problem or to improve my javascript for this to not be a problem again?
Thank you.
*note the problem i think lies where the image code is placed. (i have deleted the majority of the other code as that does not affect it)
i've tried using the .bind("swiperight", function() but it gives me the same result. It working once then not work after i go to index.html > > house1.html
Here's the house1.html code (the data-role="content":
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=yes">
<link rel="stylesheet" href="css/themes/blue.min.css" />
<link rel="stylesheet" href="css/themes/jquery.mobile.icons.min.css" />
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile.structure-1.4.5.min.css" />
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
<script src="images.js"></script>
<title>House 1</title>
</head>
<body>
<div data-role="page" id="house1" data-theme="a" data-dom-cache="true">
<div data-role="content">
<a href="#popupImg" data-rel="popup" data-position-to="window" data-transition="pop">
<img src="pictures/houses/house1/image1.PNG"/ style="width: 50%;"/>
<div data-role="popup" id="popupImg" data-theme="a" class="ui-corner-all">
Close
<img src="pictures/houses/house1/image1.PNG" style="width: 100%;" />
</a>
</div>
<div data-role="footer" data-position="fixed">
<div data-role="navbar" data-id="footernav">
<ul>
<li>About</li>
<li>Favourites</li>
<li>Contact</li>
</ul>
</div>
</div>
</div>
</body>
</html>
And here's the javascript file:
$(document).ready(function () {
var i = 0;
var imgURL = [];
imgURL.push('pictures/houses/house1/image1.PNG');
imgURL.push('pictures/houses/house1/image2.PNG');
imgURL.push('pictures/houses/house1/image3.PNG');
imgURL.push('pictures/houses/house1/image4.PNG');
$("#house1").on("swiperight",function () {
if (i < (imgURL.length - 1)) {
i++
} else {
i = 0;
}
var imgStr = "<img src=" + imgURL[i] + " style='width:100%'>";
$('#popupImg').html(imgStr);
});
$("#house1").on("swipeleft",function () {
if (i > 0) {
i--
} else {
i = (imgURL.length - 1);
}
var imgStr = "<img src=" + imgURL[i] + " style='width:100%'>";
$('#popupImg').html(imgStr);
});
});
The issue is that your image.js script isn't loaded if you start on the index page and navigate to House 1. You can check this by viewing the source of your site when you load House 1 directly (it'll be there) versus starting on index and browsing to House 1 (it'll be missing from <head>).
The reason for this is that jQuery Mobile uses AJAX for navigating between pages. By default, each page request will only update the <body> element, but not the <head> (more info can be found on this jQuery Mobile demo page).
There are a few solutions referenced on that page:
The simplest approach when building a jQuery Mobile site is to reference the same set of stylesheets and scripts in the head of every page. If you need to load in specific scripts or styles for a particular page, we recommend binding logic to the pageinit event (details below) to run necessary code when a specific page is created (which can be determined by its id attribute, or a number of other ways).
Another approach for page-specific scripting would be to include scripts at the end of the body element when no data-role=page element is defined, or inside the first data-role=page element.
Ultimately it will be up to you to decide which of these methods makes the most sense for your specific use-case.
I'm trying to append the contents of a variable to a div. The variable to append is selected based on the name the value of the <a> that is clicked (e.g. <a value="shops">).
In other words, if someone clicks a link and it has the value shops this function should append the contents of the variable shopsGrid to the div .places.
Please keep in mind that I am a very junior developer. Thanks!
var sweetsGrid = $('.places>div.sweets');
var shopsGrid = $('.places>div.shops');
// second variable shopsGrid above just to
// clarify there are lots of these variables written
// out to select from
$(".filters>.btn.labelz").click(function(){
var selection = $(this).attr( "value" );
$(''+selection+'Grid').appendTo('.places');
// the line above is the one that doesn't work :(
});
var grid = $('.places>div');
var sweetsGrid = $('.places>div.sweets');
var shopGrid = $('.places>div.drink');
var eatGrid = $('.places>div.eat');
$(".filters>.btn.labelz").click(function(){
var selection = $(this).attr( "data-value" );
if($('.label-all').hasClass('active')){
$('.label-all').removeClass('active').addClass('inactive');
$('.filters>.btn.labelz').removeClass('active').addClass('inactive');
$(this).addClass('active').removeClass('inactive');
$('.places>div:not(.'+selection+')').detach();
} else {
if($(this).hasClass('active')){
$(this).removeClass('active').addClass('inactive');
$('.places>div.'+selection+'').detach();
} else {
$(this).addClass('active').removeClass('inactive');
// THIS IS THE ISSUE!
$(selection+'Grid').appendTo('.places');
}
}
return false;
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<meta name="description" content="GUIDE">
<meta name="author" content="">
<title>NYC GUIDE</title>
<!-- Bootstrap core CSS -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<link href="css/custom.css" rel="stylesheet">
</head>
<body>
<div class="row" id="navbar">
<div class="col-xs-12 col-sm-4">
<div class="btn-group btn-group-justified filters">
Eat
Chill
Drink
Fashion
</div>
</div>
</div>
<div class="container" id="places-wrapper">
<div class="row places all-places">
<div class="col-sm-4 place drink eat">
<h5 class="tags"><span class="label label-drink">Drink</span><span class="label label-eat">Eat</span></h5>
<h3>Place 1</h3>
</div>
<div class="col-sm-4 place fashion">
<h5 class="tags"><span class="label label-fashion">Fashion</span></h5>
<h3>Place 2</h3>
</div>
<div class="col-sm-4 place fashion">
<h5 class="tags"><span class="label label-chilll">chill</span></h5>
<h3>Place 3</h3>
</div>
</div>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<script src="js/app.js"></script>
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyA6eMmlCy8MX-p6Imo0scPRWj6D0MCl1y0&&callback=initMap">
</script>
</body>
</html>
There is no such attribute value for anchors. As a workaround, (pointed by #T.J. Crowder) you can make use of HTML5 data-* attributes to store arbitrary data to any DOM element.
HTML
<a data-value="the-link-value"></a>
JS
$(".filters>.btn.labelz").click(function(){
var selection = $(this).data( "value" );
$(selection+'Grid').appendTo('.places');
});
You can access the arbitrary set data using jQuery's .data() method as well as the common .attr() method for attributes.
Note: Although you can use .data() here to serve your purpose of getting the arbitrary set data. But do remember that if .data() is used to store data in key-value pairs (e.g. $(selector).data('key', 'value')) to the DOM, then only .data() itself can be used to fetch it back (i.e. $(selector).data('key')). Using .attr() in this case is pointless and will only work on embedded HTML5 data-* attributes.
Update
I tried to emulate your code in JSfiddle and noticed that you are using a string concatination to refer a Javascript variable. Assuming the selection variable has the valuefashion in it, then selection+'Grid' will always evaluate to the string fashionGrid and NOT the variable fashionGrid.
I have two suggestions as workarounds for your problem:
Use Javascript's eval() method to enforce treating the string selection + 'Grid' to refer the actual variable.
$(eval(selection+'Grid')).appendTo('.places');
eval() is also generally slower than the alternatives, since it has to invoke the JS interpreter, while many other constructs are optimized by modern JS engines. There are safer (and faster!) alternatives to eval() for common use-cases.
The above quoted from MDN is one of many reasons which discourage use of eval(). As a better approach, avoiding eval, you could make use of Javascript objects instead:
var grid = {};
grid['fashion'] = $('.fashion'); // add another object in `fashion` key to the grid
grid['eat'] = $('.eat'); // add another object in `eat` key to the grid
Then, access the particular selector value in an object key as such:
$(grid[selection]).appendTo('.places');
A brief JSfiddle snippet to help you better understand the two approaches.
change this line:
$(''+selection+'Grid').appendTo('.places');
to
$(selection+'Grid').appendTo('.places');
This is potentially a somewhat oddly specific question.
The situation:
I have a page with a position:fixed modal dialog (actually several, but that's irrelevant to the problem, as far as I've been able to determine). The modal opens when clicking a certain link. A script listens for hashchange events in order to close the modal when the user hits the back button.
The expected behaviour is that when back:ing out of the dialog, the page returns to the scroll position where it was before opening the modal. This happens in nearly every modern browser I've tested (desktop FF, Chrome, IE9/10/11 and Safari, and Chrome for Android).
On iPhone/mobile Safari, the page instead scrolls back to the top.
Test code
This is as reduced as I've been able to make it.
<!DOCTYPE html>
<html lang="en">
<head>
<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">
<title>Modal test</title>
</head>
<body>
<h1>Header</h1>
<p>Enough<br>content<br>to<br>cause<br>some<br>scrolling</p>
<br><br><br><br><br><br><br>
<br><br><br><br><br><br><br>
<br><br><br><br><br><br><br>
<h1>Header</h1>
<p>Open popup</p>
<p>Enough<br>content<br>to<br>cause<br>some<br>scrolling<br>
<br><br><br><br><br><br>
<br><br><br><br><br><br>
<br><br><br><br><br><br>
<div id="#popup" style="background:#fff; width:300px; height:100px; border:2px solid #000; position:fixed; top:50px; left:50px; display:none">
Modal dialog.<br>
Hitting 'back' should close this modal.<br><br>
Close modal
</div>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script type="text/javascript">
(function($){
$(document).ready(function(){
$('.js-close-popup').click(function(ev){
window.history.back();
return false;
});
$('.js-open-popup').click(function(ev){
$('#\\#popup').fadeIn(400);
});
});
$(window).on('hashchange', function(){
hash = window.location.hash.replace('#','');
if(hash == '' || hash.lastIndexOf('#', 0) !== 0){
$('#\\#popup').fadeOut(400);
}
});
})(jQuery);
</script>
</body>
</html>
What I want to do is kill the scroll-to-top on iPhones, if at all possible without changing too much of the back-button logic for the popups (or breaking something in any other browsers).
I've searched SO for X number of hours but haven't been able to find a mention of this particular problem, or indeed a solution that works. Feel free to slap me on the fingers and point me in the right direction if I've missed an existing thread that answers my question.
EDIT: To clarify, opening the popup works as expected and does not cause any "auto-scroll".
This is definitely related to having "#" for "a href" value in your code. Also, I see a "##" in your code for a name of the ID, which i believe the reason for this. Try using some other name convention. When you are writing ID's, you don't need to give "#" in the HTML code. Try working on these lines, it might work for you.
Thanks!!
Seems my question is too difficult or I am unable to explain my issue properly!!
I am using barelyfitz tabifier.
My html is:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Simple Tabber Example</title>
<script type="text/javascript" src="tabber.js"></script>
<link rel="stylesheet" href="example.css" TYPE="text/css" MEDIA="screen">
<link rel="stylesheet" href="example-print.css" TYPE="text/css" MEDIA="print">
<script type="text/javascript">
/* Optional: Temporarily hide the "tabber" class so it does not "flash"
on the page as plain HTML. After tabber runs, the class is changed
to "tabberlive" and it will appear. */
document.write('<style type="text/css">.tabber{display:none;}<\/style>');
function loadDetails()
{
alert("here");
document.getElementById('myTab').tabber.tabShow(1);
alert("not here");
}
</script>
</head>
<body>
<h1>Tabber Example</h1>
<div class="tabber" id="myTab">
<div class="tabbertab">
<h2>Tab 1</h2>
<A href="#" onclick="loadDetails()";>Banana</A>
</div>
<div class="tabbertabhide">
<h2>Tab 4</h2>
<p>Tab 4 content.</p>
</div>
</div>
</body>
</html>
As clear, tab 4 is initially hidden as its class is tabbertabhide.
And tab 1 is having a text banana with onclick reference to loadDetails method.
What I want to do is, on clicking banana, I want tab 4 to become visible.
However, document.getElementById line in loadDetails method does not have any effect.
Can any one please help me with this specific technical issue!!
Below is the same issue I asked before in a generalized manner!!
Issue:
I have a webapplication with a search form on the index page which searches for fruits.
Based on the search criteria entered, the result will have a list of fruits. Each member of this will have a call back link to a javascript function. Something like:
<html>
<head>
<script type="text/javascript">
//Function to load further details on fruits
function loadDetails(){
//this will do a call back to server and will fetch details in a transfer object
}
</script>
</head>
<body>
<form method="post">
<A href="#" onclick="loadDetails('banana')";>Banana</A>
<A href="#" onclick="loadDetails('apple')";>Apple</A>
</form>
</body>
</html>
Now my issue is, I want to show the details on a tab which gets generated in a loadDetails function.
Something in the lines of www.barelyfitz.com/projects/tabber/
But dynamic tab generation on the onclick event in the content of first tab.
In other words, first tab will have the clickable list of fruits and on clicking a fruit, a new tab will get opened with more details on that fruit fetched from database.
Is it possible using simple javascript ??
Also, is it possible to do this in jquery without AJAX. I can not use ajax.
I am extremely extremely new to javascript. So I dont know how well am able to describe my question. But have tried my best.
Hope to get some help!!
Can you post this on a fiddle?
Also try the jQuery way of doing it which would be:
function loadDetails()
{
$('.tabbertabhide').show(); //make it appear without any animation OR
$('.tabbertabhide').fadeIn(); //make it to fade in.
}
The above code uses a class selector- in this case your selecting the items with class "tabbertabhide" and making them appear. Similarly you could also use an ID selector if you wanted.