For loop not working as expected with string replacement - javascript

I am trying to make a javascript webextension that adds a couple of numbers eg. "123" to the end of the inner text of a hyperlink text to each product on a shopping website, eg. http://www.tomleemusic.ca
For example, if I go to this link, http://tomleemusic.ca/catalogsearch/result/?cat=0&q=piano
I want to add the item's identification number to the end of the product's name.
name of product and the href tag are in its item link, tomleemusic.ca/xxxxxx with the x's being the item number
However with my following code, I simply append the item number of the first item in the list to every item, instead of a different item number for each item.
var productsListLink = document.querySelectorAll(".products-grid .item .product-name a:not(.product-image)");
for (var i = 0; i < productsListLink.length; i++) {
var a = productsListLink[i];
var name = a.innerHTML || "";
var addon = document.querySelector(".products-grid .item .product-name a:not(.product-image)").getAttribute('href');
var newaddon = addon.replace("http://tomleemusic.ca/","");
name += newaddon;
a.innerHTML = name;
a.setAttribute('title', name);
}

In this line, you're grabbing only the first matching element:
var addon = document.querySelector(".products-grid .item .product-name a:not(.product-image)").getAttribute('href')
You already have the element you're actually working with in each loop iteration in a; just use that instead:
var addon = a.getAttribute('href')
Example:
var productsListLink = document.querySelectorAll(".products-grid .item .product-name a:not(.product-image)");
for (var i = 0; i < productsListLink.length; i++) {
var a = productsListLink[i];
var name = a.innerHTML || "";
var addon = a.getAttribute('href');
var newaddon = addon.replace("http://tomleemusic.ca/","");
name += newaddon;
a.innerHTML = name;
a.setAttribute('title', name);
}
<div class="products-grid">
<div class="item">
<span class="product-name">
</span>
</div>
<div class="item">
<span class="product-name">
</span>
</div>
<div class="item">
<span class="product-name">
</span>
</div>
</div>

querySelector will always return the first matching element. Thus, when you do
var addon = document.querySelector(".products-grid .item .product-name a:not(.product-image)").getAttribute('href');
you're selecting the first a (the one you get in your first iteration).
But, you can make the code a whole lot cleaner by using array methods and a regular expression to match the id:
Array.prototype.forEach.call(
document.querySelectorAll(".products-grid .item .product-name a:not(.product-image)"),
(productNameElement) => {
const idMatch = productNameElement.href.match(/\d+$/);
if (idMatch) productNameElement.appendChild(document.createTextNode(idMatch[0]));
});
Also note that only some of the elements have an ID number. For example, one of the search results:
BENCHWORLD SONATA 1c Single Adjustable Artist <span class="searchindex-highlight">Piano</span> Bench In Polished Walnut
doesn't have one, so it would be good to check that there's a match first.

Related

Appending multiple templates to a DOM

Java Script
const cardDropdownTemplate = document.querySelector('[Card-Dropdown-Template]');
const cardDropdownContainer = document.querySelector('[card-dropdown-container]');
SearchBoxMainNav.addEventListener('input', async(event) =>{
var input = document.getElementById('SearchTerm').value;
const card = cardDropdownTemplate.content.cloneNode(true).children[0];
cardDropdownContainer.innerHTML = "";
if (input != "") {
var result = cardSearch(input);
console.log(result);
for (var i = 3; i > -1; i--) {
const name = card.querySelector("[Ygo-Card-Name]");
const desc = card.querySelector("[Ygo-Card-Desc]");
name.textContent = result[i].name;
desc.textContent = result[i].desc;
cardDropdownContainer.append(card);
}
console.log(card);
console.log(result);
}
})
Html
<div class="dropdown-Content" card-dropdown-container ></div>
<template Card-Dropdown-Template>
<div class="card-dropdown">
<div class="card-name" Ygo-Card-Name></div>
<div class="card-description" Ygo-Card-Desc></div>
</div>
</template>
So I have this code, when it executes it listens for an input then sends that input into my api searcher, it searches the api for the 4 most simmilar listings in name then Uses a dom template to create a box and puts all the information in it so i can make a Dropdown.
My problem is currently it is only creating one Box and is just overwriting the information in that one box instead of making multiple boxes. Am I just using append wrong or what? When i watch it in slow mo The data gets overwriitten even before the append is reached in the code, so maybe its just drawing the template in real time after the append and the append only makes a new box that first time then does nothing the rest of the times?

Text highlighting in Javascript

I have a text in the following format:
<div id="text">
<div>Hello world</div>
<div>How are you</div>
</div>
User selects the "w" of world:
So I get the selection and can insert the span:
var selection = window.getSelection();
var startNode = $(selection.anchorNode.parentElement);
var endNode = $(selection.focusNode.parentElement);
var startIndex = startNode.index();
var endIndex = endNode.index();
var startOffset = selection.anchorOffset;
var endOffset = selection.focusOffset;
Result:
<div id="test">
<div>Hello <span class="id1">w</span>orld</div>
<div>How are you</div>
</div>
id1.startNode = 0
id1.endNode = 0
id1.startOffset = 6
id1.endOffset = 7
The user user now selects "d Ho" of the text. This is how it should look like:
<div id="test">
<div>Hello <span class="id1">w</span>orl<span class="id2">d</span></div>
<div><span class="id2">Ho</span>w are you</div>
</div>
id2.startNode = 0
id2.endNode = 1
id2.startOffset = 9
id2.endOffset = 2
But using the method from before selection.anchorOffset yields me the index relative to the new created text nodes which are now "Hello ", "w" and "orld" which means I get an index of 3 instead of 10.
I use the following code to get the offset that was created:
var offs = 0;
if(startNode.context.innerHTML.length > startNode.context.innerText.length) {
var n = startNode.context.childNodes;
for (var i = 0; i < n.length; i++) {
if(n[i].textContent === selection.anchorNode.textContent) {
break;
}
else {
offs += n[i].textContent.length;
}
}
}
console.log(offs);
I can now add the offset to the start and end but this kind of breaks when the selection goes over different nodes or contains a selection inside of it. I also have no idea how to generate the spans with those offsets. There are also way to many cases that I have to consider like startNode === endNode or if a selection contains a span fully or partly.
Are there any common approaches or frameworks for stuff like this? I would rather just work with pure text indices instead of html tags inside and let something else handle the proper formatting.
At the risk of self-promotion, my Rangy library has a Class Applier module specifically for this.

Simple ToDo-List doesn't work

My ToDo List dont wanna work the way i want. I've just been working with JavaScript for 2 weeks sthis is very new to me, therefor the code maybe doesnt look that nice.
The result comes out wrong. If I type in "buy food" the first line gonna show just that, but the next time I wanna add "walk the dog", then it displays
buy food
buy food
walk the dog
I hope you understand my problem. It also ends the unordered list tag after the first click and adds the rest of the things in another.
Here's the JavaScript:
var taskList = [];
var text = "<ul>"
function addToList() {
var task = document.getElementById("toDoTask").value;
taskList.push(task);
for(i = 0; i < taskList.length; i++){
text += "<li>" + taskList[i] + "</li>" ;
}
text += "</ul>";
document.getElementById("todoList").innerHTML = text;
}
The issue is you're closing the ul tag after adding each item. Instead of concatenating raw HTML, consider using element objects and appending, and using a text node object to handle the user input - this removes the possibility of a DOM Based XSS vulnerability.
window.onload = function() {
var taskList = [];
var container = document.getElementById("todoList");
document.getElementById("add").onclick = addToList;
function addToList() {
var task = document.getElementById("toDoTask").value;
taskList.push(task);
var ul = document.createElement('ul');
var li;
for (i = 0; i < taskList.length; i++) {
li = document.createElement('li');
li.appendChild(document.createTextNode(taskList[i]))
ul.appendChild(li);
}
container.innerHTML = '';
container.appendChild(ul);
}
};
Task:
<input id="toDoTask" /> <input type="button" id="add" value="Add" />
<div id="todoList">
</div>
You should not use the innerHtml. This replace all the text of your content. You should just add the li to your ul.
You can do that by using the append function by jquery Append
your <ul> must contain an id like this <ul id="toDoList">
then you make $("#toDoList").append("yourTask");
yourTask must contains the li.
With this, you don't need to iterate on all your element list
Not sure, but you seem to keep adding to text the second time, so text will be something like <ul><li>buy food</li></ul><li>buy food</li><li>walk the dog</li></ul>, which is invalid HTML by the way, but gets outputted anyway...
On each call of function addToList() you should reset the variable text.
For example:
function addToList() {
var task = document.getElementById("toDoTask").value;
taskList.push(task);
text="";
for(i = 0; i < taskList.length; i++){
text += "<li>" + taskList[i] + "</li>" ;
}
text += "</ul>";
document.getElementById("todoList").innerHTML = text;
}
The whole list of items in array will appends to variable text on each call.

Get all the divs after a div and decrease by one their attribute

I have some divs that have an attribute: rowNumber.
one of them has an attribute: isOpen=1.
All the others that are after it have isOpen=0.
for example:
<div class="statusCell" isOpen="0" rowNumber="1">
I want to get all of the divs with class statusCell that are found after the div with isOpen=1, and decrease by one their rowNumber.
I need to do something like:
$('.statusCell[isOpen=1]').nextAll('.statusCell[isOpen=0]').each(function() {
var rowNumber = $(this).attr("rowNumber");
var newRowNumber = parseInt(rowNumber, 10) - 1;
$(this).attr('rowNumber', newRowNumber.toString());
});
but it doesn't work because:
$('.statusCell[isOpen=1]').nextAll('.statusCell[isOpen=0]').length
is zero..
a sample code is:
<td class="DesignedTableTD">
<div class="statusCell" style="cursor:pointer;" isOpen="0" rowNumber= "1">
<p style="display:inline;" class="yellow" title="fd">
<img alt="Active" src="#Url.Content("~/Images/play.png")" class="help"/>
</p>▽
</div>
</td>
any help appreciated!
nextAll selects next matching siblings of the selected element which is not the case here, you can use the index() method:
var $statusCell = $('.statusCell[isOpen]'),
$open = $statusCell.filter('[isOpen=1]'),
i = $statusCell.index($open);
$statusCell.filter(':gt('+i+')').foo();
// Decreasing attributes' value
// $statusCell.slice(++i).attr('rowNumber', function(_, value) {
// return +value - 1;
// });
Note that isOpen and rowNumber are not valid attributes, if the Doctype of the page is HTML5 you can use data-* attributes instead.
Alternatively...
var oStatCell = querySelectorAll(".statusCell[isOpen=1]");
for(i = 0; i < oStatCell.length; i++) {
var cStatCell = oStatCell[i].querySelectorAll(".statusCell[isOpen=0]");
for(j = 0; j < cStatCell.length; j++) {
cStatCell[j].setAttribute("rowNumber", String(parseInt(cStatCell[j].getAttribute("rowNumber")) - 1));
}
}
Not to say this is a better option, but I think it's nice to have a non-library-based option available.
querySelectorAll() documentation: https://developer.mozilla.org/en-US/docs/Web/API/Document.querySelectorAll

Overwriting <div id> with a variable in javascript

Im developing a prototype mobile web app that mimics a shopping cart. Ive been able to create the shopping cart and add products to the cart. The trouble im having is overwriting the total cost value. Im able to convert the values in order to perform the calculation but im unable to overwrite the value where the total value is stored. Here is the code ive got to far:
<div data-role="content">
<div class="ui-grid-b">
<div class="ui-block-a">Title</div>
<div class="ui-block-b">Format</div>
<div class="ui-block-c">Price</div>
<div class="ui-block-a"><p id = "myTitle"></p></div>
<div class="ui-block-b"><p id = "myFormat"></p></div>
<div class="ui-block-c"><p id = "myPrice"></p></div>
<div class="ui-block-a"></div>
<div class="ui-block-b"></div>
<div class="ui-block-c"><p id="myTotal"></p></div>
</div>
</div>
Now i wish to update the value stored in the "myTotal" id. I have a function called addtocart() and here is the code for that:
`
var total = 0;
function addtocart() {
var title = game.Title ;
var price = game.Price ;
var format = game.Format ;
var newParagraph = document.createElement('p');
newParagraph.textContent = title;
var newParagraph2 = document.createElement('p');
newParagraph2.textContent = format;
var newParagraph3 = document.createElement('p');
newParagraph3.textContent = price;
document.getElementById("myTitle").appendChild(newParagraph);
document.getElementById("myFormat").appendChild(newParagraph2);
document.getElementById("myPrice").appendChild(newParagraph3);
price = parseInt(price, 10);
price = total + price;
var newParagraph4 = document.createElement('p');
newParagraph4.textContent = total;
document.getElementById("myTotal").appendChild(newParagraph4);
}
`
Now i know the appendChild is for adding text to a document but I can seem to find a solution to overwrite the value with the new value stored in 'total' when a new item is added.
Thanks in advance :)
Use the innerHTML property. This will set the HTML inside the element to whatever you set, rather than adding a node.
var newParagraph4 = "<p>" + total + "</p>";
document.getElementById("myTotal").innerHTML = newParagraph4;
This performs less DOM manipulations and is faster than creating the paragraph on the DOM and then adding it.
Change .textContent to innerHTML:
var newParagraph4 = document.createElement('p');
newParagraph4.innerHTML = total;

Categories