Why is my javascript file not re-setting a div's attribute? - javascript

I have a script that is adding and removing a class to a couple divs when a link is clicked on. Each div has a set class that does not need to be removed. However, said class is being removed. How do I stop this from happening?
HTML
<div id="home" class="page pageShowing"></div>
<div id="portfolio" class="page"></div>
JS
let holder = document.getElementById("main");
let pageShowingClass = holder.getElementsByClassName("pageShowing");
let pages = holder.getElementsByClassName("page");
Navigation.Links.forEach(function(value){
let createNavLink = document.createElement("li");
let createNavText = document.createTextNode(value.title);
createNavLink.appendChild(createNavText);
createNavList.appendChild(createNavLink);
createNavLink.addEventListener("click", function(){
let link = createNavLink.innerHTML;
link = link.toLowerCase().replace(" ", "_");
let page = document.getElementById(link);
page.setAttribute("class", "page");
for(let i = 0; i < pageShowingClass.length; i++){
Here, the click handler should only be removing the pageShowing class
if it exists but is also removing the page class
if(pageShowingClass[i].getAttribute("class") == "pageShowing"){
pageShowingClass[i].removeAttribute("class");
}
}
Here, the click handler should be readding the page class when the
link is clicked on.
page.setAttribute("class", "page");
page.setAttribute("class", "pageShowing");
page.style.display = "block";
});
});
I know it's easier to do this in jQuery, but I don't want it to be in jQuery. I also already have it to where it will add and remove the pageShowing class dynamically, so that's not an issue.

As Siguza said in the reply, you're removing the class attribute, which is what you DON'T want to be doing in this case.
Let's put the element in question here for reference:
<div id="home" class="page pageShowing"></div>
class is an attribute of the element div. When you call removeAttribute('class'), it will do as it says:
<div id="home"></div>
If you check the element in chrome's dev tools or whatever you use, you'll be seeing the element as it says above.
You're probably looking for Element.className to modify your classes, so instead of
if(pageShowingClass[i].getAttribute("class") == "pageShowing"){
pageShowingClass[i].removeAttribute("class");
}
you'll want
if(pageShowingClass[i].getAttribute("class") == "pageShowing"){
pageShowingClass[i].className = "page";
}
and if you want to add the pageShowing class again, you'd just say pageShowingClass[i].className = "page pageShowing"

Element.setAttribute() adds a new attribute or changes the value of an existing attribute on the specified element.
Use Element.classList.add(String [, String]), adds specified class values. If these classes already exist in attribute of the element, then they are ignored.
page.classList.add('page', 'pageShowing')

Related

EventListener doesn't trigger by clicking to div's

I create multiple div's dynamically with Javascript
var cart = document.createElement("div");
cart.classList.add("buy-item");
cart.setAttribute("name", "buy-food");
div.appendChild(cart);
and when i collect all the elements with the "buy-item" class i get the elements as an HTMLCollection but when i want to know when it was clicked nothing happens
var elements = document.getElementsByClassName("buy-item");
console.log(elements)
function addFoodToCart() {
console.log("One of buy-item class itemes was clicked")
};
for (var i = 0; i < elements.length; i++) {
elements[i].addEventListener('click', addFoodToCart);
}
The HTML element looks like this
<div class="food-wrap dynamic-food">
<img class="food-item-img" src="/img/foodItem.png">
<p class="food-title">Csípős</p><p class="food-price">190</p>
<div class="buy-item" name="buy-food"></div>
<div class="tooltip">
<span class="tooltiptext">Csípős szósz</span>
</div>
</div>
tl;dr: Add the event listener to the parent div (the one holding all the other elements that you have/will be creating) and use event.target to know which one was clicked.
Not sure if this helps and if this is your case, but you could be benefited by the understanding of Event Bubbling and Event Delegation in Javascript.
In my example below, run the code and click in each paragraph. The console log will show which element you cliked.
function findTheOneClicked(event) {
console.log(event.target)
}
const mainDiv = document.getElementById("parent");
mainDiv.addEventListener('click', findTheOneClicked);
<div id="parent">
<p>"Test 1"</p>
<p>"Test 2"</p>
<p>"Test 3"</p>
</div>
This is really good when you have an element with many other elements on it and you want to do something with them, or when you want to add an event handler to an element that is not available (not created yet) on your page.
Good reading on this topic:
https://javascript.info/bubbling-and-capturing

How to execute event handler only once in Javascript?

I'm creating button which allows to enter post section. I'm checking if the body has class 'logged-in'. If test is false I want to create div container for message " You have to logi in" and append it to my section. My problem: Everytime when I click this button, new node is appended.
- How to invoke handler only once ?
if( !isOnline ) {
e.preventDefault();
var divForLog = document.createElement('div'),
linkElement = document.createElement('a');
linkElement.setAttribute('href', 'http://domain/login');
linkElement.text = "log in"
divForLog.innerHTML = "You have to ";
divForLog.appendChild(linkElement);
document.getElementById('last_questions').appendChild(divForLog);
}
There are several potential solutions, but I'll only list a couple here.
"Global"
Create a variable var loginShown in the scope where the handler is created. Then, change the ! isOnline check to ! isOnline && ! loginShown in the if statement, and set loginShown = true once you've appended the div.
Fiddle the DOM
Depending on the other content of #last_questions you can simply test whether or not the login element has already been appended using:
if ( ! document.getElementById('last_questions').querySelector('div > a[href="http://domain/login"]' ) ) {
...
}
Failing that, you can do as #NewToJS mentioned in the comments and add an attribute to the parent (once the div has been appended) which you can test for, such as an ID or data- attribute.
Unbind the Event
Easier if you're using jQuery, as mentioned by #Pawel you can simply unbind the event once the div has been appended. Probably the cleanest solution, but also trickier to implement. It also depends what else the handler is doing.
Try to set an attribute id to your div (container in my example) and when the user click check if the element with id already exist in document, if not add it :
if( !isOnline && document.getElementById('container').length==0) {
e.preventDefault();
var divForLog = document.createElement('div'),
linkElement = document.createElement('a');
linkElement.setAttribute('href', 'http://domain/login');
linkElement.text = "log in"
divForLog.innerHTML = "You have to ";
divForLog.appendChild(linkElement);
divForLog.setAttribute('id', 'container'); //Add id attribute
document.getElementById('last_questions').appendChild(divForLog);
}
Hope this helps.
If you're using jQuery something you could do(from the documentation .one | jQuery).
$("#button" ).one( "click", function() {
var divForLog = document.createElement('div'),
linkElement = document.createElement('a');
linkElement.setAttribute('href', 'http://domain/login');
linkElement.text = "log in"
divForLog.innerHTML = "You have to ";
divForLog.appendChild(linkElement);
document.getElementById('last_questions').appendChild(divForLog);
});
However another way I could think of would be to use jQuery's
$('#last_questions').html(divForLog);
Update
If thats not an option(most likely, as the #last_questions div may contain other stuff), you can create a <div id="log-in-alert"></div> which will live inside the #last_questions and only replace the html in this
Hope I was able to help??

Remove hidden elements from cloned element

There are some elements that are hidden in this web page. Now if I want to find the hidden elements:
var node = jQuery('body')[0];
$(node).find(":hidden").remove();
This removes the hidden elements from the main node (which further changes the layout of the page). What I want to do is to copy(clone) the elements which are not hidden. For which I am trying this:
var clone = node.cloneNode(true);
$(clone).find(":hidden").remove();
But this removes all the elements inside the clone and not just the hidden elements (as expected, since its not in the dom). What's the best possible way to remove hidden elements from the clone.
I assume the issue is that until your clone is re-inserted into the DOM, then all of it is being considered hidden.
Maybe you could mark the hidden elements for removal first, then clone and then remove the marked elements:
var $node = ... ; // jQuery object of node to be cloned
$node.find(':hidden').addClass('markedForRemoval');
var $clone = $node.clone();
$clone.find('.markedForRemoval').remove();
// tidy up:
$clone.find('.markedForRemoval').removeClass('markedForRemoval');
$node.find('.markedForRemoval').removeClass('markedForRemoval');
demo: http://jsfiddle.net/BYossarian/6ysq8/
Sometimes the :visible selector will not be enough, and you will also want a selector for styles with height:0px, since display:none; and height:0px; are not equivalent.
Before cloning, we need to mark the elements as visible or invisible, because once cloned, the clone is in a variable, but not on the page, so everything inside it will qualify as :hidden. (Bonus: Let's make this as efficient as possible, by not hijacking the class or id fields, and instead using a custom data-attribute.)
Identify truly hidden elements:
$(node).find(':hidden').attr('data-hidden', 'true');
$(node).find('
*[style*="height:0px"],
*[style*="height: 0px"]
').attr('data-hidden', 'true');
Deep clone the node:
var clone = node.clone(true, true);
Remove hidden elements:
clone.find('*[data-hidden="true"]').remove();
I would stick to jQuery clone. My method is kinda rough, but it works.
<div class="bla" >
<span class="hidden">hidden</span>
<span class="hidden">hidden</span>
<span class="hidden">hidden</span>
<span > visible </span>
</div>
So, first, clone the parent.
var a = $('.bla').clone()
Then clone the visible children.
var b = $('.bla > :visible').clone() ;
Then add them to each other.
a.html(b)
The whole thing will be like so:
var a = $('.bla').clone()
var b = $('.bla > :visible').clone() ;
a.html(b)
Here is an example : http://jsfiddle.net/4Dky9/1/
var clone = node.cloneNode(true);
var hiddenElements = clone.querySelectorAll('.hidden'); // if hidden elements are applied the css class hidden
for(var i = 0; i < hiddenElements.length; i++){
clone.removeChild(hiddenElements[i])
}
If there is no hidden class, iterate through all the child elements and check for the display property.
var children = clone.childNodes;
for(var i = 0; i < children.length; i++){
if(children[i].style && children[i].style.display == 'none'){
clone.removeChild(children[i]);
}
}
try using :
$(clone).children(':hidden').remove();

How to Reduce Size of This jQuery Script and Make it More Flexible?

I just created script that shows/hides (toggles) block of HTML. There are four buttons that each can toggle its HTML block. When any HTML block is opened, but user has been clicked on other button than that HTML block's associated button... it hides that HTML block and shows new one.
Here is what I have at the moment:
$('.btn_add_event').click( function() {
$('.block_link, .block_photos, .block_videos').hide();
$('.block_event').toggle();
});
$('.btn_add_link').click( function() {
$('.block_event, .block_photos, .block_videos').hide();
$('.block_link').toggle();
});
$('.btn_add_photos').click( function() {
$('.block_event, .block_link, .block_videos').hide();
$('.block_photos').toggle();
});
$('.btn_add_videos').click( function() {
$('.block_event, .block_link, .block_photos').hide();
$('.block_videos').toggle();
});
Any ideas how to reduce code size? Also, this script isn't very flexible. Imagine to add two new buttons and blocks.
like Sam said, I would use a class that all the blocks share, so you never have to alter that code. Secondly, you can try 'traversing' to the closest block, therefore avoiding it's name. That approach is better than hard coding each specific block, but if the html dom tree changes you will need to refactor. Last, but best, you can pass in the class name desired block as a variable to the function. Below is something you can copy paste that is close to what you started with.
$('.myAddButtonClass').click( function() {
$('.mySharedBlockClass').filter(':visible').hide();
//find a good way to 'traverse' to your desired block, or name it specifically for now.
//$(this).closest(".mySharedBlockClass").show() complete guess
$('.specificBlockClass').show();
});
I kept reading this "When any HTML block is opened, but user has been clicked on other button than that HTML block's associated button" thinking that my eyes were failing me when Its just bad English.
If you want to make it more dynamic, what you can do is add a common class keyword. Then
when the click event is raise. You can have it loop though all the classes that have the
keyword and have it hide them all (except the current one that was clicked) and then show the current one by using the 'this' keyword.
you can refer below link,
http://chandreshmaheshwari.wordpress.com/2011/05/24/show-hide-div-content-using-jquery/
call function showSlidingDiv() onclick event and pass your button class dynamically.
This may be useful.
Thanks.
try this
$('input[type=button]').click( function() {
$('div[class^=block]').hide(); // I resumed html block is div
$(this).toggle();
});
Unfortunatly I couldn't test it, but if I can remember right following should work:
function toogleFunc(clickObject, toogleTarget, hideTarget)
{
$(clickObject).click(function()
{
$(hideTarget).hide();
$(toogleTarget).toggle();
});
}
And the call:
toogleFunc(
".btn_add_videos",
".block_videos",
".block_event, .block_link, .block_photos"
);
and so far
Assuming the buttons will only have one class each, something like this ought to work.
var classNames = [ 'btn_add_event', 'block_link', 'block_photos', 'block_videos' ];
var all = '.' + classNames.join(', .'); // generate a jquery format string for selection
$(all).click( function() {
var j = classNames.length;
while(j--){
if( this.className === classNames[j] ){
var others = classNames.splice(j, 1); // should leave all classes but the one on this button
$('.' + others.join(', .')).hide();
$('.' + classNames[j]).toggle();
}
}
}
All the buttons have the same handler. When the handler fires, it checks the sender for one of the classes in the list. If a class is found, it generates a jquery selection string from the remaining classes and hides them, and toggles the one found. You may have to do some checking to make sure the strings are generating correctly.
It depends by how your HTML is structured.
Supposing you've something like this
<div class="area">
<div class="one"></div>
<div class="two"></div>
<div class="three"></div>
</div>
...
<div class="sender">
<a class="one"></a>
<a class="two"></a>
<a class="three"></a>
</div>
You have a class shared by the sender and the target.
Your js would be like this:
$('.sender > a').click(function() {
var target = $(this).attr('class');
$('.area > .' + target).show().siblings().hide();
});
You show your real target and hide its siblings, which aren't needed.
If you put the class postfixes in an array, you can easily make this code more dynamic. This code assumed that it doesn't matter in which order toggle or hide are called. If it does matter, you can just remember the right classname inside the (inner) loop, and toggle that class after the loop.
The advantage to this approach is that you can extend the array with an exta class without needing to modifying the rest of the code.
var classes = new Array('videos', 'event', 'link', 'photos');
for (var i = 0; i < classes.length; ++i)
{
$('.btn_add_' + classes[i]).click(
function()
{
for (var j = 0; j < classes.length; ++j)
{
if (this.hasClass('btn_add_' + classes[j]))
{
$('.block_' + classes[j]).toggle();
}
else
{
$('.block_' + classes[j]).hide();
}
}
});
}
You could make this code more elegant by not assigning those elements classes like btn_add_event, but give them two classes: btn_add and event, or even resort to giving them id's. My solution is based on your description of your current html.
Here is what I think is a nice flexible and performant function. It assumes you can contain your links and html blocks in a parent, but otherwise it uses closures to precalculate the elements involved, so a click is super-fast.
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js" ></script>
<script type="text/javascript">
// Enables show/hide functionality on click.
// The elements within 'container' matching the selector 'blocks' are hidden
// When elements within 'container' matching the selector 'clicker' are clicked
// their attribute with the name 'clickerAttr' is appended to the selector
// 'subject' to identify a target, usually one of the 'blocks'. All blocks
// except the target are hidden. The target is shown.
//
// Change clickerAttr from 'linkTarget' to 'id' if you want XHTML compliance
//
// container: grouping of related elements for which to enable this functionality
// clicker: selector to element type that when clicked triggers the show/hide functionality
// clickerAttr: name of the DOM attribute that will be used to adapt the 'subject' selector
// blocks: selector to the html blocks that will be shown or hidden when the clicker is clicked
// subject: root of the selector to be used to identify the one html block to be shown
//
function initToggle(container,clicker,clickerAttr,blocks,subject) {
$(container).each(
function(idx,instance) {
var containerElement = $(instance);
var containedBlocks = containerElement.find(blocks);
containerElement.find(clicker).each(function(idxC, instanceClicker) {
var tgtE = containerElement.find(subject+instanceClicker.getAttribute(clickerAttr));
var clickerBlocks = containedBlocks.not(tgtE);
$(instanceClicker).click(function(event) {
clickerBlocks.hide();
tgtE.toggle();
});
});
// initially cleared
containedBlocks.hide();
}
);
}
$(function() {
initToggle('.toggle','a.link','linkTarget','div.block','div.');
});
</script>
</head>
<body>
Example HTML block toggle:
<div class="toggle">
a <br />
b <br />
c <br />
<div class="A block"> A </div>
<div class="B block"> B </div>
<div class="C block"> C </div>
</div> <!-- toggle -->
This next one is not enabled, to show scoping.
<div class="toggle2">
a <br />
<div class="A block">A</div>
</div> <!-- toggle2 -->
This next one is enabled, to show use in multiple positions on a page, such as in a portlet library.
<div class="toggle">
a <br />
<div class="A block">A</div>
</div> <!-- toggle (2) -->
</body>
</html>

Javascript if else statement to hide and show div

Please refer to the following codes :
<div id="message-1" onclick="javascript:showresponddiv(this.id)>
</div>
<div id="respond-1" style="display:none;">
</div>
<div id="message-2" onclick="javascript:showresponddiv(this.id)>
</div>
<div id="respond-2" style="display:none;">
</div>
<script type="text/javascript">
function showresponddiv(messagedivid){
var responddivid = messagedivid.replace("message-", "respond-");
if (document.getElementById(responddivid).style.display=="none"){
document.getElementById(responddivid).style.display="inline";
} else {
document.getElementById(responddivid).style.display="none";
}
}
</script>
The codes above already success make the respond div appear when user click on message div. The respond div will disappear when user click on message div again. Now my question is how to make the respond div of 1st message disappear when user click on 2nd message to display the respond div of 2nd message?
You should give the "respond" divs a common class:
<div id="respond-1" class="response' style="display:none;"></div>
Then you can get all divs by using getElementsByTagName, compare the class and hide them on a match:
function hideAllResponses() {
var divs = document.getElementsByTagName('div');
for(var i = divs.length; i-- ;) {
var div = divs[i];
if(div.className === 'response') {
div.style.display = 'none';
}
}
}
We cannot use getElementsByClassName, because this method is not supported by IE8 and below. But of course this method can be extended to make use of it if it is supported (same for querySelectorAll). This is left as an exercise for the reader.
Further notes:
Adding javascript: to the click handler is syntactically not wrong but totally unnecessary. Just do:
onclick="showresponddiv(this.id)"
If you have to do a lot of DOM manipulation of this kind, you should have a look at a library such as jQuery which greatly simplify such tasks.
Update: If always only one response is shown and you are worried about speed, then store a reference to opened one:
var current = null;
function showresponddiv(messagedivid){
var id = messagedivid.replace("message-", "respond-"),
div = document.getElementById(id);
// hide previous one
if(current && current !== div) {
current.style.display = 'none';
}
if (div.style.display=="none"){
div.style.display="inline";
current = div;
}
else {
div.style.display="none";
}
}
Edit: Fixed logic. See a DEMO.
You can add some class to all divs with id="respond-"
e.g
<div id="respond-1" class="classname" style="display:none;"></div>
<div id="respond-2" class="classname" style="display:none;"></div>
Now at first row of your function "showresponddiv()" you should find all divs with class "classname" and hide them.
With jQuery it is simple code:
$(".classname").hide();
jQuery - is a Javascript Library that helps you to easy manipulate with DOM and provides cross-browser compatibility.
Also you can look to Sizzle - it is a JavaScript CSS selector engine used by jQuery for selecting DOM elements

Categories