Voici un stylo avec le code HTML complet: https: // codepen. io / froggomad / pen / WLdzoB
J'écris 2 fonctions - une pour afficher le contenu caché et une pour le cacher. Je veux que la fonction show () s'exécute sur le div parent et que la fonction hide () s'exécute sur le div avec le sélecteur .click-text .
Cependant, Je passe du texte sur .click-text de show à hide, donc je ne veux pas que la fonction hide reste sur le texte à tout moment. Je veux aussi qu'il soit évident que son texte interactif lors du passage à une fonction de masquage, j'en fais un lien.
C'est très bien, mais lorsque vous essayez de définir le onclick Attr de le parent retourne à la fonction show () , rien dans le bloc hide ne s'exécute du tout.
Si je supprime la ligne définissant le onclick Attr , le script s'exécute comme prévu. Si je mets en cliquant sur Attr un autre élément, le script s'exécute comme prévu.
Cependant, avec cette ligne là-dedans, rien ne se passe et il n'y a pas de sortie dans la console pour indiquer une erreur. J'ai même défini une alerte avec le type d'élément et le nom de classe pour m'assurer que je cible le bon élément.
Obtenir le parent le plus proche du sélecteur de correspondance d'élément:
function hide(elem) {
var parent = getClosest(elem, '.service-category'),
menu = parent.querySelector("ul.service-category-menu"),
click = parent.querySelector(".click-text")
;
alert(parent + "\n" + parent.className);
//Outputs div element with expected class name (class name is unique on each div)
if (menu.style.display === "block") {
menu.style.display = "none";
click.innerHTML = "Click to Show";
click.setAttribute('onclick', 'null');
//the above lines don't execute when the following line is in place. There's no error in console.
parent.setAttribute('onclick','show(this)');
}
}
Afficher l'élément masqué ul.service-category-menu
function show(elem) {
var menu = elem.querySelector("ul.service-category-menu"),
click = elem.querySelector(".click-text"),
parent = getClosest(elem, '.service-category');
;
if (menu.style.display === "none" || menu.style.display === "") {
menu.style.display = "block";
click.innerHTML = "<a href=\"#\">Click to Hide<\/a>";
click.setAttribute('onclick','hide(this);');
elem.setAttribute('onclick', 'null');
}
}
Masquer l'élément
var getClosest = function (element, selector) {
for ( ; element && element !== document; element = element.parentNode ) {
if ( element.matches(selector) ) return element;
}
return null;
}
3 Réponses :
Il est exécuté, c'est juste après avoir cliqué sur Cliquer pour masquer , l'événement continue à être parent et le gestionnaire d'événements du parent exécuté. Ainsi, ce qui se passe exactement, c'est (avec cette ligne), après l'appel de hide () , vous avez appelé par inadvertance show () .
En javascript, on l'appelle généralement bulles (quand vous cliquez sur les enfants, le gestionnaire de clics du parent sera également exécuté après que le gestionnaire de clics des enfants soit terminé).
Donc la solution, vous pouvez ajouter cette ligne à la fin du hide () fonction
event.stopPropagation();
Pour empêcher l'événement de continuer vers le parent
La configuration de event.stopPropagation comme mentionné dans l'autre réponse résoudra potentiellement votre problème. Vous pouvez également changer la dernière ligne de votre fonction hide en window.setTimeout (e => parent.setAttribute ('onclick', 'show (this)'), 0) code>.
Ce qui se passe actuellement est:
hide , et pendant cette fonction, il lie un événement de clic au parent En utilisant setTimeout (fn, 0) , vous vous assurez que l'événement de clic se termine avant que la fonction ne soit liée au parent.
Tout d'abord, je dois avouer que je suis contre l'utilisation des attributs onclick . Si vous n'utilisez pas un framework tel que VueJS ou React, je pense que HTML et JS devraient rester séparés pour un meilleur contrôle et une meilleure maintenabilité.
Vous pouvez utiliser addEventListener , removeEventListener code > et e.stopPropagation () pour éviter de déclencher plusieurs gestionnaires d'événements.
Les événements se déroulent en deux phases:
Capture d'événements : l'événement se propage du document jusqu'à l'élément cible.
Pour attraper un événement pendant cette phase, procédez comme suit:
<div class="service-category web-back" id="web-back">
<div class="row-overlay">
Web <br /> Development
<div class="click-text">Click to Show</div>
<ul class="service-category-menu web">
<li>
Some text...
</li>
</ul>
</div>
</div>
<div class="service-category web-front" id="web-front">
<div class="row-overlay">
Web <br /> Design
<div class="click-text">Click to Show</div>
<ul class="service-category-menu web">
<li>
Some text...
</li>
</ul>
</div>
</div> Événement bouillonnant : l'événement rebondit de la cible vers le document .
Pour attraper un événement pendant cette phase, procédez comme suit:
.service-category{display:inline-block;border:3px solid #ccc;margin:1%;font-weight:700;font-size:3.5vw;cursor:pointer;background-color:#fff;z-index:3;background-position:center;background-size:cover;color:#000}.click-text{text-align:right;font-size:1.25vw;font-style:italic;font-weight:700;padding-right:1%}.service-category:hover .click-text{color:#b22222}.service-category-menu{display:none;margin-left:8%;margin-right:8%;margin-top:1%;background-color:#fff;font-weight:700;font-size:1.6vw;border-radius:10px} L'utilisation de e.stopPropagation () vous permet de briser cette chaîne.
// When the DOM is ready
window.addEventListener("DOMContentLoaded", init);
function init() {
// Get all categories
var $categories = document.querySelectorAll(".service-category");
// For each of them
Array.from($categories).forEach(function($category) {
// Add an event listener for clicks
$category.addEventListener("click", show);
});
}
function getClosest(element, selector) {
for (; element && element !== document; element = element.parentNode) {
if (element.matches(selector)) return element;
}
return null;
}
function show(e) {
var $menu = this.querySelector("ul.service-category-menu"),
$click = this.querySelector(".click-text");
if (["none", ""].includes($menu.style.display)) {
$menu.style.display = "block";
$click.innerHTML = '<a href="#">Click to Hide</a>';
$click.addEventListener("click", hide);
// Remove the `show` event listener
this.removeEventListener("click", show);
}
e.stopPropagation();
}
function hide(e) {
var $parent = getClosest(this, ".service-category"),
$menu = $parent.querySelector("ul.service-category-menu"),
$click = $parent.querySelector(".click-text");
if (!["none", ""].includes($menu.style.display)) {
$menu.style.display = "none";
$click.innerHTML = "Click to Show";
$click.removeEventListener("click", hide);
$parent.addEventListener("click", show);
}
e.stopPropagation();
}
elm.addEventListener('click', myFunc, false); /* or just omit the 3rd param */
elm.addEventListener('click', myFunc, true);
Merci, j'ai appris js en 1998 avant qu'il y ait un gestionnaire de clics en html. Cela semblait être une amélioration, mais je suppose que non lol.
Jetez un œil à
addEventListener/removeEventListener- Je serais surpris si le DOM est capable de réanalyser l'attributonclicklorsqu'il est défini par programmation.