I have 5 html files with very similar content: it's a cv builder website and I am making 5 different cv templates. What I was trying to do is to create a javacript class which will instance 5 different objects from the class with different values.
But when I link the same js file with all the 5 html pages, an error occurs and the innerHTML cannot be read. Here is a piece of the code:
let resume1Div = document.querySelector(".resume-web-development");
let resume2Div = document.querySelector(".resume-data-science");
class resumeHtml {
constructor(_title, _jobPosition, _summary, _image, _email, _phone, _address, _twitter) {
this.title = _title;
this.jobPosition = _jobPosition;
this.summary = _summary;
this.image = _image;
this.email = _email;
this.phone = _phone;
this.address = _address;
this.twitter = _twitter;
}
}
let resume1 = new resumeHtml("Elon Musk", "Enterpreneur, Engineer, Inventor and Investor", "Aiming to reduce global warming through sustainable energy production and consumption. Planning to reduce the risk of human extinction by making life multi-planetary and setting up a human colony on Mars.", "assets/images/musk.jpg", "elon#teslamotors.com", "620-681-5000", "Los Angeles, USA", "#elonmusk");
let resume2 = new resumeHtml("John Doe", "Data Scientist", "Highly accurate and experienced Data Scientist adept at collecting, analyzing and interpreting large datasets, developing new forecasting models, and performing data management tasks.", "assets/images/john.jpg", "john#gmail.com", "6782092", "Ontario, Canada", "#johnDoe");
let resume1Arr = [resume1];
let resume2Arr = [resume2];
resume1Arr.forEach(el => {
resume1Div.innerHTML +=
`
<div class="resume-title col-md-10">
<span></span>
<h2>${el.title}</h2>
</div>
<div class="col-md-5 resume-description">
<h4 class="resume-job-position">${el.jobPosition}</h4>
<p class="resume-summary">${el.summary}</p>
</div>
<div class="col-md-2 resume-img">
<img src=${el.image} class="img-circle">
</div>
<div class="col-md-3 col-md-offset-2 resume-contact-details">
<span>${el.email}</span><i class="fa fa-envelope" aria-hidden="true"></i><br>
<span>${el.phone}</span><i class="fa fa-mobile" aria-hidden="true"></i><br>
<span>${el.address}</span><i class="fa fa-map-marker" aria-hidden="true"></i><br>
<span>${el.twitter}</span><i class="fa fa-twitter" aria-hidden="true"></i>
</div>
`
});
resume2Arr.forEach(el => {
resume2Div.innerHTML +=
`
<div class="resume-title col-md-10">
<span></span>
<h2>${el.title}</h2>
</div>
<div class="col-md-5 resume-description">
<h4 class="resume-job-position">${el.jobPosition}</h4>
<p class="resume-summary">${el.summary}</p>
</div>
<div class="col-md-2 resume-img">
<img src=${el.image} class="img-circle">
</div>
<div class="col-md-3 col-md-offset-2 resume-contact-details">
<span>${el.email}</span><i class="fa fa-envelope" aria-hidden="true"></i><br>
<span>${el.phone}</span><i class="fa fa-mobile" aria-hidden="true"></i><br>
<span>${el.address}</span><i class="fa fa-map-marker" aria-hidden="true"></i><br>
<span>${el.twitter}</span><i class="fa fa-twitter" aria-hidden="true"></i>
</div>
`
});
Resume1Div is the div in one html file, Resume2Div is the div in the another html file.
How can I use the same js file for all the html files just by changing the values, without making 5 separate js files with the same code?
I assume on some of your five pages you got the div with class resume-web-development on the others the div with class resume-data-science.
That leads to one variable referencing a DOM element and one variable empty in each case.
As your code is basically well structured you just have to check whether it is empty as in:
if (resume1Div) resume1Arr.forEach(el => {
resume1Div.innerHTML += ...
}
if (resume2Div) resume2Arr.forEach(el => {
resume2Div.innerHTML += ...
}
That should already do the trick.
You can then optimise the code with something like:
function fillDiv(div, el){
div.innerContent+= ...
}
let resumeDiv = resume1Div || resume2Div;
fillDiv(resumeDiv, resumeDiv === resume1Div ? resume1 : resume2);
Might still be improved.
Related
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>
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>`);
});
});
I am trying to make the following bit of code easier to maintain. I am not a web developer so bear with me. I think the following approach is appropriate.
I would like to dynamically add content and attributes to an html file using either javascript or jQuery. The items could reside in a .csv or .json (or something else?) file.
Given content like this
<div class="filtr-container">
<div class="col-12 col-sm-6 col-md-4 card filtr-item" data-category="cat-1" data-date="2018-02-09">
<div class="card-inner-border box-shadow">
<a href="address-1.html">
<img class="card-img-top rounded-top" src="./images/image-1.jpg" alt="img-2-alt">
</a>
<div class="card-body">
<h5 class="card-title">Title-1</h5>
<p class="card-text card-desc">
This is a description for title-1 content.
</p>
<a href="address-1.html">
<button type="button" class="btn btn-sm btn-outline-secondary">View</button>
</a>
<p class="card-text">
<small class="text-muted">Last updated February 2, 2018</small>
</p>
</div>
</div>
</div>
<div class="col-12 col-sm-6 col-md-4 card filtr-item" data-category="cat-2, cat-3" data-date="2018-02-14">
<div class="card-inner-border box-shadow">
<a href="address-2.html">
<img class="card-img-top rounded-top" src="./images/image-2.jpg" alt="img-2-alt">
</a>
<div class="card-body">
<h5 class="card-title">Title-2</h5>
<p class="card-text card-desc">
Here is a long description for title-2 content.
</p>
<a href="address-2.html">
<button type="button" class="btn btn-sm btn-outline-secondary">View</button>
</a>
<p class="card-text">
<small class="text-muted">Last updated February 14, 2018</small>
</p>
</div>
</div>
</div>
<!-- MANY MORE CARDS / ITEMS ... -->
</div> <!-- End of filtr-container -->
I think we could abstract the details into something like this (.csv)
item-id,title,description,categories,address,image,image-alt,update
1,Title-1,This is a description for title-1 content.,cat-1,address-1.html,image-1.jpg,img-1-alt,2018-02-09
2,Title-2,Here is a long description for title-2 content.,"cat-2, cat-2",address-2.html,image-2.jpg,img-2-alt,2018-02-14
What's a nice approach of attack for using `javascript` or `jQuery` to add this content from the `.csv` or `.json` file?
A few concerns:
The headers of the .csv will not match verbatim (e.g. <p class="card-desc"> aligns with the .csv header of description)
There could be embedded comma-separated items (e.g. item 2 has categories cat-2, cat-3 so it gets quotes " in the .csv -- maybe .json would better (?) or perhaps its a non-issue)
If possible can we reuse the date item for both data-date= and the final piece of text <small class="text-muted"> which converts the date into Last updated month-name-long, dd, yyyy instead of yyyy-mm-dd.
Some attributes are partial references (e.g. the src for an image is just the final part of the path; stated as image-1.jpg in the .csv not ./images/image-jpg).
To hopefully help make this feel less complicated, here's a picture with the highlighted elements that could be "referenced" from the .csv file.
To me this feels like:
Read in the .csv file.
For each item in the .csv file, append objects to $(".filtr-container") with the shell layout...
But I'm lost when it comes to the particulars or if that's an appropriate approach.
You seem to be searching for template parsing. You can find many libraries that will ease this burden. In its simplest form, template parses carry out the steps in the following code. If you don't need the flexibility, power, features, etc. from a template parser library or full framework, you should consider not including the thousands of lines of code if all you want to accomplish is what is shown below.
Since you mentioned both JSON and CSV I've included the code to parse both. I'll leave the AJAX and date formatting magic to you. I don't think I populate the ID either, but this shows that more data than template attributes will work fine.
let template = document.getElementById('card-template').innerHTML;
let container = document.querySelector('.filtr-container');
// Do some ajax magic to get csv file
let csv = `item-id,title,description,categories,address,image,image-alt,update
1,Title-1,This is a description for title-1 content.,cat-1,address-1.html,https://via.placeholder.com/75,img-1-alt,2018-02-09
2,Title-2,Here is a long description for title-2 content.,cat-2 cat-2,address-2.html,https://via.placeholder.com/75,img-2-alt,2018-02-14`;
let csvLines = csv.split("\n");
let csvHeaders = csvLines.shift().split(',');
csvLines.forEach(line => {
let parsed = template;
let props = line.split(',');
props.forEach((prop, idx) => {
parsed = parsed.replace('{{' + csvHeaders[idx] + '}}', props[idx]);
});
container.innerHTML = container.innerHTML + parsed;
});
let json = `[{
"item-id": "1",
"title": "Title-1",
"description": "This is a description for title-1 content.",
"categories": "cat-1",
"address": "address-1.html",
"image": "https://via.placeholder.com/75",
"image-alt": "img-1-alt",
"update": "2018-02-09"
}, {
"item-id": "2",
"title": "Title-2",
"description": "Here is a long description for title-2 content.",
"categories": "cat-2 cat-2",
"address": "address-2.html",
"image": "https://via.placeholder.com/75",
"image-alt": "img-2-alt",
"update": "2018-02-14"
}]`;
let data = JSON.parse(json);
data.forEach(col => {
let jParsed = template;
for (prop in col) {
jParsed = jParsed.replace('{{' + prop + '}}', col[prop]);
}
container.innerHTML = container.innerHTML + jParsed;
});
<div class="filtr-container">
<script type="template" id="card-template">
<div class="col-12 col-sm-6 col-md-4 card filtr-item" data-category="{{categories}}" data-date="{{date}}">
<div class="card-inner-border box-shadow">
<a href="{{address}}">
<img class="card-img-top rounded-top" src="{{image}}" alt="{{image-alt}}">
</a>
<div class="card-body">
<h5 class="card-title">{{title}}</h5>
<p class="card-text card-desc">
{{description}}
</p>
<a href="{{address}}">
<button type="button" class="btn btn-sm btn-outline-secondary">View</button>
</a>
<p class="card-text">
<small class="text-muted">Last updated {{update}}</small>
</p>
</div>
</div>
</div>
</script>
This post might help you parse your CSV document. If your data lives in a JSON, you can use JSON.parse
Once you properly retrieved and parsed your data, it's a matter or rendering it to the DOM.
You can do it using the standard javascript library, JQuery or frameworks such as React or VueJS
I'm trying to develop a Load-More button using Javascript calling an API done with PHP.
So far, so good. I can load the new objects in a range i += i + 4 (I load three new comments everytime I press the button).
This is my load-more button:
$(document).ready(function () {
$(".load-more").on('click', function () {
var tab = $(this).data('tab');
var next_page = $(this).data('next-page');
console.log(next_page);
console.log(tab);
$.get($(this).data('url') + '?tab=' + tab + '&page=' + next_page, function (data) {
addNewQuestions($.parseJSON(data));
});
});
});
And for every object loaded I want to print each of these html blocks.
<div class="question-summary narrow">
<div class="col-md-12">
<div class="votes">
<div class="mini-counts"><span title="7 votes">
{if $question['votes_count']}
{$question['votes_count']}
{else}
0
{/if}
</span></div>
<div>votes</div>
</div>
<div {if $question['solved_date']}
class="status answered-accepted"
{else}
class="status answer-selected"
{/if}
title="one of the answers was accepted as the correct answer">
<div class="mini-counts"><span title="1 answer">{$question['answers_count']}</span></div>
<div>answer</div>
</div>
<div class="views">
<div class="mini-counts"><span title="140 views">{$question['views_counter']}</span></div>
<div>views</div>
</div>
<div class="summary">
<h3>
<a href="{questionUrl($question['publicationid'])}" class="question-title" style="font-size: 15px; line-height: 1.4; margin-bottom: .5em;">
{$question['title']}
</a>
</h3>
</div>
<div class = "statistics col-sm-12 text-right" style="padding-top: 8px">
<span>
<i class = "glyphicon glyphicon-time"></i>
<span class="question-updated-at">{$question['creation_date']}</span>
</span>
<span>
<i class = "glyphicon glyphicon-comment"></i>
<span class="question-answers">{$question['answers_count']}</span>
</span>
</div>
</div>
</div>
The problem is that I have several conditions, as {if$question['votes_count']} and I'm struggling because I don't know how get those variables when rendering the html.
Then I found something but I can't figure out how to adapt to my case
On that addNewQuestions I think that a good approach would be:
function addNewQuestions(objects) {
$.each(objects, function (i, object) {
console.log(object);
var lastItem = $('div.active[role="tabpanel"] .question-line:last');
var newLine = lastItem.clone(true);
var newObject = newLine.find('.question-col:eq(' + i + ')');
newObject.find('.question-info-container').attr('data-id', object.publicationid);
newObject.find('.vote-count').html(object.votes);
updateTitleAndLink(newObject.find('.question-title'), object);
lastItem.after(newLine);
});
}
function updateTitleAndLink(questionTitle, object) {
questionTitle.attr('href', questionTitle.data('base-question-url') + object.publicationid);
questionTitle.html(object.title);
}
But nothing happens and I can't figure out why.
Any idea or suggestion?
Kind regards
So I'm running into a weird issue with Angular where I created a raw predefined object to loop through card stats for a card game I am working on.
$scope.attack = 500;
$scope.defense = 500;
$scope.cardPowers = [
{
name: "balanced",
attack: 2500,
defense: 1500,
cost: 3
},
{
name: "high-offense",
attack: 2500,
defense: 1000,
cost: 5
},
{
name: "base-offense",
attack: 1800,
defense: 1000,
cost: 4
}
];
This is all in the main controller where most of my functionality is going through. I'm looping through them and creating buttons where someone can choose the attributes for the car like so:
<div id="preview" class="vertical-display card twenty-five building" ng-class="{'building': gotComics, 'editing' : editing}" ng-controller="Save">
{{attack}} : {{defense}}
<h3>Card Builder</h3>
<div class="card-functions">
<ul>
<li>SAVE <i class="fa fa-floppy-o"></i></li>
<li>EDIT <i class="fa fa-pencil-square-o"></i></li>
<li>CLOSE <i class="fa fa-times"></i></li>
</ul>
<!-- <i class="fa fa-file-image-o"></i> Save as Image -->
</div>
<div ng-show="editing" class="options">
<h4>Choose Power</h4>
<p ng-repeat="power in cardPowers">{{power.name | uppercase}} {{attack}}</p>
<h4>Thumbnail Positioning</h4>
<!-- repeat card positioning -->
</div>
<div ng-show="gotComics">
<?php include('flip-container-code.php'); ?>
</div>
Mostly everything works fine, however, when I click on my button to set the new attack, it only updates {{attack}} inside the loop, and not outside. See this screencast to see what I am talking about: http://screencast.com/t/0ZukLLYqtUYM
Not sure why this is happening, did some research and it seems I am doing this all right, so it's odd. Note: Everything else is actually working just fine, my card saving function, seeing the default attack and defense values, the models, etc.
To start, you should move the implementation details outside of the view (i.e., you shouldn't be directly setting model.attack = power.attack in the view, that should be at least one level deeper, in the controller). With that, moving this to the controller will resolve your issue itself.
Your view can look like this:
<p ng-repeat="power in cardPowers">
<a href="" ng-click="setAttackPower(power)">
{{power.name | uppercase}} {{attack}}
</a>
</p>
And in your controller:
$scope.setAttackPower = function(power) {
$scope.attack = power.attack;
}
If I understand your problem right, try to put the attack property inside an object. This is because when you do attack = power.attck inside ng-repeat you are creating a new property on the child scope created by the ng-repeat instead of changing the value of the attack on the parent scope.
In your controller:
$scope.model = {attack: 500};
And your html:
<div id="preview" class="vertical-display card twenty-five building" ng-class="{'building': gotComics, 'editing' : editing}" ng-controller="Save">
{{model.attack}} : {{defense}}
<h3>Card Builder</h3>
<div class="card-functions">
<ul>
<li>SAVE <i class="fa fa-floppy-o"></i></li>
<li>EDIT <i class="fa fa-pencil-square-o"></i></li>
<li>CLOSE <i class="fa fa-times"></i></li>
</ul>
<!-- <i class="fa fa-file-image-o"></i> Save as Image -->
</div>
<div ng-show="editing" class="options">
<h4>Choose Power</h4>
<p ng-repeat="power in cardPowers">{{power.name | uppercase}} {{model.attack}}</p>
<h4>Thumbnail Positioning</h4>
<!-- repeat card positioning -->
</div>
<div ng-show="gotComics">
<?php include('flip-container-code.php'); ?>
</div>
See here for further explanation.
Try to accest the parent and it should work
Like this on ng-click:
<p ng-repeat="power in cardPowers">{{power.name | uppercase}} {{attack}}</p>