My goal is to make these links open in a new tab only if the check box is ticked.
Why is my anchor.getAttribute not a function if I change getElementByID to getElementsByClassName?
<!DOCTYPE html>
<html>
<head> </head>
<title> </title>
<body>
<input id="checkr" type="checkbox">Open in New Window</input>
<br />
Google <br>
W3 Schools <br>
Twitch <br>
<script>
var checkr = document.getElementById('checkr');
var anchor = document.getElementsByClassName('linker');
var link = anchor.getAttribute('href');
function OpenWindow(href) {
if (checkr.checked) {
window.open(href, '_blank');
} else {
window.open(href, '_self');
}
}
anchor.onclick = function() {
OpenWindow(link);
return false;
};
</script>
</body>
</html>
First, getElementsByClassName returns an array-like object...elements plural should be a clue...it's not returning a single thing, it's returning a collection of thing.
So to attach your handlers, you need to loop over them like so:
const linkers = document.getElementsByClassName('linker');
for(const linker of linkers) {
linker.addEventListener('click', function(evt) {
// this is your click event listener
});
}
Second, the way you're trying to get the anchor isn't going to work, because which anchor are you talking about? The best way to do it is let the event itself tell you what anchor was clicked, which it does through it's target property:
const linkers = document.getElementsByClassName('linker');
for(const linker of linkers) {
linker.addEventListener('click', function(evt) {
const href = evt.target.attributes['href'].value;
});
}
Since you don't want the default behavior to happen, call evt.preventDefault():
const linkers = document.getElementsByClassName('linker');
for(const linker of linkers) {
linker.addEventListener('click', function(evt) {
evt.preventDefault();
const href = evt.target.attributes['href'].value;
});
}
Then finally you can get the value of the checkbox and take the appropriate action:
const linkers = document.getElementsByClassName('linker');
for(const linker of linkers) {
linker.addEventListener('click', function(evt) {
evt.preventDefault();
const href = evt.target.attributes['href'].value;
const newWindow = document.getElementById('checkr').checked;
window.open(href, newWindow ? '_blank' : '_self');
});
}
Note that I'm using for...of loops, which may not be available in manky old browsers. If that's a problem, you can replace them with regular for loops with indices (you can't use Array#forEach because the DOM, in its infinite wisdom [cough] doesn't return arrays, but array-like objects).
Related
I have a few links. When I hover mouse over the links I would like to get the values stored in data attributes. I need to pick the t values and pass them into function
HTML
<a href="#" data-lat="23.333452" data-lon="-97.2234234">
JS
var loc = document.querySelectorAll("a[data-lat]");
loc.addEventListener("mouseover", locOver);
loc.addEventListener("mouseout", locOut);
function locOver() {
// do something
}
function locOut() {
// do something else
}
It's been a while since I used vanilla JS and it's been a long day so I'm sure it's pretty close but I'm stuck. I keep getting:
Uncaught TypeError: loc.addEventListener is not a function
What am I missing here?
You need to loop through the nodes that you obtained with document.querySelectorAll("a[data-lat]") for adding events.
Working example.
Node
<script>
var loc = document.querySelectorAll("a[data-lat]");
loc.forEach(node => {
node.addEventListener("mouseover", locOver);
node.addEventListener("mouseout", locOut);
})
function locOver(event) {
// do something
console.log('mouseover', event.target.dataset)
}
function locOut() {
// do something
console.log('locOut')
}
</script>
const loc = document.querySelector("a[data-lat]");
const locOver = () => {
console.log("Mouse is over the link");
}
const locOut = () => {
console.log("Mouse is out of the link");
}
loc.addEventListener("mouseover", locOver);
loc.addEventListener("mouseout", locOut);
Link
Explanation:
I target the link using .querySelector method that returns a single node.
After that i created two event handler for mouseOver and mouseOut and than i added the eventListener to the link.
I need to make a web page with a lot of content. In order to be more efficient when modifying this content, I decided to put it in separate files and then include these files, following this tutorial: https://www.w3schools.com/howto/howto_html_include.asp.
For example one of these files may contain some clickable links to book descriptions, which are modal boxes. So I need to get them in a loading script to get these clickable links and make them trigger some events. But it seems this loading script is called before JavaScript gets the included nodes, even if I add an event listener after reading some threads (I tried to run it at 'DOMContentLoaded' or 'load') : document.getElementById or document.getElementsByClassName still returns null so it fails to define an onclick function. Let me show an example code:
script.js
function includeHTML() { /* Some code replacing the div by some-content.html, which is : <a id="clickable">Hello</a> */}
var button = null
window.addEventListener('load', function() {
button = document.getElementById("clickable");
button.onclick = function() { alert('Hello'); }
});
index.html
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="script.js"></script>
</head>
<body>
<p>Here is a trigger button : </p>
<div include-html="some-content.html"></div>
<script>includeHTML();</script>
</body>
</html>
On Firefox, this will fail on defining button.onclick as button is still null.
Any idea on how to fix it?
Not only should I be adding links, but also modal boxes. Here is a script code, more complete, for what my guess was:
script.js
var boxNames = ["bibliography", "about", "book1", "book2" ];
var boxes = null /* Contains the boxes to be displayed */
var trigs = null /* Contains the trigger buttons for each box */
var close = null /* Contains the close buttons for each box */
function setTrigger(i) {
trigs[i].onclick = function() { setBoxVisible(true, i); }
}
function setClose(i) {
trigs[i].onclick = function() { setBoxVisible(false, i); }
}
function load() {
boxes = new Array(4);
trigs = new Array(4);
close = new Array(4);
for(var i = 0; i < boxNames.length; i++) {
boxes[i]=document.getElementById(boxNames[i]+"-box");
trigs[i]=document.getElementById(boxNames[i]+"-trig");
close[i]=document.getElementById(boxNames[i]+"-close");
setTrigger(i); setClose(i);
}
}
window.onload = function() { load(); }
For the code of includeHTML(), you can have a look at the tutorial I shared, I copy/pasted.
I think this kind of function would be more elegant if dealing with such stuff, but I would need it to be launched once everything is loaded, as if I was running it manually.
Your code only added the event listener when the page was loading, likely before the link existed.
You need to delegate from the nearest static container.
Here in your code it is document
Give the link a class instead of ID and do
window.addEventListener('load', function(e) {
document.addEventListener('click', function(e) {
const tgt = e.target;
if (tgt.classList.contains("clickable")) {
e.preventDefault(); // because it is a link
alert('Hello');
}
});
});
<a class="clickable" href="#">Click</a>
Update after new code
You overwrite the trigs code
You can very simply extend my code so you do not need to loop
window.addEventListener('load', function(e) {
document.addEventListener('click', function(e) {
const tgt = e.target;
if (tgt.classList.contains("clickable")) {
e.preventDefault(); // because it is a link
alert('Hello');
}
else if (tgt.classList.contains("box")) {
e.preventDefault(); // because it is a link
const [id, what] = tgt.id.split("-")
console.log(id)
if (what === "trig") {
document.getElementById(id).classList.remove("hide")
}
else if (what === "close") {
document.getElementById(id).classList.add("hide"); // or tgt.closest("div").classList.add("hide")
}
}
});
});
.hide { display:none; }
<a class="clickable" href="#">Click</a>
<hr/>
<a class="box" id="bibliography-trig" href="#">Trigger Biblio</a>
<a class="box" id="about-trig" href="#">Trigger About</a>
<div id="bibliography" class="hide">Biblio
<a class="box" id="bibliography-close" href="#">Close</a>
</div>
<div id="about" class="hide">About
<a class="box" id="about-close" href="#">Close</a>
</div>
I'm trying to create an extension that will be able to remember some websites I visited. When I press the extension, there's a button (that allows the user to save the current URL of the tab and the title) and an “a” element, that will redirect to the URL saved. The idea is making it able to save different URLs, but first I want it able to save one.
Someone told me to do that with a Set(). I've the code, but something is wrong so when I press the “a” element with the last saved element, it opens that direction: chrome-extension://the_key_of_my_extension/[object%20Set%20Iterator] or: chrome-extension://the_key_of_my_extension/[undefined].
What I'm doing wrong? I've been searching for a while but I don't see anything wrong. Thanks!
var urlSet = new Set();
var titleSet = new Set();
chrome.storage.sync.get(["activeTab", "nameOfTheTab"], function(items){
urlSet.add(items.activeTab);
titleSet.add(items.nameOfTheTabs);
document.getElementById('urlDude').href = [...urlSet];
document.getElementById('urlDude').innerHTML = [...titleSet];
});
saveItem.onclick = function(element) {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
urlSet.add(tabs[0].url);
titleSet.add(tabs[0].title);
chrome.storage.sync.set({ "activeTab": [...urlSet] }, function(){ });
chrome.storage.sync.set({ "nameOfTheTab": [...titleSet] }, function(){ });
document.getElementById('urlDude').href = [...urlSet];
document.getElementById('urlDude').innerHTML = [...titleSet];
});
};
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="./popus.css">
</head>
<body>
<button id="saveItem">save</button>
<script src="popup.js"></script>
<br>
<br>
<a id="urlDude" target="_blank"><p>Hola</p></a>
<a id="urlDude2" target="_blank"><p>Hola2</p></a>
</body>
</html>
UPDATE:
This is what I have now:
var urlSet = new Map();
chrome.storage.sync.get(["activeTabs"], function(items){
if(!items.activeTabs)
return;
urlSet = new Map(JSON.parse(items.activeTabs));
AddLinks();
});
function AddLinks()
{
var myLinks="";
urlSet.forEach(function(value, key) {
//myLinks+="<a href='"+value+"' target='_blank'>"+key+"</a> <span>X</span><br>";
pushingData(value, key);
});
//document.getElementById('mydiv').innerHTML = myLinks;
}
saveItem.onclick = function(element) {
chrome.tabs.query({active: true}, function(tabs) {
urlSet.set(tabs[0].title, tabs[0].url);
chrome.storage.sync.set({ "activeTabs":JSON.stringify(Array.from(urlSet.entries())) });
pushingData(tabs[0].url, tabs[0].title);
});
};
function pushingData(value, key) {
var atags = document.createElement("a");
atags.href = value;
atags.innerHTML = key;
document.getElementById('mydiv').append(atags);
var newButton = document.createElement("button");
newButton.innerHTML = "x";
newButton.addEventListener('click', function() { urlSet.delete(key); });
document.getElementById('mydiv').append(newButton);
var breakLine = document.createElement("br");
document.getElementById('mydiv').append(breakLine);
};
I tried your code (with some slight modifications) in my extension and it worked, so I suspect there might be something stuck in the storage from your previous attempts. Here's what I've got:
var urlSet = new Map();
chrome.storage.sync.get(["activeTabs"], function(items){
if(!items.activeTabs)
return;
urlSet = new Map(JSON.parse(items.activeTabs));
AddLinks();
});
function AddLinks()
{
//There are better ways of doing this!
var myLinks="";
urlSet.forEach(function(value, key) {
myLinks+="<a href='"+value+"' target='_blank'>"+key+"</a>";
});
// You may want to come up with a way to delete links
//before they get out of control and proclaim independence
document.getElementById('mydiv').innerHTML = myLinks;
}
saveItem.onclick = function(element) {
chrome.tabs.query({active: true}, function(tabs) {
urlSet.set(tabs[0].title, tabs[0].url);
chrome.storage.sync.set({ "activeTabs":JSON.stringify(Array.from(urlSet.entries())) });
AddLinks();
});
};
UPDATE
Seems like adding inline scripts to the popup is causing security issuer. While it is probably possible white-list your page to use its own script a better way of adding new elements is creating them with document.createElement, rather than generating a sting, to give you an idea of how it may look:
var delButton = document.createElement("button");
delButton.innerHTML = "delete";
delButton.addEventListener('click',function(){/*something useful happens here*/});
document.getElementById('mydiv').append(delButton);
About 19 years ago, I needed a script that changes images using the onmouseover event. The script used name="foo" which, of course, can no longer be supported by the <img> tag. I've literally forgotten how the script works and I need some help adapting it to use id="foo".
This is the script that lives in my <head>:
<script>
function newImage(arg) {
if (document.images) {
rslt = new Image();
rslt.src = arg;
return rslt;
}
}
function changeImages() {
if (document.images && (preloadFlag == true)) {
for (var i=0; i<changeImages.arguments.length; i+=2) {
document[changeImages.arguments[i]].src = changeImages.arguments[i+1];
}
}
}
var preloadFlag = false;
function preloadImages() {
if (document.images) {
archives_over = newImage("/images/index/menu/archives_over.jpg");
bio_over = newImage("/images/index/menu/bio_over.jpg");
poetry_over = newImage("/images/index/menu/poetry_over.jpg");
preloadFlag = true;
}
}
</script>
...and this is the HTML that lives in my <body>:
<a href='https://www.foo.com/index.php?page=news'
onmouseover='changeImages("poetry", "/images/index/menu/poetry_over_static.jpg"); return true;'
onmouseout='changeImages("poetry", "/images/index/menu/poetry_over_static.jpg"); return true;'
onmousedown='changeImages("poetry", "/images/index/menu/poetry_over_static.jpg"); return true;'
onmouseup='changeImages("poetry", "/images/index/menu/poetry_over_static.jpg"); return true;'>
<img name="poetry" src="/images/index/menu/poetry_over_static.jpg">
</a>
If I change <img name="poetry" to <img id="poetry", the whole thing stops working.
Your
document[changeImages.arguments[i]]
when the first argument is poetry, accesses document.poetry. Use getElementById instead, to select an element with a particular id:
function changeImages(id, newSrc) {
document.getElementById(id).src = newSrc;
}
You also might consider attaching listeners properly using addEventListener, rather than inline handlers:
const a = <select your <a> tag here>
const img = document.getElementById('poetry');
const fn = () => {
img.src = '/images/index/menu/poetry_over_static.jpg';
}
['mouseover', 'mouseout', 'mousedown', 'mouseup'].forEach((eventType) => {
a.addEventListener(eventType, fn);
});
If you're doing this sort of thing for multiple images on the page, then you can make the code even better by dynamically selecting the <a>'s nextElementSibling, allowing you to avoid IDs entirely:
const fn = ({ target }) => {
const img = target.nextElementSibling;
img.src = '/images/index/menu/poetry_over_static.jpg';
}
['mouseover', 'mouseout', 'mousedown', 'mouseup'].forEach((eventType) => {
a.addEventListener(eventType, fn);
});
HTML:
<li>Facebook</li>
<li>Twitter</li>
<li>Souncloud</li>
JS:
var links = ['https://www.facebook.com/', 'https://twitter.com', 'https://soundcloud.com'];
function openURL (url) {
location.href = url;
}
window.onload = function () {
var listElement = document.getElementsByTagName('li');
for (i=0;i<listElement.length;i++) {
listElement[i].addEventListener('click',openURL(links[i]))
}
}
I want the code to work in such a way that when the user clicks either of the list elements, it opens up the respective website. For certain reasons I do NOT want to use the <a> tag.
The logic to the code is very simple. First, a variable is created which returns an array of all the <li> tags in the document. Then using the 'for' loop, I set an event listener to each of the <li> tags in the array. The function I run simply opens the website.
Somehow, whenever I open the page, it gets redirected automatically, without clicking, to facebook.com. Why does this happen??
Any help would be appreciated, thanks!
This is because your event handler will be called later (by user action), and that time, i isn't what you want. You have to use closure to keep it internal.
var links = ['https://www.facebook.com/', 'https://twitter.com', 'https://soundcloud.com'];
function openURL(link) {
console.log(link);
};
window.onload = function () {
var listElement = document.getElementsByTagName('li');
for (i=0;i<listElement.length;i++) {
listElement[i].addEventListener('click',(function (i) {
return function () {
openURL(links[i]);
};
}(i)));
}
}
<li>Facebook</li>
<li>Twitter</li>
<li>Souncloud</li>
When you do click',openURL(links[i]), it will call openUrl. You should use .bind(context, params). Also note, first argument of eventListener is event
Also, window.onload = function is considered as bad practice as it will override any previous listener. You should use .addEventListener
var links = ['https://www.facebook.com/', 'https://twitter.com', 'https://soundcloud.com'];
function openURL(event, url) {
//location.href = url;
console.log(url)
}
window.addEventListener('load', function() {
var listElement = document.getElementsByTagName('li');
for (i = 0; i < listElement.length; i++) {
listElement[i].addEventListener('click', openURL.bind(null, event, links[i]))
}
})
<li>Facebook</li>
<li>Twitter</li>
<li>Souncloud</li>