Style sheet toggle doesn't work on both buttons - javascript

I'm trying to get my dark mode toggle button to function properly. It can work on EITHER mobile or desktop but not BOTH. I tried everything and search countless of information and can't find the answer. I'm not a developer and this is my first time with javascript. Any help or information would be helpful and appreciated. I want 2 buttons to toggle the single function of changing style sheets. I have 2 different navs, one for mobile and one for desktop (I don't know why but this is what I was taught in my web design class.)
I guess this is where I put my code. Sorry if I don't include all the information but I'll include what I think is relevant.
HTML:
<div class="col-3 col-3-tb d-none">
<div id="myDIV" class="btn-click" onclick=" myFunctiontext();">Dark<br>Mode<br>Enable</div>
</div>
<div class="switch">
<div id="myDIVmb" class="btn-push" onclick="myFunctiontextmb();">Dark Mode<br>Enable</div>
</div>
the classes are just margins to change up positions on the layout or the font.
Javascript:
// Select the button
const btn = document.querySelector(".btn-push, .btn-click");
// Select the stylesheet <link>
const theme = document.querySelector("#theme-link");
// Listen for a click on the button
btn.addEventListener("click", function() {
// If the current URL contains "ligh-theme.css"
if (theme.getAttribute("href") == "style-light.css") {
// ... then switch it to "dark-theme.css"
theme.href = "style-dark.css";
// Otherwise...
} else {
// ... switch it to "light-theme.css"
theme.href = "style-light.css";
}
});
Like I said I'm not a developer. I found the code from this website and tried it out.

Related

Accordion closing onClick anywhere in container

I am trying to create an accordion for a project I'm working on. For the sake of argument, lets assume this accordion only has one container. So far, I've got it all working, however when I expand the accordion, which will only expend if I click on the header, I am then able to close the open container by clicking anywhere in the container, rather than just the header.
I've tried a few different things resulting in null .toggle errors so far. Below is a snippet of the accordion itself, along with the JS I'm using with it. Please bare in mind the accordion functions correctly and displays correctly, its the click function that's the issue (meaning my CSS is fine so no point sharing):
JS:
const accordion = document.getElementsByClassName('container1');
for (i=0; i<accordion.length; i++) {
accordion[i].addEventListener('click', function () {
this.classList.toggle('active');
});
}
Accordion HTML:
<div class="accordion-body">
<div class="accordion">
<hr>
<div class="container1" id="container1">
<div class="label" id="linkId">Generate a payment link</div>
<div class="content">
<div class="form">
</div>
</div>
</div>
<hr>
</div>
</div>
Essentially, I only even want the accordion to open/close if the 'label' div is pressed. This is because within my accordion there are buttons, so at the moment when a button is pressed when accordion is open, it changes the class which closes the accordion back up. Right now I understand that the clickable area is 'container1' but I've not been able to make it work against the label div.
I can also provide a link to the webpage where it is happening, happy to provide this via PM.
I tried changing the JS to the below:
const accordion = document.getElementsByClassName('container1');
const linkId = document.getElementById('linkId');
for (i=0; i<accordion.length; i++) {
document.getElementById('linkId').addEventListener('click', function () {
accordion.classList.toggle('active');
});
}
However the above produces the following error:
Uncaught TypeError: Cannot read properties of undefined (reading 'toggle')
at HTMLDivElement. (txninfo.php:825:25)
LINE 825 is the following:
accordion.classList.toggle('active');
Any help would be massively appreciated
accordion in that case is instance of HTML-Collection and not Element as you assume.
So you correctly iterate over the collection of elements, you now just want to select the current element (where key == i) of iteration instead of the whole collection.
accordion[i].classList.toggle('active');
then you should have an Element and should be able to access it´s properties like classList
https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName

How to change add and remove tags with JS in django template

I have a quiz Django app which consists of two parts. One is showing 10 sentences with their audio to remember, one per page, and the second is asking questions for the same set of sentences. First part was set up with js function which creates pagination in my html the following way:
my_template.html
<button id="prev">prev</button>
<button id="next">next</button>
<ul class="list-articles" id="dd">
</ul>
<script>
var my_objects = `{{ my_objects|safe}}:` #list of items from my view function
function paginate(action) {
console.log(action)
if (action ==='next') {
page_number++;
}
else{
page_number--;
}
const audio = document.createElement('audio');
audio.src =`/media/${my_objects[page_number].fields.audio}`;
$('#dd').empty();
$('#dd').append('<li><h1>'+ my_objects[page_number].fields['content'] +'</h1></li>');
$('#dd').append(audio);
$('#page_number').val(page_number);
}
document.addEventListener('DOMContentLoaded', function() {
document.querySelector('#next').onclick = function() {
paginate('next'); #same goes for 'prev' button.
}
})
</script>
Now when the user paginated through all the sentences I want to show the continue button and if the user clicks it, start the second part. The second part has absolutely same page except I need to hide content of my objects and leave only audio or vice versa and add textarea tag for the user's input. After that I need to loop over my objects again - now in the form of questions. I need to do that without page re-rendering so I don't need to store the list of objects or query the DB again.
I tried to make it with tag disabling and activating the second loop after the first ends but that looks quite messy and I suppose there is a smarter way of doing that. I'm not a JS developer so any help would be appreciated!
Thanks for all who viewed and not commented! I found the answer myself. Just need to add JS functions which will empty the main tag and refill with necessary field.
Details: By accessing elements of DOM in the following way you can empty any element on a web page. For example, if we have a div like:
<div class= id="maindiv">
<h2> Hello, world! </h2>
</div>
We can empty and refill it with a new header:
var container = document.getElementById("maindiv");
container.empty();
new_header = document.createElement('h2');
new_header.innerHTML = 'Hello, brave new world!'
container.append(new_header);
Same applies to the whole web-page. The only thing is, if it is too big, you probably going to be tired of manipulating the DOM each time. So, you may want to check out some frameworks like React to make it easier .

display icon on mouse hover in Accordion via JavaScript (EventHandler)

EDIT UPDATE: Deleted original question and revising to make easier to read.
OK,so I figured it out! Or kinda! Here's the JS Fiddle:
https://jsfiddle.net/reflexez/d7yvym8q/2/
var test = document.getElementById("what");
var accordionList = document.getElementsByClassName("accordion");
var i;
var j;
for(i=0; i < accordionList.length; i++) {
accordionList[i].addEventListener("mouseenter", function() {
//document.getElementById("accordion-icon").style.display = "block";
var addIcon = this.firstChild.nextElementSibling;
addIcon.style.display = "block"
console.log(addIcon);
});
}
for(j=0; j < accordionList.length; j++) {
accordionList[j].addEventListener("mouseleave", function() {
//document.getElementById("accordion-icon").style.display = "none";
var removeIcon = this.firstChild.nextElementSibling;
removeIcon.style.display = "none"
console.log(removeIcon);
});
}
my site:
http://alpizano.com
I'm a Comp. Engineering student and have taken up to Data Structures in Java but I never formally learned JavaScript (teaching myself now) so that's why something as trivial as this was giving me issues.
I STILL have a question though for the future if you guys could help me out. Is the ONLY way to get information out of the "current" item being accessed by the for loop, that is, the arrayList[i] item,instead of firstChild, parentNode, nextElementSibling etc...?
These functions seem really sloppy to me coming from Java. I know I've done LinkedLists and similiar stuff with nodes and LinkedList.getLink stuff to the the contents of the next item in the list, but if the HTML is really sloppy, like in my case, I had multiple DIVs, and the tag immediately after the DIV, then a tag, then another DIV, it seems so convulted.
I figured there was a way to just do like accordionList[i].getElementById("the ID of the tag you want goes here, like in my case the tag because I want to change it from display: none to block");
obviously it didn't work that way and I had to use firstChild.nextElementSibling but I am still wondering. And yes, I am currently reading the JS script book written by Duckett which I don't find that good actually.
Honestly, I see why pro JS programmers debug in chrome and console.log their outputs, because, the only reason I figured it out was that I was typing random code and was console.log miscellanous variables to and checking them in chrome, and I did .firstChild and it came up NULL, but then in chrome, it said the .nextElementSibling to that was the tag I needed to manipulate.
Either way, I hope this helps other people and I hope I provided research & intent on my part to code this that you guys give me a thumbs up :D
First - for the next question, please provide a full "working" code example - your javascript code differ from your posted html (e.g. you are accessing the id accordion-icon which is not available in your html. But with the link to your website i got a good overall view :)
The main problem is - you have more than one element with the id accordion-icon - don't do that, IDs should be unique
--> so I removed the id and add it instead as a additonal class to the icon (see snipped below)
The second thing is you want only the corresponding icon to be visible. In your event listener callback function you can access event.target which points to the element triggering the event listener (e.g. the current div.accordion). From this element you search for the next icon and only show/hide this one. (see snipped below)
And the last thing - you need to hide the icons at the beginning - do it via js (like in my example) or directly in the html on each icon
var test = document.getElementById("what");
var accordionList = document.getElementsByClassName("accordion");
var i;
var j;
//hide all icons first (or instead hide it directly via style in html)
var iconsList = document.getElementsByClassName("accordion-icon");
for(i=0; i < iconsList.length; i++){
iconsList[i].style.display = "none";
}
for(i=0; i < accordionList.length; i++) {
accordionList[i].addEventListener("mouseenter", function(event) {
//only set style for the next icon (inside current accordion)
event.target.getElementsByClassName('accordion-icon')[0].style.display = "block";
});
}
for(j=0; j < accordionList.length; j++) {
accordionList[j].addEventListener("mouseleave", function(event) {
event.target.getElementsByClassName('accordion-icon')[0].style.display = "none";
});
}
.panel {margin-bottom:30px;}
<div class="accordion">
<i class="accordion-icon fas fa-arrow-down">icon1</i>
Title of accordion box goes here, after arrow icon.
</div>
<div class="panel">
Text inside accordion goes here.
</div>
<div class="accordion">
<i class="accordion-icon fas fa-arrow-down">icon2</i>
Title of accordion box goes here, after arrow icon.
</div>
<div class="panel">
Text inside accordion goes here.
</div>
<div class="accordion">
<i class="accordion-icon fas fa-arrow-down">icon3</i>
Title of accordion box goes here, after arrow icon.
</div>
<div class="panel">
Text inside accordion goes here.
</div>

Issue with handling a form using javascript

We have a website hosted at hubspot, we use their native WYSIWYG to design layouts then style them with css and js.
On the homepage http://www.lspatents.com/ it used to have a form under the "Get started here" title, it had around 10 questions, and used javascript to split them to steps so they can fit in the same area on the currently shown blank box.
It was working just fine till two days ago the form disappeared and left it with a blank area as you can see now, and as far as i know no one has touched this code recently.
Here is the js code that was used to manipulate the form
// Hero Form
$(window).load(function() {
// disable autocomplete to fix bug
$('.hero-form form').attr("autocomplete", "off");
$('.hero-form .hs-richtext').each(function() {
$(this).nextUntil('.hs-richtext').wrapAll('<div class="step" />');
});
// Hide Loading icon
$('.hero-form form').css('background', 'none');
$('.hero-form form .step:nth-of-type(2)').show();
// First Step to Second Step
$('.step').find('.hs-richtext').change(function() {
$('.step:nth-of-type(2)').hide().next().next().fadeIn();
});
// Second Step to Third Step
$('.step').find('.hs-input').change(function() {
var names = {};
$(':radio').each(function() {
names[$(this).attr('name')] = true;
});
var count = 0;
$.each(names, function() {
count++;
});
if ($(':radio:checked').length === count) {
$('.step:nth-of-type(4)').hide().next().next().fadeIn();
}
});
});
As far as i was able to tell, the developer used css to hide the whole form area with display:none; and used the js above to split the questions to steps and show a certain number in each step.
You can see the code being called in the footer so there is no problem with the link to the .js file, also if you inspect the element and disable the display:none; that's declared for any of the divs within the hero-form all questions get displayed, so there is no problem with the form either, so why has it stopped working?
Would appreciate any help,
This line will no longer work with your mark-up...
$('.hero-form form .step:nth-of-type(2)').show();
There are a number of additional divs that wrap your mark-up, placed there by react, React has placed a series of div inside your form which are being hidden by your existing CSS (which I assume used to just be a series of STEP's)
The CSS that hides the nodes is :
.hero-form form>div, .hero-form form>.step {
display: none;
}
The nodes that are being hidden with display:none
<div data-reactid=".0.0:$1">
<div class="hs-richtext" data-reactid=".0.0:$1.0">
<hr>
</div>
<div class="step">
<div class="hs_patent field hs-form-field" data-reactid=".0.0:$1.$patent">
<label placeholder="Enter your Do you have a patent?" for="patent-9fc8dd30-a174-43bd-be4a-34bd3a00437e_2496" data-reactid=".0.0:$1.$patent.0">
<span data-reactid=".0.0:$1.$patent.0.0">Do you have a patent?</span>
<span class="hs-form-required" data-reactid=".0.0:$1.$patent.0.1">*</span>
</label>
<div class="hs-field-desc" style="display:none;" data-reactid=".0.0:$1.$patent.1">
</div>
</div>
Your JQuery will add display:block to the DIV with the class 'step' bit wont alter the parent DIV (inserted by React) which still prevents your node from being shown.
You need to alter you JQuery to call show() on the parent() that contains the "step" div you wish to show.
Please check your browser console ans see you have problem loading this form:
https://forms.hubspot.com/embed/v3/form/457238/9fc8dd30-a174-43bd-be4a-34bd3a00437e
and this is the error:
net::ERR_NAME_RESOLUTION_FAILED
It's better you change your DNS to something like 8.8.8.8 and see if the problem still exists or not.

JS - Shouldn't this be written better? One function vs multiples?

I have a tweet stream where new tweets are added at the top and the older ones pushed down. You can click on the entire tweet and a panel slides down to reveal, "reply", "retweet", "favorite" etc. The panel is added to each new tweet added in the stream.
The code below works. Shouldn't this be better written so that only one call is being made? Or, as a new tweet is added. would I just have to add to the code with div#tc4, ul#tb4 etc?
$(document).ready(function () {
$("div#tc1").click(function () {
$("ul#tb1").slideToggle("fast");
});
$("div#tc2").click(function () {
$('ul#tb2').slideToggle("fast");
});
$("div#tc3").click(function () {
$('ul#tb3').slideToggle("fast");
});
});
Added Markup:
<div id="tc1" class="tweetcontainer">
<div class="avatarcontainer">
<div class="avatar"></div>
</div>
<div class="content">
<div class="tweetheader">
<div class="name">
<h1>John Drake</h1>
</div>
<div class="tweethandle">
<h2>#Drakejon</h2>
</div>
<div class="tweettime">10m</div>
</div>
<div>
<p>Exceptional Buys Ranger To Give Monitoring Shot In The Arm To Its 'DevOps' Platform http://tcrn.ch/11m3BrO by #sohear </p>
</div>
</div>
</div>
<!-------------Tool Bar -------------------------------->
<ul id="tb1" class="toolbar">
<li><a class="reply" href="#"><span>reply</span></a></li>
<li><a class="retweet" href="#"><span>retweet</span></a></li>
<li><a class="favorite" href="#"><span>favorite</span></a></li>
<li><a class="track" href="#"><span>track</span></a></li>
<li><a class="details" href="#"><span>details</span></a></li>
</ul>
I highly recommend separating your javascript from your detailed page function. The best way to do this is to put the retweeting panel inside the tweet container, then you don't even need to give it an id at all or encode in the javascript information about your html structure and ids. You can then just do:
$('.tweetcontainer').on('click', function(event) {
if ($(event.target).is(':descendantof(.toolbar)')) {
//ignore all clicks within the toolbar itself
return;
}
$(this).find('.toolbar').slideToggle();
});​
It's that easy! See it in action in a jsFiddle.
Now you can add as many tweet containers as you want to your page--and your javascript doesn't have to change one bit. Other solutions that require knowledge of specific ids linking to specific ids are suboptimal.
Note the descendantof pseudo-selector is custom (see the fiddle to find out how it works). Also, since you didn't provide any css, I had to choose some--it was quick so don't expect much. (Aww heck I just saw you updated your question to provide a jsFiddle with css giving a far prettier result--but I won't change mine now.) I did have to add a class to the actual tweet itself, but there is probably a better way to style it.
And if you want a click on the displayed toolbar itself (outside of a link) to allow collapsing the toolbar, change the code above to :descendantof(a).
If you don't want to change your page layout, another way to it is to encode the information about the linkage between html parts in the html itself using a data attribute. Change your tweetcontainer div to add a data attribute with a jQuery style selector in it that will properly locate the target:
<div class="tweetcontainer" data-target="#tb1">
You don't really have to remove the id if you use it elsewhere, but I wanted you to see that you don't need it any more. Then on document.ready:
$('.tweetcontainer').click(function () {
$($(this).data('target')).slideToggle('fast');
});
Here is another jsFiddle demonstrating this alternate technique (though it less elegant, in my opinion).
Last, I would like to mention that it seems possible you have a little bit of "div-itis". (We have all been there.) The toolbar anchor elements have unnecessary spans inside of them. The tweet name h1 element is inside a div, but could just be an h1 with class="name" instead.
In general, if there is only a single item inside a div and you can change your stylesheet to eliminate the div, then the div isn't needed. There are an awful lot of nested divs in your html, and I encourage you to remove as many of them as you can. Apply style to the other block elements you use and at least some, if not many, won't be needed.
I'd suggest (though currently untested):
$('div[id^="tc"]').click(function(){
var num = parseInt(this.id.replace(/\D+/g,''),10);
$('#tb' + num).slideToggle("fast");
});
Though given that you don't need the num to be a number (it'd be fine as a string), you could safely omit the parseInt().
Yes, you can write this code much more compactly like this:
$(document).ready(function() {
for (var i = 1; i < 3; i++) {
$("div#tc" + i).click(function() { $("ul#tb" + i).slideToggle("fast"); } );
}
});

Categories