I'm trying to make a SPA with html, css and vanilla JS (I have very little idea of JS). The problem I have is that the method I am using, works correctly in my header where I have only "a" with text inside. But when I want to use an img as "a", the script does not work inside the img, it only allows me to click around the img and not inside it. I appreciate any help. This is my script and my html in the part where I have the problem.
const route = (event) => {
event = event || window.event;
event.preventDefault();
window.history.pushState({}, "", event.target.href);
handleLocation();
};
const routes = {
404: "./pages/404.html",
"/": "./pages/index.html",
"/vehicles": "./pages/vehicles.html",
"/services": "./pages/services.html",
"/contact": "./pages/contact.html",
"/financing": "./pages/financing.html",
"/locations": "./pages/locations.html",
};
const handleLocation = async () => {
const path = window.location.pathname;
const route = routes[path] || routes[404];
const html = await fetch(route).then((data) => data.text());
document.getElementById("main-page").innerHTML = html;
};
window.onpopstate = handleLocation;
window.route = route;
handleLocation();
<a href="/financing" onclick="route()" >
<div >
<h2>Financing</h2>
</div>
<img src="../image/financing-image-colored.svg" alt="">
</a>
CodePudding user response:
The issue is that when you click the <img>
the target
isn't set to your <a>
tag so event.target.href
is undefined
.
Instead of using onclick
attributes, use a delegated event listener and check if it originates from an <a>
tag or a descendant.
document.addEventListener("click", (e) => {
const anchor = e.target.closest("a[href]");
if (anchor) {
e.preventDefault();
window.history.pushState({}, "", anchor.href);
handleLocation();
}
});
Demo ~ https://jsfiddle.net/dgn1r5zh/
CodePudding user response:
This behavior happens because event.target
is a reference to the element that fired the event. In this case that is your image.
First of let me point out that inline event handlers should not be used. They can create unexpected behavior and make you write JS inside HTML. More about this topic. Learn to use addEventListener
instead.
You can fix your issue by adding some CSS to your element that you want to click. With CSS the pointer-events
you can determine if an element should be able to become a target
.
Implement this by selecting all the elements within the element that you want to have as your target and set pointer-events: none;
to disable any pointer events on those elements. This ensures that the <a>
tag is the only element that can be considered a target
. So event.target
in your code will always point to the correct element.
.mainServices-section * {
pointer-events: none;
}
Alternatively, if you can't use pointer-events
because, for example you need some other pointer event to fire on the children of .mainServices-section
, then you should extend your code by explicitly checking if the current target is the <a>
tag and otherwise select the <a>
tag if possible.
You can search for the <a>
from any child within the element with the closest
that is on the clicked element. It walks up and looks for the element that you're searching for.
const route = (event) => {
const target = event.target.closest('.mainServices-section');
if (target === null) {
return;
}
event.preventDefault();
window.history.pushState({}, "", target.href);
handleLocation();
};
const mainServicesSections = document.querySelectorAll('.mainServices-section');
for (const mainServiceSection of mainServicesSections) {
mainServiceSection.addEventListener('click', route);
}
CodePudding user response:
You need to look for the .closest("a")
of the clicked element ev.target
:
var hist="none";
const route = (event) => {
event = event || window.event;
event.preventDefault();
hist=event.target.closest("a").getAttribute("href");
handleLocation();
};
const routes = {
404: "./pages/404.html",
"/": "./pages/index.html",
"/vehicles": "./pages/vehicles.html",
"/services": "./pages/services.html",
"/contact": "./pages/contact.html",
"/financing": "./pages/financing.html",
"/locations": "./pages/locations.html",
};
const handleLocation = async () => {
const route = routes[hist] || routes[404];
console.log(route);
// fetch() etc. to be placed here ...
};
// window.onpopstate = handleLocation;
// window.route = route;
handleLocation();
<a href="/financing" onclick="route()" >
<div >
<h2>Financing</h2>
</div>
<img src="https://picsum.photos/200" alt="">
</a>
Another thing I changed is the way to look for the href
attribute of the <a>
element: DOMelement.href
will return an absolute path, starting with "https://" while DOMelement.getAttribute("href")
will actually get you the string as defined in the element's attribute.