Click here to Skip to main content
15,884,879 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have content I am adding to my HTML through Javascript by using innerHTML. But now when I try to access a class or id attached to that content I just get null. How would I access the class or id?

This is what the HTML code looks like after it is added:

HTML
<a href="#" id="PE" class="portfolio__item filter-projects PE projects" data-id="PE">
                            <img class="portfolio__image" src="../img/image-confetti.jpg" alt="Columbia-Wrightsville Bridge Book Cover" />
                            <div class="portfolio__clientName">
                                <h3 id="PE" class="portfolio__clientName-text">Photo Essays</h3>
                            </div> <!-- end portfolio__clientName -->
                        </a>


I am just trying to select the class
projects
or even the id PE. But like I said it comes up null. Is there a way around this?

Thank you,
Emily

What I have tried:

document.querySelector('.projects') and document.querySelector('#PE'). I don't know anyother way to do it.
Posted
Updated 12-Jun-21 18:37pm
Comments
[no name] 10-Jun-21 21:38pm    
HTML is not considered "content".

Quote:
HTML
<a id="PE" ...
<h3 id="PE" ...
You've got multiple elements in your fragment with the same ID. The ID needs to be unique across the whole document.

Beyond that, querying by class name should work. You might need to use querySelectorAll if there will be multiple elements with the same class name, but querySelector will return the first matching element. If it's not returning any elements, then those elements are not in your document when you call querySelector.
 
Share this answer
 
Copy and paste the code below into a text file and rename it 'index.htm' You will have a running example of the answer.

This a second version of the answer
all the alert are commented out to make easier to follow the code in debug mode
(once created 'index.html' open it and then hit F12. The browser will turn in debug mode and the 'debugger' key word will stops the code excution waiting for continue (F8 or F10 or F11)

<!DOCTYPE html>
<html>
<head>
<title>Questions 5305079</title>


</head>
<body>




</body>
</html>

<script>
try{

	debugger;
	
	var html = `
	<a href="#" id="PE" class="portfolio__item filter-projects PE projects" data-id="PE">
		<img class="portfolio__image" src="../img/image-confetti.jpg" alt="Columbia-Wrightsville Bridge Book Cover" />
		<div class="portfolio__clientName">
			<h3 id="PE" class="portfolio__clientName-text">Photo Essays</h3>
		</div> <!-- end portfolio__clientName -->
	</a>
	`;
	
	// there are 2 scenarius:
	//
	// 1) the HTML still doesn't belong to the 'document'. It is still a string in your code.
	//    In that case you can't use:
	//
	//    document.querySelector('.projects');
	//
	//    The way to create many (free: not belonging to the 'document') elements in one shot, avoiding boring DOM manipulation is 
	//    creating an 'template' element.
	// 
	//    see at:	
	// 	  https://stackoverflow.com/questions/494143/creating-a-new-dom-element-from-an-html-string-using-built-in-dom-methods-or-pro
	//    (2nd Answer)
	//
	
	
	/**
	 * @param {String} HTML representing a single element 
	 * (an HTML as complex as you like BUT with only one root element) 
	 * @return {the root Element}
	 */
	function htmlToElement(html) {
		var template = document.createElement('template');
		html = html.trim(); // Never return a text node of whitespace as the result
		template.innerHTML = html;
		return template.content.firstChild;
	}

	// Now, you have a 'live' ancor element.
	var pe = htmlToElement(html);
	
	
	//alert(pe.outerHTML);
	var pe_outerHTML = (pe.outerHTML);
	
	// the element 'pe' is accessible by 'querySelector'.	
	var img = pe.querySelector('img.portfolio__image');
	
	// Please note that 'querySelector' is called from 'pe' and not 'document'
	// That because 'pe' is still a 'free element' (not attached to 'document')
	
	// that prof that 'querySelector' is working
	//alert(img.src);
	var img_src = img.src;
	
	// Now the element 'pe' belongs to the 'document'
	document.querySelector('body').appendChild(pe);
	
	// 'querySelector' can be called from the 'document'
	var h3 = document.querySelector('h3.portfolio__clientName-text');
	
	// that prof that 'document.querySelector' is working
	
	// (later will be clear why this assignment)
	h3.innerText = 'Photo Essays number 1';
	//alert(h3.innerText);
	var h3_innerText = h3.innerText;
	
	// ----------------------------------------------------- //
	// remember an ID must be unique in all the document
	// (having two element with id = 'PE' is wrong)
	// ----------------------------------------------------- //
	
	
	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX //
	
	// 2) In this second scenariuos the HTML is directly inserted into the 'document'
	
	document.querySelector('body').insertAdjacentHTML('beforeend', html);
	
	
	var pe = document.querySelector('a.portfolio__item');
	//alert(pe.outerHTML);
	pe_outerHTML = pe.outerHTML;
	
	// Be aware the element 'pe' we get is the first we added.
	// (In fact:	the text we get is 'Photo Essays number 1')
	var h3 = document.querySelector('h3.portfolio__clientName-text');
	
	// That is why 
	// - ID must be unique
	// - the text for 'h3' was previously changed
	
	// querySelectorAll is the right method to call when there are many elements with the same selector
	
	var elements = document.querySelectorAll('#PE');
	
	
	//alert(elements.length);
	var elements_length = elements.length;

}
catch(jse){
	alert(jse.message);
}

</script>
 
Share this answer
 
v2
Comments
Emily Ryan 12-Jun-21 16:29pm    
I totally understand what you have done here but I am stumped as to how to implement it with my code.

I am playing around with code be for implementing it with my actual code and below is what I have come up with. I don't want to have to create a template for each item. Having a template is great so that I can select the dynamically added content (which is one of my goals). But I don't want to create a template for each item. I would like to create one template and then iterate over an array of objects that will add a bunch of content on the click event.

Is there a way to do this? And all the content on the page should be the same just slightly different content inside. Like different text. (my actual code will have different images and different text inside of the h3 element).

var portfolioGrid = document.querySelector('.portfolio__grid');
var portfolioConetnt = document.querySelector('.portfolio__content');

var html = `
<a href="#" id="PE" class="portfolio__item filter-projects PE projects" data-id="PE">
<img class="portfolio__image" src="../img/image-confetti.jpg" alt="Columbia-Wrightsville Bridge Book Cover" />
<div class="portfolio__clientName">
<h3 id="PE" class="portfolio__clientName-text">Photo Essays</h3>
</div>
</a>
`;

var html2 = `
<a href="#" id="CAHQ" class="portfolio__item filter-projects PE projects" data-id="PE">
<img class="portfolio__image" src="../img/image-confetti.jpg" alt="MAYBE IT WORKED??" />
<div class="portfolio__clientName">
<h3 id="CAHQ" class="portfolio__clientName-text2">THE CAT IN THE HAT</h3>
</div>
</a>
`;

var html3 = `
<a href="#" id="TAFE" class="portfolio__item filter-projects PE projects" data-id="PE">
<img class="portfolio__image" src="../img/image-confetti.jpg" alt="MAYBE IT WORKED??" />
<div class="portfolio__clientName">
<h3 id="TAFE" class="portfolio__clientName-text3">I THINK IT IS WORKING!!!</h3>
</div>
</a>
`;

var html4 = `
<a href="#" id="PWC" class="portfolio__item filter-projects PE projects" data-id="PE">
<img class="portfolio__image" src="../img/image-confetti.jpg" alt="WHO KNOWS??" />
<div class="portfolio__clientName">
<h3 id="PWC" class="portfolio__clientName-text3">YES?!!!</h3>
</div>
</a>
`;

function htmlToElement(html) {
var template = document.createElement('template');
html = html.trim(); // Never return a text node of whitespace as the result
template.innerHTML = html;
return template.content;
}

function newHTML(html2) {
var template2 = document.createElement('template');
html2 = html2.trim(); // Never return a text node of whitespace as the result
template2.innerHTML = html2;
return template2.content;
}

function getIT(html3) {
var template3 = document.createElement('template');
html3 = html3.trim(); // Never return a text node of whitespace as the result
template3.innerHTML = html3;
return template3.content;
}

function ohYeah(html4) {
var template4 = document.createElement('template');
html4 = html4.trim(); // Never return a text node of whitespace as the result
template4.innerHTML = html4;
return template4.content;
}

var pe = htmlToElement(html);
var pet = newHTML(html2);
var met = getIT(html3);
var det = ohYeah(html4);

var img = pe.querySelector('img.portfolio__image');
var h3 = pe.querySelector('h3.portfolio__clientName-text');
var text = pet.querySelector('h3.portfolio__clientName-text3');
var filterProjects = document.querySelectorAll('filter-projects');


var img_src = img.src;

portfolioConetnt.appendChild(pe);
portfolioConetnt.appendChild(pet);

portfolioConetnt.addEventListe
Emily Ryan 12-Jun-21 16:46pm    
And where I have the variables with the html content content I want to be able to use the map function to be able to add the different content from the array of objects. So for the images I could do something like this: ${item.img} and this for adding a specific id to each element ${item.dataId} and ${item.title} for text of the h3 element.

But when I use the map function I can't get it to work with the template. And then have that be used in the click event. ANd in the click event, I want to filter out the array elements to display only certain ones to the HTML page when I click on the items. And and after appending the filtered elements I still want to be able to select them by their class or id.

The filtering should look something like the following unless it should be different to work with with the templates. Or maybe there is a better way entirely.

portfolioGrid.addEventListener('click', function(e) {
let target = e.target;
if(target.className = 'filter-projects') {
var project = document.getElementById('TAFE');

if(filterProjects.id = 'TAFE') {
const projectCategory = projectCards.filter(function(projectItem) {
if(projectItem.category === target.id) {
return projectItem;
}
});

if(target.className === "all") {
displayProjectItems(projectCards);
}
else {
displayProjectItems(projectCategory);
}
} else if(filterProjects.id = 'CAHQ') {
const projectCategory = projectCards.filter(function(projectItem) {
if(projectItem.category === target.id) {
return projectItem;
}
});

if(target.className === "all") {
displayProjectItems(projectCards);
}
else {
displayProjectItems(projectCategory);
}
} else if(filterProjects.id = 'PWC') {
const projectCategory = projectCards.filter(function(projectItem) {
if(projectItem.category === target.id) {
return projectItem;
}
});

if(target.className === "all") {
displayProjectItems(projectCards);
}
else {
displayProjectItems(projectCategory);
}
} else if(filterProjects.id = 'PE') {
const projectCategory = projectCards.filter(function(projectItem) {
if(projectItem.category === target.id) {
return projectItem;
}
});

if(target.className === "all") {
displayProjectItems(projectCards);
}
else {
displayProjectItems(projectCategory);
}
}

}
});

Any help would be appreciated!

Emily Ryan 12-Jun-21 23:24pm    
I've just I have been trying to get filtering, mapping, and templates to work with the dynamic code I want to add and I just can't get it to work.

Here is my js code:


let portfolioHeadingContainer = document.getElementById('portfolio__headingContainer');
let portfolioContentContainer = document.getElementById('portfolio_content_container');

let buttonContainer = document.querySelector('button-container');
let filterProjects = document.querySelectorAll('.filter-projects');
let projectsBtn = document.querySelectorAll('.projects-btn');
let portfolioGrid = document.querySelector('.portfolio__grid');
let portfolioContent = document.querySelector('.portfolio__content');

let cards = 'all';

function cardsFilter() {
cards = this.id;
filterCards(cards);
}

function filterCards(cards) {
filterCards = projectCards.filter( projectItems => {
if (projectItems.id === cards) {
const projectCard = copyProjectCard(projectItems);
portfolioGrid.appendChild(projectCard);
}
});
}

function copyProjectCard(projectItems) {
var template = document.createElement('template');
projectItems = projectItems.trim(); // Never return a text node of whitespace as the result
template.innerHTML = projectItems;

projectItems.map(item => {
const individualProject = `



${item.title}



`;

if ( item !== 'all' ) {
portfolioGrid.innerHTML = "";
portfolioGrid.appendChild(individualProject);
}
});

return template.content;
}

portfolioGrid.addEventListener('click', function(e) {
let target = e.target;

if(target.className = 'filter-projects') {
cardsFilter();
}
});

What am I doing wrong??
Emily Ryan 12-Jun-21 23:29pm    
Where you see ${item.title} above, I am using a map function but for some reason, it won't show up.
If I got you correctly, the code below is an enterely new way to do what you did
(It is still not correct because it is not clear what the {'html'; 'html2'; 'html3'; 'html4'} are and then how they should be managed)

Please note that I added to each root 'ancor' the event 'onclick' to access directly the clicked element, without the need of all the switch you did. I mean, the chain of 'if-then-else' in the function 'portfolioGrid.addEventListener('click', function(e) {...})'.


Cheers

Vincenzo



HTML
<!DOCTYPE html>
<html>
<head>
<title>Questions 5305079</title>


</head>
<body>




</body>
</html>

<script>
try{

	debugger;
	
	// NOTE: the event 'onclick'
	
	var html = `
	<a href="#" onclick="return pe_onclick(this)" id="PE" class="portfolio__item filter-projects PE projects" data-id="PE">
		<img class="portfolio__image" src="../img/image-confetti.jpg" alt="Columbia-Wrightsville Bridge Book Cover" />
		<div class="portfolio__clientName">
			<h3 id="PE" class="portfolio__clientName-text">Photo Essays</h3>
		</div> <!-- end portfolio__clientName -->
	</a>
	`;
	
	
	var html2 = `
	<a href="#" onclick="return cahq_onclick(this)" id="CAHQ" class="portfolio__item filter-projects PE projects" data-id="PE">
		<img class="portfolio__image" src="../img/image-confetti.jpg" alt="MAYBE IT WORKED??" />
		<div class="portfolio__clientName">
			<h3 id="CAHQ" class="portfolio__clientName-text2">THE CAT IN THE HAT</h3>
		</div>
	</a>
	`;

	var html3 = `
	<a href="#" onclick="return tafe_onclick(this)" id="TAFE" class="portfolio__item filter-projects PE projects" data-id="PE">
		<img class="portfolio__image" src="../img/image-confetti.jpg" alt="MAYBE IT WORKED??" />
		<div class="portfolio__clientName">
			<h3 id="TAFE" class="portfolio__clientName-text3">I THINK IT IS WORKING!!!</h3>
		</div>
	</a>
	`;

	var html4 = `
	<a href="#" onclick="return pwc_onclick(this)" id="PWC" class="portfolio__item filter-projects PE projects" data-id="PE">
		<img class="portfolio__image" src="../img/image-confetti.jpg" alt="WHO KNOWS??" />
		<div class="portfolio__clientName">
			<h3 id="PWC" class="portfolio__clientName-text3">YES?!!!</h3>
		</div>
	</a>
	`;

	
	
	/**
	 * @param {String} HTML representing a single element 
	 * (an HTML as complex as you like BUT with only one root element) 
	 * @return {the root Element}
	 */
	function htmlToElement(html) {
		var template = document.createElement('template');
		html = html.trim(); // Never return a text node of whitespace as the result
		template.innerHTML = html;
		return template.content.firstChild;
	}

	
	// (DRY: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself)
	// the functions: {newHTML; getIT; ohYeah} are not needed.	
	
	var pe = htmlToElement(html);
	var pet = htmlToElement(html2);
	var met = htmlToElement(html3);
	var det = htmlToElement(html4);
	
	
	function pe_onclick(pe){
	
		// Do what you want to the 'ancor' element 'pe'.
	
		// returning 'false' the event 'onclick' is cancelled.
		return false;
	}
	
	function cahq_onclick(cahq){
	
		// Do what you want to the 'ancor' element 'cahq'.
	
		// returning 'false' the event 'onclick' is cancelled.
		return false;
	}	
	
	function tafe_onclick(tafe){
	
		// Do what you want to the 'ancor' element 'tafe'.
	
		// returning 'false' the event 'onclick' is cancelled.
		return false;
	}	
	
	function pwc_onclick(pwc){
	
		// Do what you want to the 'ancor' element 'pwc'.
	
		// returning 'false' the event 'onclick' is cancelled.
		return false;
	}	
	

}
catch(jse){
	alert(jse.message);
}

</script>
 
Share this answer
 
Comments
Emily Ryan 13-Jun-21 6:56am    
Thank you so much and sorry for the confusion. I had the different {‘html’; ‘html2’; ‘html3’; ‘html4’;} because I wasn’t sure how do do everything with creating a single template. That is just code I am trying out to see how I can get what I want to work.

To make things easier I put my code into codepe. Here is the link: https://codepen.io/emilyray2720/pen/KKWrNPq

If you follow the link you can see what I have done originally which works. It is different then the code I showed above because I was just playing around to try to get something to work. Also, the original JS code which I am showing in my codepen, I am just filtering the cards and adding the new cards dynamically. Note that I am just using the same image for each card as a template. Each card will have a different image.

My original problem which lead me to try everything I can think of is that what I want to do is after I click on one of the project cards and bring up other items (all looking similar), I want to be able to click on that item and again bring up dynamically added code (and that code would again be filtered from another array of objects).

But the way I have it doesn't allow me to select a specific class that is added dynamically with the HTML code.

How would I change the code to get templates working with it? Or is there another way entirely to allow me to access the dynamically added class on the dynamically added HTML so that I can add a click event to each one.

Thank you so much for your help!

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900