This is yet another question about appending HTML (I have seen another questions already answered but I can't make it work on my own).
I need to add a couple of HTML lines inside a DIV in the form of a DL. To put it context, it's a (spanish) forum where I want to add extra info under every user in a thread.
Take this page as an example: http://www.antronio.cl/threads/comunidad-steam-antronio.1127070/page-222, each user has its avatar, a nick name, a secondary nick name and under all that a box with extra info. This is the box I want to modify.
The box is a DIV called "extraUserInfo" and every line on that box is a DL called "pairsJustified". I want to add a DL at the begining with text on it like the ones already there, but I can't make it work.
This is my manifest.json
{
"manifest_version": 2,
"content_scripts": [{
"js": ["js\/medallas.js"],
"matches": ["http://www.antronio.cl/threads/*/*/"]
}],
"description": "medallas para usuarios",
"name": "medallas",
"version": "1",
"permissions": ["tabs","http://www.antronio.cl/threads/*/*/"]
}
and my "medallas.js"
var dl = document.createElement("dl");
dl.setAttribute("class","pairsJustified");
dl.innerHTML = "<dt>Status:</dt><dd>OK</dd>";
document.getElementById("extraUserInfo").appendChild(dl);
I'm new to this and actually trying to learn with this extension. Maybe even my folder structure is wrong
/medallas/
manifest.json
/js/
medallas.js
Any help would be appreciated.
The main issue is there is no element with id extraUserInfo. They are using a class. You'll want to use getElementsByClassName and iterate over the NodeList. Something similar to this should get you started.
var extraUserInfos = document.getElementsByClassName("extraUserInfo");
for (var i = 0; i < extraUserInfos.length; ++i) {
var item = extraUserInfos[i];
var dl = document.createElement("dl");
dl.setAttribute("class","pairsJustified");
dl.innerHTML = "<dt>Status:</dt><dd>OK</dd>";
item.appendChild(dl);
}
From the source on the site you mentioned the "extraUserInfo" is a class not an id.
You would want to use
document.getElementsByClassName("extraUserInfo")
however this will return an array of all elements that match this. To handle this you will need to loop through the array one at a time.
var items = document.getElementsByClassName("extraUserInfo");
for( var i = 0; i < items.length; i++) {
// place your logic here for each item.
var dl = document.createElement("dl");
dl.setAttribute("class","pairsJustified");
dl.innerHTML = "<dt>Status:</dt><dd>OK</dd>";
items[i].appendChild(dl);
}
fyi: getElementById will only return the first matched element, (id attributes are supposed to be unique)
hope this helps
more info: getElementsByClassName
edit:
since the page utilizes jQuery you can place the code inside the jQuery doc ready event and you can even use jquery to append the elements
$(document).ready(function(){
$('.extraUserInfo').each(function(){
$('<dl class="pairsJustified"><dt>Status:</dt><dd>OK</dd></dl>').appendTo(this)
})
});
Related
Update: This is a better way of asking the following question.
Is there an Id like attribute for an Element in a Document which I can use to reach that element at a later time. Let's say I inserted a paragraph to a document as follows:
var myParagraph = 'This should be highlighted when user clicks a button';
body.insertParagraph(0, myParagraph);
Then the user inserts another one at the beginning manually (i.e. by typing or pasting). Now the childIndex of my paragraph changes to 1 from 0. I want to reach that paragraph at a later time and highlight it. But because of the insertion, the childIndex is not valid anymore. There is no Id like attribute for Element interface or any type implementing that. CahceService and PropertiesService only accepts String data, so I can't store myParagraphas an Object.
Do you guys have any idea to achieve what I want?
Thanks,
Old version of the same question (Optional Read):
Imagine that user selects a word and presses the highlight button of my add-on. Then she does the same thing for several more words. Then she edits the document in a way that the start end end indexes of those highlighted words change.
At this point she presses the remove highlighting button. My add-on should disable highlighting on all previously selected words. The problem is that I don't want to scan the entire document and find any highlighted text. I just want direct access to those that previously selected.
Is there a way to do that? I tried caching selected elements. But when I get them back from the cache, I get TypeError: Cannot find function insertText in object Text. error. It seems like the type of the object or something changes in between cache.put() and cache.get().
var elements = selection.getSelectedElements();
for (var i = 0; i < elements.length; ++i) {
if (elements[i].isPartial()) {
Logger.log('partial');
var element = elements[i].getElement().asText();
var cache = CacheService.getDocumentCache();
cache.put('element', element);
var startIndex = elements[i].getStartOffset();
var endIndex = elements[i].getEndOffsetInclusive();
}
// ...
}
When I get back the element I get TypeError: Cannot find function insertText in object Text. error.
var cache = CacheService.getDocumentCache();
cache.get('text').insertText(0, ':)');
I hope I can clearly explained what I want to achieve.
One direct way is to add a bookmark, which is not dependent on subsequent document changes. It has a disadvantage: a bookmark is visible for everyone...
More interesting way is to add a named range with a unique name. Sample code is below:
function setNamedParagraph() {
var doc = DocumentApp.getActiveDocument();
// Suppose you want to remember namely the third paragraph (currently)
var par = doc.getBody().getParagraphs()[2];
Logger.log(par.getText());
var rng = doc.newRange().addElement(par);
doc.addNamedRange("My Unique Paragraph", rng);
}
function getParagraphByName() {
var doc = DocumentApp.getActiveDocument();
var rng = doc.getNamedRanges("My Unique Paragraph")[0];
if (rng) {
var par = rng.getRange().getRangeElements()[0].getElement().asParagraph();
Logger.log(par.getText());
} else {
Logger.log("Deleted!");
}
}
The first function "marks" the third paragraph as named range. The second one takes this paragraph by the range name despite subsequent document changes. Really here we need to consider the exception, when our "unique paragraph" was deleted.
Not sure if cache is the best approach. Cache is volatile, so it might happen that the cached value doesn't exist anymore. Probably PropertiesService is a better choice.
I have a JSON response from a server, which returns me a array with 32 objects (in this case). Something like this:
[{object1},{ object2},{ object3}, etc].
Each object have some info that I use to populate an html template. For that, I just use a simple loop:
for(var i = 0; i < api_empresaListar.length; i++)
{
var item = api_empresaListar[i];
var htmls;
htmls = $('...lots of html code');
...
Then it’s just a simple matter of finding/changing the values, and append items on the DOM. Everything works fine. BUT, for some next parts of the code, I would like to access all the info from the object I used to build the html elements (I just show part of the info). So, after searching a lot, I tried to use data, like this:
var tp = htmls.find(".rl_grupo"); // the main div of each html element created in the loop
$(tp).data('key', api_empresaListar[i]); // here, I expected to just insert the object data in each created item.
But when I try it in the console, I got the object info as expected, but always from the last element in the array. Why is that happening? I believe it might be something stupid, but I can’t figure it out.
So, any ideas on how to solve this, or another method to make this work is appreciated. I made it work by setting some "display:none" placeholder html tags and populate those with the info I need later, but looks like a poor solution...
You should not set your htmls variable in the loop. I think that you crush its content every turn, that's why you only have the last item. You should do something like this:
var htmls = $('<div></div>');
for(var i = 0; i < api_empresaListar.length; i++) {
htmls.append($('...lots of html code'));
}
How about setting an index number on each element inside of your html creating code, then iterating over the $('.rl_grupo') elements, like this?
$('.rl_grupo').each(function(){
var index = $(this).data('index');
var currentData = api_empresaListar[index];
$(this).data('key', currentData);
})
Is there a way to find out whether a HTML-tagName comes in pair or alone (Standalone-Tag)?
E.g. <div></div>, <em></em>, <p></p>, ... they come in pair, but <br/>, <input>, <area> ... are Standalone.
I need a function which should find out if a HTML-Code snippet is entered correct. Therefore the function has to investigate among others which HTML-Element can be created with Standalone-Tag.
Do you have any idea how can I find out if an HTML element is standalone? Except for example
something like this:
var myArray = [ list of Standalone-Tags ];
if(jQuery.inArray("test", myArray) != -1 ) { ... }
Thanks.
Browsers don't have a built in list of elements which are defined as empty.
You're most reliable bet would be to create one manually by reading the HTML specification.
Alternatively, you could create an element and see what the browser returns when you convert it to HTML.
var element = prompt("What element name? e.g. br");
var container = document.createElement('div');
var content = document.createElement(element);
container.appendChild(content);
var reg = new RegExp("/" + element);
alert(reg.test(container.innerHTML) ? "Not Empty" : "Empty");
I'm trying to crete 3 HTML collections containing all my links on a page, so I can attach 3 separate function to each categories of links.
My first HTML collection is "header links", the second is "footer links" and the third is "all other links". I need to attach link tracking functions and other elements as well.
Creating the first two collections is fairly easy as I can do document.getElementById('header'); and document.getElementById('footer'); and then this.getElementsByTagName('a');
However, getting the third collection of "all other links" is a bit more tricky. There isn't a clean div that contains just the "middle" of the page, and there are links outside the header and footer that are also difficult to single out.
I wish I could do something like allLinks = document.linnks, and then filter out of that all the links already present in the first and second HTML collections.
Any way to do that ? Ideally I would like to avoid loading more libraries and pure JS would be welcome
Thanks !
You can turn the node lists into arrays, then use filter() to pull out links that are already in one of the other lists:
var hdr = document.getElementById('header');
var hlinks = arrayOf(hdr.getElementsByTagName('a'));
var ftr = document.getElementById('footer');
var flinks = arrayOf(ftr.getElementsByTagName('a'));
var others = arrayOf(document.getElementsByTagName('a')).
filter(
function(element) {
return (hlinks.indexOf(element) < 0) && (flinks.indexOf(element) < 0);
}
);
function arrayOf(nodelist)
{
var result = [];
for ( var i = 0; i < nodelist.length; ++i )
result.push(nodelist.item(i));
return result;
}
Example: http://codepen.io/paulroub/pen/ikebh
If you need to support older browsers that lack Array.prototype.filter(), include the code from this MDN page to implement it when needed.
I need to run this function every X number of posts and the page uses AJAX to load in new posts as you scroll down the page. I was hoping to use the function below using a for loop with the modulus operator but it doesn't seem to accomplish what i'm looking for. Any idea how to do this?
$(document).ready(function($) {
function adTileLoop(){
var adTile = "<div class='new-box' id='article-tile'></div>";
var adLoc = 11;
var p = $('#article-tile');
var tile = $('#article-tile:nth-child('+adLoc+')');
for(var i = 0; i <= p.length; i++){
if(i % adLoc == 0){
$(tile).after(adTile);
}
}
}
$('#content').live(adTileLoop);
}
First of all, you need to be careful to keep IDs unique. The fact that you have $("#article-tile") and you are trying to select more than one is a mistake. ID's must be unique.
Here's a better way to run over a bunch of divs with jQuery:
$("div").each(function() {
console.log($(this));
});
You can then improve the selector to select only nth-children as you do in your question:
$("div:nth-child(2)") for example will get every other div on the page.
To retrieve the information about your posts specifically, use a selector specific to each post, something like: $(".post:nth-child(2)").
As Ashirvad suggested in the comments, you can run this after a successful ajax call and you will be able to retrieve the updated information about your page.