I have two similar elements to replace:
Replace:
<div class="radio__label content-box__emphasis ">
<label class="radio__label__primary" for="checkout_payment_gateway_1">
Payment 1
</label>
</div>
with:
<div class="radio__label content-box__emphasis payment-method-wrapper">
<label class="radio__label__primary" for="checkout_payment_gateway_1">
<img alt="Payment 1" class="offsite-payment-gateway-logo" src="payment-1.png">
<span class="visually-hidden">
Payment 1
</span>
</label>
</div>
And analogously for a Payment 2.
I want to do this without jQuery.
I don't want to add payment-method-wrapper to all <div class="radio__label content-box__emphasis "> on the site, but only for Payment 1 and Payment 2.
What would be the best approach to do this?
Moti Korets beat me to it, but here's a more complete solution:
function transformLabelContainer(id, imgSrc) {
const label = document.querySelector(`label[for$=_gateway_${id}]`);
const labelDiv = label.parentElement;
labelDiv.className += 'payment-method-wrapper';
const originalText = label.innerText;
const span = document.createElement('span');
span.className = 'visually-hidden';
span.innerText = originalText;
const img = document.createElement('img');
img.alt = originalText;
img.className = 'offsite-payment-gateway-logo';
img.src = imgSrc;
label.replaceChild(img, label.firstChild);
label.appendChild(span);
}
const labelsToTransform = [
{
id: '1',
imgSrc: 'payment-1.png'
},
{
id: '2',
imgSrc: 'payment-2.png'
}
];
labelsToTransform.forEach(label => transformLabelContainer(label.id, label.imgSrc));
To query for the label object you can run
let label_payment1 = document.querySelector('.payment-method-wrapper>label[for$=_gateway_1]')
Then create the inner node with
let img = document.createElement("img")
img.src = "payment-1.jpg"
// .. set other image attributes
let span = document.createElement("span")
span.class = "visually-hidden"
let textContent = document.createTextNode("Payment 1");
span.appendChild(textContent)
And lastly add created nodes to label
label_payment1.replaceChild(img,label_payment1.firstChild)
label_payment1.appendChild(span)
Edit: This is just a sketch. See #Kevin Huang answer for complete solution.
Related
I'm trying to add a new item on an array that displays in a container on HTML. It's supposed to get an image file and an url from input, check the selected option and push each one to its specified array
Here is the relevant html:
<input type="file" name="comic-cover" id="comic-cover" required />
<input type="url" name="comic-url" id="comic-url" placeholder="url" required />
<select name="publisher-list" id="publisher">
<option value="publisher" disabled>Publisher</option>
<option value="dc">DC Comics</option>
<option value="marvel">Marvel</option>
</select>
<button type="submit" class="btn-submit">Add</button>
<h2 class="comic-publisher">DC Comics</h2>
<div class="dc" id="block-dc">
</div>
<h2 class="comic-publisher">Marvel</h2>
<div class="marvel" id="block-marvel">
</div>
and the js:
var comicCovers = ["imgs/Dark Knights of Steel-000.jpg", "imgs/Hawkeye-000.jpg"]
var trailers = ["https://www.youtube.com/watch?v=WspmgrmEgn4", "https://www.youtube.com/watch?v=ssj0P0uY08U"]
var publishers = [0, 1];
var i = 0;
var blockDC = document.querySelector("#block-dc");
var blockMarvel = document.querySelector("#block-marvel");
render()
var publisher = document.querySelector("select").value;
document.querySelector("select") = function renderPublisher() {
publisher = document.querySelector("select").value;
return publisher;
}
// add new comics to the list //
document.querySelector(".btn-submit") = function addOnList() {
var newCover = document.querySelector("#comic-cover");
var newTrailer = document.querySelector("#comic-url");
if (newCover.endsWith(".jpg") & newTrailer.startsWith("https://")) {
if (publisher == "dc") {
publisher.push(0);
} else {
publisher.push(1)
}
comicCovers.push(newCover.value);
trailers.push(newTrailer.value);
newCover.value = "";
newTrailer.value = "";
render()
}
}
function render() {
for (i; i < comicCovers.length; i++) {
// creates the comic covers using js var //
var comicCover = document.createElement("img");
comicCover.setAttribute("src", `${comicCovers[i]}`);
// creates trailer button //
var trailerButton = document.createElement("button");
// //
var container = document.createElement("div");
container.setAttribute("class", "container");
container.appendChild(trailerButton);
container.appendChild(comicCover);
blockDC.appendChild(container);
trailerButton.appendChild(document.createTextNode("Trailer"));
trailerButton.setAttribute("class", "trailer-button");
trailerButton.setAttribute("onclick", `openTrailer("${trailers[i]}")`);
if (publishers[i] == 0) {
blockDC.appendChild(container);
} else {
blockMarvel.appendChild(container);
}
}
}
I tried to use if (newCover != "" & newTrailer != "") and even changed the input type from file to url. What am I doing wrong?
You're tring to push values inside the variable called publisher which is just a string and not an array.
var publisher = document.querySelector("select").value;
publisher.push(0);
You're assigning a function to an html element by writing:
document.querySelector("select") = function renderPublisher() {
publisher = document.querySelector("select").value;
return publisher;
}
which is not correct.
Also The variables newCover and newTrailer are not strings.
var newCover = document.querySelector("#comic-cover");
var newTrailer = document.querySelector("#comic-url");
so you can't use the methods startsWith and endsWith for them, you have to access the elements' value instead. In order to get the name of the selected file you need to use the input tag's files attribute which returns an array of the selected files, we want the first file so we'll get the first item's name attribute.
var newCover = document.querySelector("#comic-cover").value;
var newTrailer = document.querySelector("#comic-url")?.files[0]?.name;
Overall first you need to grab the values provided inside the inputs, then push them inside the array related to the selected block(Marvel or DC), then you simply need to create the img, video and button tags and append them as a child to the selected block.
Url and file type validation should be handled also on both server and client side and it's gotta be much more than just checking if the fileName ends with ".jpg" or the url starts with "https://".
You need to store the uploaded image and video somewhere on the server. In order to do so, you'll have to attach an eventListener to the input tags so that whenever a file is selected, you'll send an upload request to the server, there you'll check if the file is valid and if so, you'll store the file somewhere on the server, then you'll pass the url of the uploaded photo/video as src to the img/video tags.
const publishers = {
dc: {
comicCovers: [],
trailers: []
},
marvel: {
comicCovers: [],
trailers: []
}
}
const blockDC = document.querySelector("#block-dc");
const blockMarvel = document.querySelector("#block-marvel");
const blocksDivs = {
dc: blockDC,
marvel: blockMarvel
};
const addButton = document.querySelector(".btn-submit");
// add new comics to the list //
addButton.addEventListener("click", () => {
const publisher = document.querySelector("#publisher").value;
const newCoverFileName = document.querySelector("#comic-cover")?.files[0]?.name;
const newTrailerUrl = document.querySelector("#comic-url").value;
if (newCoverFileName?.endsWith(".jpg") && newTrailerUrl?.startsWith("https://")) {
publishers[publisher].comicCovers.push(newCoverFileName);
publishers[publisher].trailers.push(newTrailerUrl);
var container = document.createElement("div");
var coverImg = document.createElement("img");
var playTrailerButton = document.createElement("button");
playTrailerButton.innerHTML = "play trailer"
coverImg.src = "http://yourserveraddress/imgs/" + newCoverFileName;
//test photo
coverImg.src = "https://picsum.photos/200/300"
container.appendChild(coverImg);
container.appendChild(playTrailerButton);
blocksDivs[publisher].appendChild(container);
playTrailerButton.setAttribute("class", "trailer-button");
playTrailerButton.addEventListener("click", () => {
var videoExists = document.getElementById(publishers[publisher].trailers.length)
if (!videoExists) {
var video = document.createElement('video');
video.setAttribute("id", publishers[publisher].trailers.length)
video.style.backgroundColor = "aliceblue"
video.setAttribute("src", newTrailerUrl);
video.setAttribute("width", "200")
video.setAttribute("height", "200")
container.appendChild(video);
playTrailerButton.innerHTML = "close Trailer"
} else {
container.removeChild(videoExists)
playTrailerButton.innerHTML = "play Trailer"
}
})
}
})
<input type="file" name="comic-cover" id="comic-cover" required />
<input type="url" name="comic-url" id="comic-url" placeholder="url" required />
<select name="publisher-list" id="publisher">
<option value="publisher" disabled>Publisher</option>
<option value="dc">DC Comics</option>
<option value="marvel">Marvel</option>
</select>
<button type="submit" class="btn-submit">Add</button>
<h2 class="comic-publisher">DC Comics</h2>
<div class="dc" id="block-dc">
</div>
<h2 class="comic-publisher">Marvel</h2>
<div class="marvel" id="block-marvel">
</div>
I am trying to make multiple comment boxes for a website. The first click to create the text area and submit button works fine. However when I click another it appends the text area and submit button twice - creating two text areas and submit buttons and then three and so on..
If you go to https://alexpd93.github.io/FAC-Website/ and click on various comment buttons it should make more sense.
I would like it so that each time I click on the comment button, it only creates one text area for each section.
How can I fix this?
function addComment(element) {
const boxContainer = element.parentNode.parentNode.parentNode;
const commentContainer = element.parentNode;
commentContainer.classList.add("comment-container-after-click");
const commentBox = document.createElement("textarea");
commentBox.classList.add("comment-box-after-click");
commentBox.placeholder = "What are your thoughts?";
commentBox.innerHTML = "";
const submitComment = document.createElement("button");
submitComment.classList.add("submit-comment-after-click");
submitComment.innerHTML = "Comment";
commentContainer.append(commentBox, submitComment);
submitComment.onclick = function submitComment() {
let comment = commentBox.value;
const newComments = document.createElement("p");
boxContainer.appendChild(newComments);
newComments.innerHTML = `${comment}`;
commentBox.value = "";
};
}
function comment(event) {
const commentButton = event.target;
commentButton.style.display = "none";
const commentIcon = document.getElementsByClassName("comment-icon");
const iconArray = Array.from(commentIcon);
iconArray.forEach((icon) => {
if (icon.nextElementSibling.style.display === "none") {
icon.style.display = "none";
addComment(commentButton);
}
});
}
<div class="comments-container" id="comments">
<img id="commentIcon" class="comment-icon" src="Images/Comment.png" alt="comment icon">
<button class="comment-button" id="commentButton" onclick="comment(event)" > Comment </button>
</div>
After playing around with the website I believe I understood the problem. You are calling addComment() for all elements with class comment-icon. And due to your (tbh) little bit weird handling of addComment() this adds a comment to every single one of this elements.
I've corrected the error below and marked my changes with comments.
function addComment(element) {
const boxContainer = element.parentNode.parentNode.parentNode;
const commentContainer = element.parentNode;
commentContainer.classList.add("comment-container-after-click");
const commentBox = document.createElement("textarea");
commentBox.classList.add("comment-box-after-click");
commentBox.placeholder = "What are your thoughts?";
commentBox.innerHTML = "";
const submitComment = document.createElement("button");
submitComment.classList.add("submit-comment-after-click");
submitComment.innerHTML = "Comment";
commentContainer.append(commentBox, submitComment);
submitComment.onclick = function submitComment() {
let comment = commentBox.value;
const newComments = document.createElement("p");
// why append to the box container? Then all comments from different textareas will be merged
// boxContainer.appendChild(newComments);
commentContainer.appendChild(newComments);
newComments.innerHTML = `${comment}`;
commentBox.value = "";
};
}
function comment(event) {
const commentButton = event.target;
commentButton.style.display = "none";
const commentIcon = document.getElementsByClassName("comment-icon");
// add the comment once not to every comment box available
addComment(commentButton);
const iconArray = Array.from(commentIcon);
iconArray.forEach((icon) => {
if (icon.nextElementSibling.style.display === "none") {
icon.style.display = "none";
// this causes that the comment is added multiple times
// addComment(commentButton);
}
});
}
<div div="all-comments">
<div class="comments-container" id="comments">
<img id="commentIcon" class="comment-icon" src="https://dummyimage.com/100x100/000/fff" alt="comment icon">
<button class="comment-button" id="commentButton" onclick="comment(event)"> Comment </button>
</div>
<div class="comments-container" id="comments">
<img id="commentIcon" class="comment-icon" src="https://dummyimage.com/100x100/000/fff" alt="comment icon">
<button class="comment-button" id="commentButton" onclick="comment(event)"> Comment </button>
</div>
<div class="comments-container" id="comments">
<img id="commentIcon" class="comment-icon" src="https://dummyimage.com/100x100/000/fff" alt="comment icon">
<button class="comment-button" id="commentButton" onclick="comment(event)"> Comment </button>
</div>
</div>
I'm making a chat and I want to add an avatar pics feature so I figured it might work well with span, but the problem is I don't know how to add the span to the element.
let avatar = document.createElement("span");
let userMessage = document.createElement("H3");
avatar.setAttribute(userMessage);
userMessage.innerHTML = username + ": " + message;
//document.getElementById("chat").appendChild(avatar);
document.getElementById("chat").appendChild(userMessage);
userMessage.style.background = color;
userMessage.style.textAlign = "left";
document.getElementById("msg").value = "";
I am assuming that you have div with id="chat" and you want to append an h3 tag in a span and then append the chat div so your code will look like this
var username="zulqarnain jalil";
var message ="welcome back, have a nice day";
var color='lightgrey';
var avatar = document.createElement("span");
var userMessage = document.createElement("h3");
userMessage.innerHTML = username + ": " + message;
userMessage.style.background = color;
userMessage.style.textAlign = "left";
avatar.appendChild(userMessage);
document.getElementById("chat").appendChild(avatar);
//document.getElementById("msg").value = "";
<div id="chat">
</div>
I have created a chatbot snippet for you, here you can test it
var username="zulqarnain jalil";
function sendMessage()
{
var message =document.getElementById('messagebox').value;
if(message)
{
document.getElementById('messagebox').value='';
var color='lightgrey';
var avatar = document.createElement("span");
var userMessage = document.createElement("h3");
userMessage.innerHTML = username + ": " + message;
userMessage.style.background = color;
userMessage.style.textAlign = "left";
avatar.appendChild(userMessage);
document.getElementById("chat").appendChild(avatar);
}
else
{
// message empty
}
}
//document.getElementById("msg").value = "";
<div id="chatBox">
<div id="chat">
</div>
<div>
<input type="text" id="messagebox" />
<input type="button" onclick="sendMessage()" value="Send" />
</div>
</div>
First you need to add the span as a child of the H3 element.
I think the best approach to this problem is creating a class Message. Initializing that class creates h3 and span with unique ids stored in a variable id for future use. The class will also add the h3 as a child of it's parent element ( what ever it is ), and the span as a child of the h3 element.
var counterText = 0;
var counterAvatar = 0;
class UserMessage {
constructor(msgTxt, avatar){
// This block initializes the text message of the user
// It will also add an id to the tag for future use
let msgTxt = document.createTextNode(msgTxt);
this.messageID = 'text' + counterText;
this.message = document.createElement('h3');
this.message.appendChild(msgTxt);
this.message.setAttribute('id', this.messageID);
counterText++;
// This block creates an img element with the attributes src and id
this.avatarID = 'avatar' + counterAvatar;
this.avatar = document.createElement('img');
this.avatar.setAttribute('src', avatar);
this.avatar.setAttribute('id', this.avatarID);
counterAvatar++;
// This block appends the avatar element to the text and the text to the
// chat div.
let chat = document.getElementById('chat');
this.message.appendChild(this.avatar);
chat.appendChild(this.message);
}
}
to initialize a new instance:
var message = new UserMessage("Hello, this is a text message!",'<path/to/avatar>')
this is an object oriented aproach.
you could also just append the avatar to the message and the message to the chat.
But I think aproaching the problem in an object oriented way is much better since it will save time in the future when you're updating your app.
Markdown works fine in here.
Block-level HTML elements have a few restrictions:
They must be separated from surrounding text by blank lines.
The begin and end tags of the outermost block element must not be indented.
Markdown can't be used within HTML blocks.
So I have the following function vanilla JS code:
function get_menu(menu_id) {
wp.api.loadPromise.done(function() {
const menus = wp.api.collections.Posts.extend({
url: wpApiSettings.root + 'menus/v1/menus/' + menu_id,
});
const Menus = new menus();
Menus.fetch().then(
posts => {
let post_list = posts.items;
console.log(post_list);
});
});
}
get_menu(4);
This gives me a object of objects as shown below:
What is the best way to loop through these objects and render HTML within? So let's say I want to loop through each object and grab the post_title and output HTML <div> + post_title + </div>.
All help would be appreciated!
Update:
Need to render this in a loop:
<div class="column is-one-third is-flex py-0">
<a href=" ***url*** " class="dropdown-item px-2 is-flex is-align-items-center">
<figure class="image is-32x32 is-flex">
<img src=" ***image*** + ***post_title*** '.svg'; ?>">
</figure>
<span class="pl-2"><?= ***post_title*** ?></span>
</a>
</div>
You can iterate through the array and create a dom tree
function get_menu(menu_id) {
wp.api.loadPromise.done(function () {
const menus = wp.api.collections.Posts.extend({
url: wpApiSettings.root + 'menus/v1/menus/' + menu_id,
});
const Menus = new menus();
Menus
.fetch()
.then(posts => {
let post_list = posts.items;
// Map through response data and turn objects into elements
const postElements = post_list.map(createDomTree)
// spread all elements from array into arguments for the append method
document.body.append(...postElements)
});
});
}
function createDomTree(post) {
// I'm not sure if these values are available in the response data, but can be replaced
const { post_url, post_title, post_image } = post
const container = document.createElement('div')
container.className = 'column is-one-third is-flex py-0'
const anchor = document.createElement('a')
anchor.href = post_url
anchor.className = 'dropdown-item px-2 is-flex is-align-items-center'
const figure = document.createElement('figure')
figure.className = 'image is-32x32 is-flex'
const img = document.createElement('img')
img.src = `${post_image}${post_title}.svg`
const span = document.createElement('span')
span.className = 'pl-2'
span.textContent = post_title
figure.appendChild(img)
anchor.append(figure, span)
container.appendChild(anchor)
return container
}
get_menu(4);
<div id = "first"></div>
<div id = "second"></div>
<div id = "third"></div>
Trying to dynamically append to these divs within a javascript function
if (condition??) variable = "first"
else if (condition) variable = "second"
else variable = "third"
let div = document.getElementById(variable);
Variable above could be first, second, or third depending on some condition.
div.appendChild(img)
div.appendChild(text)
However since div ends up being null I wont be able to append anything to it cuz of the following error.
Cannot read property 'appendChild' of null.
So any idea how to go around this?
Edit: Added the actual code snip below
// my html
<div id = "list" class = "center"> Listing <br><br>
<div id = "first"></div>
<div id = "second"></div>
<div id = "third"></div>
</div>
//my js
$.ajax({
async: true,
type: 'GET',
url: "rankings/" + boss + ".txt",
boss : boss,
success: function(sData){
let temp = this.boss
let div = document.getElementById(temp);
//console.log(temp) logs "first" on the console
let img = document.createElement("img");
let text = document.createElement('td1');
img.src = "images/" + temp + ".jpg";
img.alt = temp
div.appendChild(img)
div.appendChild(text)
Edit 2: Image of code exection
https://imgur.com/a/WkYTn
Your code looks correct! However, I think the problem is, you are loading the Javascript before the DOM is being rendered (Eg. you are loading the script in <head>). So javascript is not able to find the divs..
var variable= "second";
var boss = "boss_baby";
let div = document.getElementById(variable);
console.log(variable) //logs "first" on the console
let img = document.createElement("img");
let text = document.createElement('td1');
img.src = "images/" + boss + ".jpg";
img.alt = boss
div.appendChild(img)
div.appendChild(text)
#first, #second, #third {
display:block;
border: 1px solid blue;
height: 50px;
width: 200px;
margin : 10px;
}
<div id = "list" class = "center"> Listing <br><br>
<div id = "first"></div>
<div id = "second"></div>
<div id = "third"></div>
</div>
To solve this issue you can either load the <script> right before the body tag ends </body>
or add all your script in onload methods
window.onload = function() {
// all your JS code goes here
}
OR
document.onload = function() {
// all your JS code goes here
}
After question EDIT:
Your code looks correct! However, Now i suspect the element you are trying to access might have been removed by the time you get your response from ajax.
Try this Code Pen Example . I think your problem is with the "variable" and how your assigning value to it.
<div id = "first" style="border:1px red solid"></div>
<div id = "second" style="border:blue"></div>
<div id = "third" style="border:green"></div>
<script>
//try changing the value of condtion
var condition=3;
var variable="";
var img = document.createElement("img");
//var text = document.createElement('td1');
img.src = "https://www.w3schools.com/w3images/fjords.jpg";
img.alt = "boss"
//div.appendChild(img)
if (condition==1) variable = "first"
else if (condition==2) variable = "second"
else variable = "third"
console.log(img)
document.getElementById(variable).appendChild(img);
<script>