Vanilla JS: Reveal only the next element at the same parent group - javascript

I am trying to create few tabs - with loop.
Then when you click on the tab's name which is a span, its expand(or hide) the next span, which is below it. The problem is that the function works only in the first created tab from the loop and if you click on the second/third, etc tab - the function triggers only at the first tab.
Was trying with querySelector and nextElementSibling without any success.
Using ejs template, express and vanilla javascript only.
This is the loop/tab.
<div>
<% listedProducts.forEach(function(product) { %>
<div class="col">
<span id="info" class="b-d" onclick="hideReveal()">
<%= product.name %>
</span>
<span>
<img src="<%= product.logo %>" alt="">
<p class="r-d"><%= product.supplierCompany %></p>
<p class="r-d"><%= product.dateListed %></p>
<p class="r-d"><%= product.dateApproved %></p>
<p class="r-d"><%= product.supplierName %></p>
</span>
</div>
<% }); %>
</div>
and the function
function hideReveal() {
var tab = document.getElementById("info");
if (tab.style.display === "none") {
tab.style.display = "block";
} else {
tab.style.display = "none";
}
}
Would like a little help, please. Thank you and have a nice weekend.

in your click handler, pass an event argument, e.g., function clickHandler(evt) then say var thisTab = evt.target.id. This assumes you have an id attribute on the tab(s). Then you can toggle the open/closed class of thisTab's firstElementChild

Related

How to redirect user to new page on div click JQuery [duplicate]

This question already has answers here:
What does "this" refer to in arrow functions in ES6?
(10 answers)
Closed 4 years ago.
How to load a new URL depending on which div was clicked. Every div has its own URL for loading (in my implementation divs are blog pages).
My JQuery: & My html:
$('.postWraper').click(() => {
window.location = $(this).find('h3').find('a').attr('href');
return false;
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="postWraper">
<h3 class="postTitleH3">
<a href="blog/first-page" class="postTitle">
<%= post.postTitle %>
</a>
</h3>
</div>
<div class="postWraper">
<h3 class="postTitleH3">
<a href="blog/second-page" class="postTitle">
<%= post.postTitle %>
</a>
</h3>
</div>
When I clicked on first or second div it's allways redirect me to first URL. I can't understand why. Please help!
The meaning of this is changed because you're using arrow function as the event handler. Change that to a regular function:
$('.postWraper').click(function() {
window.location = $(this).find('h3').find('a').attr('href');
return false;
});
You don't necessarily need to change the arrow function. You can still make it work by passing the event object in click event and using e.target.href; to get the href value:
$('.postWraper').click((e) => {
window.location = e.target.href;
return false;
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="postWraper" id="<%= post.id %>">
<h3 class="postTitleH3">
<a href="blog/first-page" class="postTitle">
Some title
</a>
</h3>
</div>
<div class="postWraper">
<h3 class="postTitleH3">
<a href="blog/second-page" class="postTitle">
Some title
</a>
</h3>
</div>

Add a css class using to "this" item in a forEach within EJS/node.js

I've been trying this for hours but I am stumped.
I have a kind of "shopping list" node.js app where you can tick off items as bought. I want the bought items to be styled green or smth. It is passing a mongoose obj to the client side along a route.
Server side:
router.get("/:id", function(req, res){
//find the list with provided ID
List.findById(req.params.id).populate("listItems").exec(function(err, foundList){
if(err){
console.log(err);
} else {
//render show template with that list
res.render("lists/show", {list: foundList});
The foundList obj basically has a structure of (not valid code, just so u can get an idea):
List {
name: String,
listItems: [{
text: String,
bought: Boolean
}]
On the client side I am using a forEach in ejs to loop through the listItems and display a html element for each item:
<% list.listItems.forEach(function(listItem){ %>
<div class="card">
<div class="card list">
..listItem name here
</div>
</div>
<% }) %>
I want to basically make it so that when an element is checked off and listItem.bought == true, the card list element is green, and when lisItem.bought == false, it's just white.
I've got it working with following code, however if one occurrence of the forEach is bought = true, all .card elements turn green. I want it so that only the element in question is effected.
<% list.listItems.forEach(function(listItem){ %>
<% if(listItem.bought === true){ %>
<script>
$(document).ready(function(){
(".card").css("background-color","green");
});
</script>
<% } else { %>
<script>
$(document).ready(function(){
(".card").css("background-color","white");
});
</script>
<% } %>
<div class="card">
<div class="card list">
....
Question: How do I target 1 occurrence of a forEach from server side data in ejs, and add a class/style to it. E.g. if there is 2 items on the list, 1 is bought and one not, one should be green and one white. Currently they both go green.
Thanks!
I don't think you need jQuery for this. You can just add the background color like this:
<% list.listItems.forEach(function (listItem) { %>
<div class="card" style="background-color: <%= listItem.bought ? 'green' : 'white' %>">
<div class="card list">
...
</div>
</div>
<% }); %>
If you want to use jQuery you probably need do add ids to the cards and use them to change the backgrounds.

Rails jquery select by dynamic value

I'm using Ruby on Rails and I'm trying to hide/show an individual div from a collection of dynamically generated items by using the dynamically generated id which I then insert in a data attribute. This is what I've come up with so far, I think I'm on the right track but can't figure out what I'm missing. Without the if statement it works but shows the hidden div of every item instead of that specific one, and with the if statement, nothing happens.
Link and hidden div
<span class="options" data-ovly=<%= activity.targetable.id %>>
<i class="fa fa-ellipsis-h"></i>
</span>
<div class="ed_ovly none" data-ovly=<%= activity.targetable.id %>>
<span class="shu_e">
<%= link_to "Edit", edit_medium_path(activity.targetable), :title => "Edit", :alt => "Edit", class: '' %>
</span>
<span class="shu_d">
<%= render :partial => "activities/delete", :locals => {:activity => activity} %>
</span>
</div>
jquery
$('.options').click(function(){
var $ovly = $(this).attr("data-ovly");
if ($('.ed_ovly').data("ovly") === $ovly) {
$('.ed_ovly').toggleClass("none");
}
});
TL;DR
Use id attributes instead of data-ovly in your div tags and avoid the if statement in your script:
html
<span class="options" data-ovly=<%= activity.targetable.id %>>
<i class="fa fa-ellipsis-h"></i>
</span>
<div class="ed_ovly none" id="<%= activity.targetable.id %>">
<span class="shu_e">
<%= link_to "Edit", edit_medium_path(activity.targetable), :title => "Edit", :alt => "Edit", class: '' %>
</span>
<span class="shu_d">
<%= render :partial => "activities/delete", :locals => {:activity => activity} %>
</span>
</div>
script
$('.options').click(function() {
var $ovly = $(this).data("ovly");
$('#' + $ovly).toggleClass("none");
});
Check this snippet:
$(document).ready(function() {
$('.options').click(function() {
var $ovly = $(this).data("ovly");
$('#' + $ovly).toggleClass("none");
});
});
.none {
display: none;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span class="options" data-ovly="1">
ovly#1 <i class="fa fa-ellipsis-h"></i>
</span>
<br><br>
<span class="options" data-ovly="2">
ovly#2 <i class="fa fa-ellipsis-h"></i>
</span>
<div class="ed_ovly none" id="1">
<span class="shu_e">
<a title="Edit ovly # 1" alt="Edit" href="#">Edit</a>
</span>
<span class="shu_d">
"Your rendered partial for ovly # 1"
</span>
</div>
<div class="ed_ovly none" id="2">
<span class="shu_e">
<a title="Edit ovly # 2" alt="Edit" href="#">Edit</a>
</span>
<span class="shu_d">
"Your rendered partial for ovly # 2"
</span>
</div>
Explanation
The first problem is in your if statement: You are using === to compare your values, but, since you are getting those values with different methods (attr and data), you are getting different data types.
Look closely at your code (i added a couple of comments):
$('.options').click(function(){
var $ovly = $(this).attr("data-ovly"); // Returns a string: "1"
if ($('.ed_ovly').data("ovly") === $ovly) // Returns a number: 1
{
$('.ed_ovly').toggleClass("none");
}
});
So your if returns false every time (since you are comparing string with number). To fix this, just use the same method1 to get data in both lines, like this:
$('.options').click(function(){
var $ovly = $(this).data("ovly"); // Returns a number: 1
if ($('.ed_ovly').data("ovly") === $ovly) // Returns a number: 1
{
$('.ed_ovly').toggleClass("none");
}
});
Now that you are getting same data types, your if statement will work as expected. But you are not out of the woods yet, there is another issue with your code, look how you are identifying each unique <div>:
if ($('.ed_ovly').data("ovly") === $ovly)
{
$('.ed_ovly').toggleClass("none");
}
You are using $('.ed_ovly'), that is, you are using the class, and all divs have that class, so with
$('.ed_ovly').data("ovly")
you will always get the value of the first div with class ed_ovly; and in
$('.ed_ovly').toggleClass("none")
you apply toggleClasss("none") to all divs with ed_ovly class (and, as a result, you will hide/show all divs every time).
So, you want to target only one div at a time, and the best way to do it is assigning an id to each div (also dynamically), something like this:
<div class="ed_ovly none" data-ovly="<%= activity.targetable.id %>" id="<%= activity.targetable.id %">
<!-- Your div code... -->
</div>
And on your script, refer only to the div you want to show/hide using its id, and that can be done by changing $('.ed_ovly') to $('#' + $ovly). Now your script should look like this:
if ($('#' + $ovly).data("ovly") === $ovly) {
$('#' + $ovly).toggleClass("none");
}
Your script now should be working fine, but something is off: with the changes we made, now the if is not necessary at all! Since we now get a unique id for each div, its safe to delete it:
$('.options').click(function() {
var $ovly = $(this).data("ovly");
$('#' + $ovly).toggleClass("none");
});
And, while we are at it, lets get rid also of that unnecessary data tag in your div (i assume you don't need it for anything else):
<div class="ed_ovly none" id="<%= activity.targetable.id %">
<!-- Your div code... -->
</div>
Note
1 You could still get away using both methods by comparing them with ==, but i prefer sticking with ===. Check this question for more information.

How to add "previous" and "next" button to text lightbox/modal

I am partially rendering data from an endpoint in a list using underscore, each list item has a link to "learn more...". Clicking on that link opens up a text modal/lightbox that shows the rest of the info for that particular element. I would like to add prev and next arrows to see the additional info on the items on the list, without having to close the modal and click on the next item.
I am thinking the way to approach this would be to add a class of "current" to my selected element, and remove/assign this class to each element as needed. But I am stuck thinking how to go about this... (newbie here, be kind).
HTML
<script type="text/template" id="event-template">
<div class="event row no-gutter">
<div class="col-md-12 info">
<div class="col-md-3">
<p class="day"><%= day %></p>
</div>
<div class="col-md-9">
<%= month %> at <%= time %>, <%= storename %> <br> <%= city %> <%= state %>
<a class="show-panel" data-id=<%=entry_id%> href="#_"> Learn More...</a>
</div>
<div class="lightbox-panel" id="lightbox-panel-<%=entry_id%>">
<h2><%= storename %></h2>
<p><%= month %>, <%= desc %></p>
<p align="center"><a class="close-panel" href="#_">Close window</a></p>
<div class="col-md-10" id="prev">Previous</div>
<div class="col-md-2" id="next">Next</div>
</div>
</div>
</div>
</script>
JS
// Open lightbox
$("#events-placeholder").on("click", ".show-panel", function(e) {
var entry_id = $(this).attr("data-id");
$("#lightbox-panel-"+entry_id).fadeIn(300);
$("body").css("background-color", "black");
});
//Close lightbox
$("#events-placeholder").on("click", "#close-panel", function(e) {
$(".lightbox-panel").fadeOut(300);
$("body").css("background-color", "white");
});
// Prev Info
$(".events-placeholder").on("click", "#prev", function(e) {
// ???
var current = $(this).attr("data-id");
var previous = current - 1;
$("#lightbox-panel-"+entry_id-1).fadeIn(300);
});
// Next Info
$("#events-placeholder").on("click", "#next", function(e) {
// ???
});

Ruby Rails - optional form input shown conditional on checkbox

I am working on a dynamic form input display where if the users clicks something to show "advanced options" in the form, the input vectors will be displayed, but otherwise (the default) they will not be shown. I have the following pieces in my form partial:
UPDATED 1x:
<div class="form-inputs">
<%= f.input :message, label: 'Write message'%>
</div>
<div class="form-inputs">
<%= f.check_box :advanced, {}, :id => "advancedbox" %>
</div>
<div class="form-inputs" id="advancedopts" style="display:none;">
<%= f.input :startdatetime, label: 'Specify', :as => :datetime_picker %>
<%= f.input :expirationdatetime, label: 'Specify', :as => :datetime_picker %>
</div>
And the JS in the head of the form partial:
UPDATED 1x:
<head><script type="text/javascript">
var checkbox = document.getElementById('advancedbox');
checkbox.onchange = function() {
if (checkbox.checked) {
document.getElementById("advancedopts").style.display = "block;";
} else {
document.getElementById("advancedopts").style.display = "none;";
}
}
</script></head>
I took the inspiration for this from the following post: show rest of a form if a checkbox is ckecked in ruby on rails
But I cannot get it to work: the checkbox shows, but clicking it on or off does not show my advanced form inputs. Can anyone help me debug?
Thanks!!
Your problem is that there is no event being listened for. In the example you linked, checkbox.onchange is an event that triggers a function (action) that displays the div. As yours is all namespaced within a function with no event to fire it, nothing happens as it is not evaluated when the state of your checkbox changes.
So if you move the line
var checkbox = document.getElementById('advancedbox');
outside your show function and then key the onchange event to that variable as in the example you linked, it should work.
EDIT: jQuery solution...
$(document).ready(function(){
$("#advancedbox").change(function(){
if( $(this).is(':checked') ){
$('#advancedopts').show(); // you could instead animate using .fadeIn() or .slideDown() if that was preferable
} else {
$('#advancedopts').hide(); // ditto above, using .fadeOut(), .slideUp() etc.
}
});
});
Your problem is here:
<div class="form-inputs" name="advancedopts" style="display:none;">
Changing the name attribute to id should do the trick:
<div class="form-inputs" id="advancedopts" style="display:none;">
Because I do not need to save the user selection, I use the following which works well for my purposes (inspired by changing DIV visibility with JavaScript):
in form partial:
<input type="button" value="Show advanced" onClick="show()">
<div class="form-inputs" id="advancedopts">
... </div>
in application.js:
function show(){
document.getElementById("advancedopts").style.visibility= 'visible' ;
}
in scaffolds.css:
#advancedopts{
visibility:hidden;
}
And works sufficiently for me without the use of a listener. Just in case anyone has a similar simple need and starts off on a track-more-complicated-than-necessary like me.

Categories