I need to display the URLs in a list of search results in a breadcrumb style (i.e. such as Google does - Google Breadcrumbs), and have limited knowledge of JavaScript (and haven't touched it in nearly two years). Can you help me please? I can apply the code if it's provided with clear instruction, and am very comfortable with HTML and CSS, but have not attempted to create breadcrumbs without lists before.
Where do I start?
Input would be page's URL (class is .slimBreadcrumbLink) - e.g. https://www.example.com/level1/level2/level3/level4 - and output would be as below:
Level 2 > Level 3 > Level 4
I haven't tried anything of significance yet, I'm starting here. I've read through the other breadcrumb questions posed, but it's not helped so far. I found the below but don't know how to implement it.
var path = location.pathname;
var here = location.href.split('/').slice(3);
var parts = [{
"text": 'Home',
"link": '/'
}];
for (var i = 0; i < here.length; i++) {
var part = here[i];
var text = part.toUpperCase();
var link = '/' + here.slice(0, i + 1).join('/');
parts.push({
"text": text,
"link": link
});
}
Thank you.
I rolled my own demo snippet. The code is quite a bit to explain, but if anything doesn't appear self-explanatory feel free to let me know and I'll elaborate more here.
// Utility functions
function createBreadcrumbs (origin, path) {
var levels = path.slice(1).split('/')
return levels.map(function (e, i, levels) {
var text = e.replace(/-+/g, ' ').replace(/\s*\b\S/g, function (e) {
return e.toUpperCase()
})
return anchor(origin + '/' + levels.slice(0, i+1).join('/'), text)
})
}
function anchor (href, text) {
var a = document.createElement('a')
a.href = href
a.textContent = text
return a
}
function render (breadcrumbs) {
var ol = document.createElement('ol')
ol.className = 'breadcrumbs'
breadcrumbs.forEach(function (anchor) {
var li = document.createElement('li')
anchor.className = 'breadcrumbs__link'
li.className = 'breadcrumbs__crumb'
li.appendChild(anchor)
ol.appendChild(li)
})
return ol
}
// How to use
var breadcrumbs = createBreadcrumbs('//example.com', '/example-thing/location-stuff/level-2'),
list = render(breadcrumbs)
console.log(breadcrumbs)
document.body.appendChild(list)
<p>Breadcrumbs for //example.com/example-thing/location-stuff/level-2</p>
Here is a function that will create the HTML structure for the breadcrumbs:
const getLevels = url => url.replace(/http(s.):\/\//, "").split("/");
const createBreadcrumb = function(url, elem) {
const ol = document.createElement("ol");
getLevels(url).forEach((e, i) => {
if(i > 2) {
const li = document.createElement("li");
const a = document.createElement("a");
a.href = url.substring(0, url.indexOf(e) + e.length);
a.innerText = e;
li.appendChild(a)
ol.appendChild(li);
}
}
});
elem.appendChild(ol);
};
Because it is ES6, you will have to use a transpiler like babel to make it compatible with older browsers. Also, because you are parsing the URL, you cannot customize the title of the links.
You can then use the function like this, to parse the url and create the ol list in the element with the id.
createBreadcrumb(url, document.getElementById("id"));
Related
I want to personalize a CMS for FAQ.
I need to display the 4 first questions and set a button to toggle the display of the other questions.
I'm using that function to display the questions :
function toggleViewMoreLess() {
let articlesRequestPromise = fetch("/api/v2/help_center/fr-fr/articles.json");
let articlesArray = [];
let arrayOfSectionLinks = document.getElementsByClassName('article-list');
articlesRequestPromise.then(response => {
return response.json();
}).then(results => {
articlesArray = results.articles;
for (let i = 0; i < arrayOfSectionLinks.length; i++) {
let aId = JSON.parse(arrayOfSectionLinks[i].id);
let threeArticles = articlesArray.filter(article => article.section_id === aId).slice(0, 3);
threeArticles.map(function (article) {
let articleLink = document.createElement("a");
articleLink.type = 'link';
articleLink.innerText = article.title;
articleLink.className = 'article-list-link';
articleLink.href = article.html_url + "_" + article.section_id;
return (
arrayOfSectionLinks[i].append(articleLink)
)
});
}
})
}
toggleViewMoreLess();
The idea I have and I can't achieve is to set a display none on the each section's array after index 3 and to set a ternary condition on the button for the toggle.
I'm more use to work with react than vanilla js so I'm kind of lost.
Ideally, I would like to do something like with useState
If your array is an array of HTML elements have you considered just using CSS for this? e.g. if you have an array of p tags:
p:nth-child( n+4 ).no-display {
display: none;
}
Then conditionally apply the class "no-display" to your tags
I'm making a moviefilter website for school. Now the last part is that I need to link the imdbID from my array to the movie posters on the screen. I made the links, the filter mechanics work but when I click on a poster all the ID's of that filter are being added to the end. Not just one.
My teacher says I can forEach trough the movieArray array and write everything I need in that loop. Can someone help me or look at my code what I'm doing wrong. I wrote 2 seperate functions now to create the arguments needed.
My code:
const addMoviesToDom = function (movieArray) {
const movieList = document.querySelector("#movielist");
movieList.innerHTML = "";
const moviePoster = movieArray.map(item => {
return item.Poster;
});
const imdbId = movieArray.map(item => {
return item.imdbID;
})
moviePoster.forEach(element => {
let newLi = document.createElement("li");
let newLink = document.createElement("a");
let images = document.createElement("img");
images.src = element;
newLink.setAttribute("href", "https://www.imdb.com/title/" + imdbId);
newLink.setAttribute("target", "_blank");
newLi.append(newLink);
newLink.append(images);
movieList.appendChild(newLi);
})
};
addMoviesToDom(movies);
Thanks in advance, hopefully you guys understand what I'm trying to explain, this is all pretty new for me.
imdbId is an array of all the IDs. When you concatenate that to "https://www.imdb.com/title/" it's turned into a string, which joins all the IDs with a , delimiter. So you're not setting the href to just the ID corresponding to the current moviePoster element.
You need to index the array to get the correct ID. forEach provides the array index as the second argument to the callback function, so you can change element => to (element, i) =>, and then use imdbId[i].
But a simpler way would be to skip creating the moviePoster and imdbId arrays, and just create all the elements from movieArray.
const addMoviesToDom = function(movieArray) {
const movieList = document.querySelector("#movielist");
movieList.innerHTML = "";
movieArray.forEach(element => {
let newLi = document.createElement("li");
let newLink = document.createElement("a");
let images = document.createElement("img");
images.src = element.Poster;
newLink.setAttribute("href", "https://www.imdb.com/title/" + element.imdbId);
newLink.setAttribute("target", "_blank");
newLi.append(newLink);
newLink.append(images);
movieList.appendChild(newLi);
})
};
addMoviesToDom(movies);
I'm working with an API that returns strings with inline links like so:
This is a question I'm asking on <my_link type="externalLink" data="https://stackoverflow.com/">StackOverflow</my_link> about splitting a string and reconstructing as a HTML link.
The reason for this is apparently so the API can be used by both web and native platforms and that HTML is kept away from the data. There are also internalLink types which will allow app developers to link to content within an app rather than opening a web browser.
I need to be able to pass this string into a function and return the full string with an tag like so:
This is a question I'm asking on StackOverflow about splitting a string and reconstructing as a HTML link.
Another thing to consider is that the string could have multiple links in it.
My initial attempt is basic and does get externalLink from the first link but I'm unsure of how to get the value of the data attribute and then re-run for any other links.
export default function convertLink(string) {
let stringWithLinks = string;
if (string.includes('<my_link')) {
const typeStart = string.indexOf('"') + 1;
const typeEnd = string.indexOf('"', typeStart);
const typeText = string.substring(typeStart, typeEnd); // externalLink
}
return stringWithLinks;
}
You can set the string as .innerHTML of a dynamically created element and use .getAttribute() to get the data attribute of <my_link> element, set .innerHTML of dynamically created <a> element and use .replaceChild() to replace <my_link> with <a> element
let str = `This is a question I'm asking on <my_link type="externalLink" data="https://stackoverflow.com/">StackOverflow</my_link> about splitting a string and reconstructing as a HTML link.`;
let div = document.createElement("div");
div.innerHTML = str;
let my_links = Array.prototype.map.call(div.querySelectorAll("my_link"), link =>
link.getAttribute("data"));
console.log(my_links);
for (let link of my_links) {
let a = document.createElement("a");
a.href = link;
a.target = "_blank";
a.innerHTML = div.querySelector("my_link").innerHTML;
div.replaceChild(a, div.querySelector("my_link"))
}
console.log(div.innerHTML);
Add the string as HTML of a new element. Loop over all the my_link elements extracting the relevant data, then build a new anchor that can then replace the my_link on each iteration.
function convertAllLinks(str) {
let el = document.createElement('div');
el.innerHTML = str;
el.querySelectorAll('my_link').forEach(link => {
let anchor = document.createElement('a');
anchor.href = link.getAttribute('data');
anchor.setAttribute('target', '_blank');
anchor.textContent = link.textContent;
link.parentNode.replaceChild(anchor, link);
});
return el.innerHTML;
}
convertAllLinks(str);
DEMO
Here's another solution using DOMParser(), in case you might need to do any more DOM modifications later on.
let stringWithLinks = `This is a question I'm asking on <my_link type="externalLink" data="https://stackoverflow.com/">StackOverflow</my_link> about splitting a string and reconstructing as a HTML link.`,
tempDOM = new DOMParser().parseFromString('<doc>' + stringWithLinks + '</doc>', "text/xml"),
linkElements = tempDOM.getElementsByTagName('my_link');
for (let i=0; i<linkElements.length; i++) {
let newA = document.createElement('a');
newA.setAttribute('src', linkElements[i].getAttribute('data'));
let linkType = linkElements[i].getAttribute('type');
if (linkType == 'externalLink') {
newA.setAttribute('target', '_blank');
}
newA.innerHTML = linkElements[i].innerHTML;
tempDOM.documentElement.replaceChild(newA, linkElements[i]);
}
console.log(tempDOM.documentElement.innerHTML);
Everytime an certain image is clicked I fetch some information, to add that information to a string and after that replace the current url with the new one. This is my code so far:
jQuery(document).ready(function(){
var kleuren = [];
jQuery('.swatch-category-container img').click(function(){
var kleur = jQuery(this).attr('title');
console.log("Selected kleur: " + kleur);
var link = jQuery(this).closest('.popup').find('.photo a').prop('href');
kleuren.push(kleur);
console.log(kleuren);
console.log(kleuren.length);
console.log("Fetched link: " + link);
var length = kleuren.length -1;
var avoid ="?kleur="+kleuren[length];
console.log("Remove string: " + avoid);
var news_link = link.replace(avoid,'');
var new_link = news_link + "?kleur="+kleur;
console.log("Cut string: " + news_link);
jQuery('.photo').find('.sendkleur').attr("href", new_link);
});
});
This works fine, but the previous data doesn't get removed.
For example
When the first image is clicked the kleur = zwart that info is put in the url.
But when the user clicks another image after that the url will look like ?kleur=zwart?kleur=beige instead just of ?kleur=beige
How can I remove the first part?
Although we could debug the replacement logic, I think I'd come at this a totally different way: Have a data-* attribute on the link that gives the raw version without kleur, then just reuse that; you can even initialize it on the first pass so you don't have to update your markup:
var linkElement = jQuery(this).closest('.popup').find('.photo a');
var link = linkElement.attr("data-rawhref");
if (!link) {
link = linkElement.attr("href");
linkElement.attr("data-rawhref", link);
}
// ...add kleur to `link`
If you don't need it to actually be an attribute, you can use data instead, but there's probably no real advantage over the above as the link is just a string.
var linkElement = jQuery(this).closest('.popup').find('.photo a');
var link = linkElement.data("rawhref");
if (!link) {
link = linkElement.attr("href");
linkElement.data("rawhref", link);
}
// ...add kleur to `link`
But the problem with your code is that you're using the current kleur, rather than the old one, in avoid:
kleuren.push(kleur); // Pushes the current kleur at the end
var length = kleuren.length -1; // Will be the index of the *current* kleur
var avoid ="?kleur="+kleuren[length];// ...and so this is looking for the wrong one
instead:
var avoid, new_link;
if (kleuren.length) {
avoid = "?kleur="+kleuren[kleuren.length - 1];
new_link = link.replace(avoid,'') + "?kleur="+kleur;
} else {
new_link = link + "?kleur="+kleur;
}
kleuren.push(kleur);
That's assuming you really need an array of previously-selected colors. If you don't, just use a variable:
var avoid, new_link;
if (letzteKleur) { // Meant to be "last color", my German is non-existant
avoid = "?kleur="+letzteKleur;
new_link = link.replace(avoid,'') + "?kleur="+kleur;
} else {
new_link = link + "?kleur="+kleur;
}
letzteKleur = kleur;
Maybe because you never do a pop on the array?
How does your log look like?
It seems that you try to replace the current value not the previous.
kleuren.push(kleur);
console.log(kleuren);
console.log(kleuren.length);
console.log("Fetched link: " + link);
var length = kleuren.length -1;
var avoid ="?kleur="+kleuren[length];
maybe that will do (of course only if there is at least 1 item in the array)
console.log(kleuren);
console.log(kleuren.length);
console.log("Fetched link: " + link);
var length = kleuren.length -1;
var avoid ="?kleur="+kleuren[length];
kleuren.push(kleur);
I need to write a method that takes a String and parses it for links (a href). If it finds a link it should add target="_blank" to the link, if it is not already there.
Example:
The Inputstring "
Google and target="_blank">Yahoo are search engines
... should result in the output String
Google and Yahoo are search engines
Any idea how to realize this?
Not very difficult with plain js.
var links = document.getElementsByTagName('a');
var len = links.length;
for(var i=0; i<len; i++)
{
links[i].target = "_blank";
}
Fraught with problems but usable with plain JavaScript:
function addBlankTargets(s) {
return (""+s).replace(/<a\s+href=/gi, '<a target="_blank" href=');
}
Or with jQuery:
function addBlankTargets(s) {
var p = $('<p>' + s + '</p>');
p.find('a').attr('target', '_blank');
return p.html();
}
var s = 'Google and '
+ 'Yahoo '
+ 'are search engines.';
var x = addBlankTargets(s);
x; // => 'Google and
// Yahoo
// are search engines.'
If you are targeting at modern browsers, instead of manipulating a string containing HTML and having to handle all the many special cases, you can transform the string into DOM.
At this point manipulating the DOM is trivial and you can then convert it back to a serialised string.
function decorateRichText(html) {
const domParser = new DOMParser()
const document = domParser.parseFromString(html, `text/html`)
const serializer = new XMLSerializer()
// Handles external links
const links = document.querySelectorAll(`a`)
links.forEach((link) => {
if (link.href) {
if (isExternalUrl(link.href)) {
link.target = `_blank`
link.rel = `noopener noreferrer`
}
}
})
return serializer.serializeToString(document)
}
Leave the browser JS engine do the heavy stuff and remember: code that you don't write is code you have not to debug :-)
You can use jQuery to parse the element, add the attribute, and then read out the HTML, like so:
var addTarget = function(input) {
return $('<span>' + input + '</span>').find('a').attr('target', '_blank').html();
};
console.log(addTarget('Google'));
in two lines
var links = document.getElementsByTagName('a');
for(i in links)
links[i].target=="_blank"?links[i].style.color="#f0f" : links[i].style.color ='#0f0'
jsfiddle