Change style of an element in a forEach loop - javascript

Goal: Change style of an element in a forEach loop
Issue: Style not being applied
In my loop, if I console.log(element), I do get the right list of filtered elements.
Running this works, but I want my function to modify every element that match my filter:
let contentPreview = document.querySelectorAll('.o_last_message_preview');
contentPreview[0].style.color = 'yellow'
All elements are correctly assigned.
function changeNotificationColor() {
// Notification button that opens list of notifications
let notifyButton = document.querySelector('.dropdown-toggle.o-no-caret[title="Conversations"]');
notifyButton.addEventListener('click', function () {
// List of notifications - each notification is contained in a .o_last_message_preview class
let contentPreview = document.querySelectorAll('.o_last_message_preview');
contentPreview.forEach(function (element) { if (element.innerText.includes('Marine')) {element.style.color = 'yellow'}})
})
}
changeNotificationColor();
HTML Notifications Dropdown button:
<a class="dropdown-toggle o-no-caret" data-toggle="dropdown" data-display="static" aria-expanded="true" title="Conversations" href="#" role="button">
<i class="o_mail_messaging_menu_icon fa fa-comments" role="img" aria-label="Messages"></i> <span class="o_notification_counter badge badge-pill">366</span>
</a>
HTML template of a notification node (each notification creates one of these nodes):
<div class="o_preview_info">
<div class="o_preview_title">
<span class="o_preview_name">
You have been assigned to Backorder xx
</span>
<span class="o_preview_counter">
(1)
</span>
<span class="o_last_message_date ml-auto mr-2"> 3 minutes ago </span>
</div>
<div class="o_last_message_preview">
Marine
</div>
<span title="Mark as Read" class="o_discuss_icon o_mail_preview_mark_as_read fa fa-check"></span>
</div>

I don't know what your issue is, but it's not in the code that you showed; this works fine:
let contentPreview = document.querySelectorAll('.o_last_message_preview');
contentPreview.forEach(function (element) { if (element.innerText.includes('Marine')) {element.style.color = 'yellow'}})
<div class="o_last_message_preview">This contains Marine</div>
<div class="o_last_message_preview">This does not</div>
<div class="o_other_message_preview">This has a different class</div>

Related

How do I hide my class element when data-reviews is less than 5?

I have tried using basic JavaScript when data-reviews="2" and it worked, but what I need is when data-reviews is less than 5 or any specific number, the whole <span> section should be hidden. I need assistance on this. I am trying to achieve the same outcome but I want to hide this, when data reviews has < 5.
<div class="collection-list-badge">
<span class="stamped-product-reviews-badge" data-id="5649263493270" style="display:block;">
<span class="stamped-badge" data-rating="5.0" data-lang="en" aria-label="Rated 5.0 out 42reviews"></span>
</span>
<span class="stamped-badge-caption" data-reviews="42" data-rating="5.0" data-label="reviews" aria-label="42 reviews" data-version="2">42</span>
</div>
Loop over the badge elements. For each collection badge select the review element. Parse the data-reviews attribute with parseInt. Check if the review is less than 5. If so, remove the collection badge element.
const collectionListBadges = document.querySelectorAll('.collection-list-badge');
for (const collectionListBadge of collectionListBadges) {
const reviewsElement = collectionListBadge.querySelector('[data-reviews]');
const amountOfReviews = parseInt(reviewsElement.getAttribute('data-reviews'));
if (amountOfReviews < 5) {
collectionListBadge.remove();
}
}
<div class="collection-list-badge">
<span class="stamped-product-reviews-badge" data-id="5649263493270" style="display:block;">
<span class="stamped-badge" data-rating="5.0" data-lang="en" aria-label="Rated 5.0 out 42reviews"></span>
</span>
<span class="stamped-badge-caption" data-reviews="42" data-rating="5.0" data-label="reviews" aria-label="42 reviews" data-version="2">42</span>
</div>
<div class="collection-list-badge">
<span class="stamped-product-reviews-badge" data-id="5649263493270" style="display:block;">
<span class="stamped-badge" data-rating="4.0" data-lang="en" aria-label="Rated 4.0 out 38reviews"></span>
</span>
<span class="stamped-badge-caption" data-reviews="4" data-rating="4.0" data-label="reviews" aria-label="38 reviews" data-version="2">4</span>
</div>

Dropdown menu click eventListener producing a different target element based on area clicked

I have a MaterializeCSS dropdown menu implemented with this HTML. The event listener works only if the dropdown item is clicked in the upper portion.
<div class="left">
<span id="notificationTotal" class="new badge red" style="display:none">
</span>
<a class="dropdown-trigger" href="#!" data-target="notificationsDropdown">
<i class="material-icons"> message</i>
</a>
</div>
<ul id="notificationsDropdown" class="dropdown-content">
</ul>
I'm using the following Javascript to populate the menu with notifications. This is working just fine.
// Reset Notification Dropdown
notificationsDropdown.innerHTML = '';
notifications.forEach(notif => {
const displayTime = moment(notif.date).fromNow();
let typeIcon = 'sms';
if (notif.type === 'chat') {
typeIcon = 'lock';
}
notificationsDropdown.innerHTML += `<li class="notification">
<a style="margin-top:0px;margin-bottom:0px;padding-bottom: 0;font-size:14px" href="#" class="blue-text">
<span class="js-patientName">
${notif.contact.firstName} ${notif.contact.lastName}</span>
<span class="js-notificationPhone" style="display:none">${
notif.contact.phone
}</span>
<span class="js-patientId" style="display:none">${
notif.patientId
}</span>
<span class="js-patientDOB" style="display:none">${
notif.contact.birthDate
}</span>
<p style="margin-top:0px;margin-bottom:0px;padding-bottom: 0;padding-top: 0;">
<i style="display: inline-flex; vertical-align:middle" class="tiny material-icons">${typeIcon}</i>
<span class="black-text" style="font-size:12px">
${displayTime}
</span>
</p>
</a></li>`;
});
notificationsDropdown.innerHTML += `<li class="divider" class="blue-text"></li><li>See All Notifications</li>`;
}
The dropdown gets populated and when a user clicks on a particular dropdown .li entry depending on the exact location they click, it may or not work. The user must click at the main top of the dropdown item.
This is the event listener code that extracts the values from the hidden span elements.
document
.querySelectorAll('#notificationsDropdown', '.li .a .notification')
.forEach(input =>
input.addEventListener('click', async e => {
// console.log('clicked', e.target);
console.log(e.target.parentNode);
const name = e.target.children[0].textContent.trim();
const phone = e.target.children[1].textContent.trim();
const patientId = e.target.children[2].textContent.trim();
const birthDate = e.target.children[3].textContent.trim();
console.log('patientid ', patientId);
const patient = {
name,
patientId,
phone,
birthDate
};
Is there a way I can rewrite the eventListener code to resolve this issue? Possibly instead of using e.target.children[insert_number_here].textContent I could use .closest('js-patientId') or similar ?
This is how the HTML is rendered to the page. This is an example of a single notification:
<ul
id="notificationsDropdown"
class="dropdown-content"
tabindex="0"
style="display: block; width: 177.297px; left: 1648.7px; top: 0px; height: 251px; transform-origin: 100% 0px; opacity: 1; transform: scaleX(1) scaleY(1);"
>
<li class="notification">
<a
style="margin-top:0px;margin-bottom:0px;padding-bottom: 0;font-size:14px"
href="#"
class="blue-text"
>
<span class="js-patientName">ANDREW TAYLOR</span>
<span class="js-notificationPhone" style="display:none">
5555551212
</span>
<span class="js-patientId" style="display:none">
1
</span>
<span class="js-patientDOB" style="display:none">
1960-01-01
</span>
<p style="margin-top:0px;margin-bottom:0px;padding-bottom: 0;padding-top: 0;">
<i
style="display: inline-flex; vertical-align:middle"
class="tiny material-icons"
>
sms
</i>
<span class="black-text" style="font-size:12px">
2 hours ago
</span>
</p>
</a>
</li>
<li class="divider" />
<li>
<a href="/notifications" class="blue-text">
See All Notifications
</a>
</li>
</ul>;
For dropdowns, the better way to handle actions is through onChange event. So you could attach your eventHandler on onChange of dropDown itself rather than attaching onClick on each of dropDown items. So whenever you change the selected value the onChange event will be triggered and you can easily get the selected value.
I was able to remove the in each li. Then adjust the value assignment to use the following:
e.target.parentNode.children[0].textContent.trim();

Problem selecting correct value from array and assigning to variable

Problem:
I am writing a tracking electron app where the user data is stored on a local JSON file. Essentially i have the cards (user info from json) loaded to display via html. My next step is to run the python backend, problem i am having is I currently can't load the correct array value, only the last one is currently loading into a variable that im trying to pass to python. Since im using forEach i shouldn't have to count or do i still need to do that?
What I expect to happen:
I want an alert to pop up with the current user.handle value. What happens now is it only pops up the last value in the array regardless of which card i press. How can i make each button press trigger the corresponding handle value?
When i test and swap out onclick="myFunction11()" with onclick=alert('${user.handle}') it prints the correct handle value. So i can assume i am just overwriting var city every time and left with the last one in the array. Any advice on how to correctly have myFunction11 pull the corresponding handle value i would love. thanks
Question:
How can i correctly have myFunction11 pull the correct handle value from the array? or is there a better way to achieve this?
Code:
$(document).ready(function() {
users.forEach(function(user) {
$('.team').append(`<div class="card">
<figure>
<img src="${user.image}" />
<figcaption>
<h4>${user.name}</h4>
<h5>${user.handle}</h5>
</figcaption>
</figure>
<div class="links">
<i class="fa fa-pencil"></i>
<i class="fa fa-truck"></i>
<i class="fa fa-trash-o"></i>
</div>
<div class="task">
<div>
</div>
</div>
<div class="bottom-links">
<i class="fa fa-pencil"></i> EDIT
<i class="fa fa-truck"></i> TRACK
</div>
</div>
<script>
function myFunction11() {
var city = '${user.handle}';
alert(city);
}
</script>
`);
});
Adding duplicates of the same function is indeed bad practice. Your way when you have three users you will have defined function myFunction11 three times. And in fact (as you guessed) you end up with the function only having the last value of city.
Instead, this should be better:
$(document).ready(function() {
$('.team').append(`<script>
function myFunction11(city) {
alert(city);
}
</script>`);
users.forEach(function(user) {
$('.team').append(`
...
<i class="fa fa-pencil"></i>
...`);
});
define function outside loop and pass user index as parameter
function myFunction11(index) {
var city = users[index].handle;
alert(city);
}
$(document).ready(function() {
users.forEach(function(user,i) {
$('.team').append(`<div class="card">
<figure>
<img src="${user.image}" />
<figcaption>
<h4>${user.name}</h4>
<h5>${user.handle}</h5>
</figcaption>
</figure>
<div class="links">
<i class="fa fa-pencil"></i>
<i class="fa fa-truck"></i>
<i class="fa fa-trash-o"></i>
</div>
<div class="task">
<div>
</div>
</div>
<div class="bottom-links">
<i class="fa fa-pencil"></i> EDIT
<i class="fa fa-truck"></i> TRACK
</div>
</div>`);
});
});

Is it possible to display a JavaScript variable where the value will change in between two <li> tags?

Apologies if the question title is a little vague, I'm still a beginner in javascript (I'm open to edit suggestions!). If you look at the screenshot above I have 5 input boxes with values in it. These values are ratings returned by Google Places APIs and every time a user searches a new location these values will change. I'm displaying them like so.(I'll use the Gym option as an example).
Gymcallback function (Calucluates an average rating for all gyms in the area)
function gymCallback(results2, status2) {
var totalRating = 0,
ratedCount = 0;
results2.forEach(function( place ) {
if (place.rating !== undefined) {
ratedCount++;
totalRating += place.rating;
}
});
var averageRating = results2.length == 0 ? 0 : totalRating / ratedCount;
var averageRatingRounded = averageRating.toFixed(1);
var averageGymRatingTB = document.getElementById('gymAvgRating');
averageGymRatingTB.value = averageRatingRounded;
}
The averageGymRatingTB value will then be passed into the input box in the screenshot like so:
<p>
Gyms:
<input type="text" size="10" name="gymAvgRating" id="gymAvgRating" />
</p>
My question is, is it possible to display the gym rating next "fitness" in the navbar?
Fitness list option in the navbar
<li data-toggle="collapse" data-target="#fitness" class="collapsed">
<a onclick="clearMarkers();GymReport();" href="#"><i class="fa fa-heart fa-lg"></i> Fitness <span
class="arrow"></span></a>
</li>
I've looked at using the following approach by adding an innerhtml element using 'test' as the value I'm passing through but this won't work, so I'm unsure.
<li data-toggle="collapse" data-target="#fitness" class="collapsed">
<a onclick="clearMarkers();GymReport();" href="#"><i class="fa fa-heart fa-lg"></i> Fitness <span
class="arrow"></span></a><h5 id = demo></h5>
</li>
And in my javascript I use this:
document.getElementById("demo").innerHTML = "test";
You can simply add a span to the navigation, store it in a variable and change the content of using innerText.
function clearMarkers() {}
function GymReport() {}
var a = document.querySelector('li[data-target="#fitness"] > a'); //get the a in the menu
var fitnessScore = document.createElement("span"); //create a new span
a.appendChild(fitnessScore); // add the span to the a
function changeValue(v) {
fitnessScore.innerText = Math.random();
}
<ul>
<li data-toggle="collapse" data-target="#fitness" class="collapsed">
<a onclick="clearMarkers();GymReport();" href="#"><i class="fa fa-heart fa-lg"></i> Fitness <span
class="arrow"></span></a>
</li>
</ul>
<input type="button" onclick='changeValue()' value="Change value">

Running click functions on every instance of listing instead of current

I have a listing of articles here, and I can't figure out how to execute the ng-click function calls on every new article inside the ng-repeat. Right now it works for existing articles, but when new articles are added dynamically (via AJAX), I need those to have the same functionality too.
For example: the ng-click function calls on the "+" sign to reveal social buttons seem to not work once new articles are inserted via AJAX (ie: delete articles, and let list be populated again with new elements)
Does AngularJS provide any tools to do that?
<div>
<div>
<input type="text" ng-model="search">
<span>{{filtered.length}} article(s)</span>
</div>
<div article-listing ng-repeat="article in filtered = (wikiArticles | filter:search)">
<!--Individual article begin-->
<span>
{{article.title}}
</span>
<div>
<a ng-click="articles.removeArticle($index)" title="Delete">
<span>✖</span>
</a>
<a ng-click="articles.toggleShare(article)">
<span class="plus-sign" title="Share">✖</span>
<div social-share ng-show="article.socialShare">
<div ng-click="socialShare = !socialShare" class="addthis_toolbox addthis_default_style addthis_32x32_style"
addthis:title="{{article.title}}" addthis:description="{{article.extract}}" addthis:url="{{article.url}}">
<a class="addthis_button_facebook"></a>
<a class="addthis_button_twitter"></a>
<a class="addthis_button_google_plusone_share"></a>
<a class="addthis_button_reddit"></a>
<a class="addthis_button_hackernews"></a>
</div>
</div>
</a>
</div>
<div>{{article.extract}}</div>
<!--Individual article end-->
</div>
</div>
Code for ng-click calls that don't seem to work for new article insertions
$scope.articles = (function() {
return {
shuffleArticles : function() {
$scope.wikiArticles.reverse();
},
removeArticle : function(index) {
$scope.wikiArticles.splice(index, 1);
$scope.fireAPICalls();
},
toggleShare : function(currArticle) {
var previousState = currArticle.socialShare;
angular.forEach($scope.wikiArticles, function(article) {
article.socialShare = false;
});
currArticle.socialShare = previousState ? false : true;
}
}
})();
Your ng-click calls are actually working- you can watch the ng-show toggle in the debugger.
The problem is that there is nothing to display on the new items you add.
The articles you initially add all have their icons populated with the .addthis classes, for instance here's your Facebook icon element:
<a class="addthis_button_facebook at300b" title="Facebook" href="#">
<span class=" at300bs at15nc at15t_facebook">
<span class="at_a11y">Share on facebook</span>
</span>
</a>
at300bs includes the following css which displays the image:
background: url(widget058_32x32.gif) no-repeat left!important;
However as you add new items, you aren't including the needed .addthis classes to them. Their elements look like this:
<a class="addthis_button_facebook"></a>
So ng-show has nothing to display (it shows a 0x0 div).
Add the .addthis classes to your new elements as you add them and you'll be all set.

Categories

Resources