Javascript mouse over on dynamic amount of elements - javascript

My goal is on hover a p element contained inside an a tag gets bigger on hover. I have achieved this via css3 transitions, however this is not the issue.
A loop creates a variable amount of elements in the form below on each iteration.
anchorElement = "<a id='anchor" + countWide + "' class=\"boxOPT oneplustwo\" alt=\'"+ image_website +"' style=\"cursor:pointer;width:"+ itemWidth + "px"+";height:"+anchorHeight+";position:absolute;left:"+ locationLeft + "px"+";top:0.3%;\" ><p id=\"test\" class=\"popupDynamic\"> " + popupImageTitles[i] + "</p>";
anchorElement += '</a>';
I would love to be able to add a mouse in/out effect whenever the user scrolls on the relevant anchor. each p tag contains unique information that needs to be conveyed and on hover only the relevant one should react.
I dont want to it it the below way, making two each of the methods every time a new element is created above. is there a way to have the following below which will work for a dynamic amount of elements?
$("#anchor" + etc).mouseover(function() {
document.getElementById("test").style.height="1.1em";
});
$("#anchor" + etc).mouseout(function() {
document.getElementById("test").style.height="1.1em";
});
My version of suggestions. the console logs works.
.popupHighlight {
color: red;
}
..
$('.boxOPToneplustwo').mouseover(function (e) {
console.log("in");
$(e.target).next('p').addClass("popupHighlight");
});
$('.boxOPToneplustwo').mouseout(function (e) {
$(e.target).next('p').removeClass("popupHighlight");
});

What about selecting all a elements?
$('a').mouseout(function() {
//do stuff in here
});
or better yet, have a class selector:
$('.mySpecialRolloverClass').mouseover(function (e) {
$(e.target).next('p').addClass("highlight");
});
$('.mySpecialRolloverClass').mouseout(function (e) {
$(e.target).next('p').removeClass("highlight");
});
which would go hand in hand with
An anchor
and
.highlight {
color:red;
}
Here's a jsfiddle demo:
http://jsfiddle.net/8J6kM/

The #yochannah answer is correct, however if you want to add more links dynamically, you then need to use on method instead of mouseover and mouseout, otherwise it won't work. See the demo and jQuery documentation for further details.
// I assumed that links are placed inside of a container element: #links
$('#links').on('mouseover', '.mySpecialRolloverClass', function (e) {
$(e.target).next('p').addClass("highlight");
});

Related

Hover and selectors

I am completely new to Javascript / Jquery and I have a problem.
Suppose in my html page I have a number (undefined) of class to hover (when I hover something happens). Here I put 2 examples.
<div class='hover' id='hov33749'>Wikipedia</div>
<div class='hover' id='hov32747'>Google</div>
How to :
Apply a function when I hover on Google or Wikipedia
Retrieve the div corresponding to the hover (id, text, position on the page etc.)
I tried to put a random id and put some regex but it doesn't work well
I thank you in advance
Since you are starting out with JavaScript, I'd suggest refrain JQuery for now and understand how the language itself works.
The following code adds an eventListener to all elements with class hover, the functionality of which is in onHover method
const onHover = (e) => {
const id = e.target.id;
const text = e.target.textContent;
console.log(id, text);
}
const hover = document.querySelectorAll(".hover");
hover.forEach(item => item.addEventListener("mouseover", onHover));
<div class='hover' id='hov33749'>Wikipedia</div>
<div class='hover' id='hov32747'>Google</div>
See JQuery documentation
.hover()
$(".hover").each(function(){
$(this).hover(function(){
// Do something ...
console.log("Text: " + $(this).text() + ", Id: " + $(this).attr("id"));
});
});

Toggle images on click with associated IDs in jQuery

I have a repeated component with a control that toggles between displaying 2 images (mobile image and desktop image). I need each control to only toggle the component it is in, and function independently from every other component.
I am able to generate unique ids for all the controls, unique ids for all the images, and on click I am able to add/remove classes as well as show/hide images. My problem is that I don't know how to associate the toggle control id to the image id so that I'm only changing one component. Right now I am targeting the class (which is the same for every component) so everything toggles when you click the control.
This is inside Wordpress using Visual Composer, so I don't believe I am able to use a loop to render the repeated components.
JSFiddle Here
below is a single component, which would be repeated a number of times
<div class="wrapper">
<div class="platform-toggle">
<div class="mobile-toggle">
mobile
</div>
<div class="desktop-toggle">
desktop
</div>
</div>
<div class="platform-images">
<img class="mobile-image" src="https://via.placeholder.com/100x100.png?text=mobile" />
<img class="desktop-image" src="https://via.placeholder.com/100x100.png?text=desktop" />
</div>
</div>
$.each($('.platform-toggle'), function(ind) {
$(this).attr('id', 'platform-toggle_' + parseInt(ind + 1));
});
$.each($('.mobile-toggle'), function(ind) {
$(this).attr('id', 'mobile-toggle_' + parseInt(ind + 1));
});
$.each($('.desktop-toggle'), function(ind) {
$(this).attr('id', 'desktop-toggle_' + parseInt(ind + 1));
});
$.each($('.mobile-image'), function(ind) {
$(this).attr('id', 'mobile-image_' + parseInt(ind + 1));
});
$.each($('.desktop-image'), function(ind) {
$(this).attr('id', 'desktop-image_' + parseInt(ind + 1));
});
$(".mobile-toggle").click(function() {
if ($(".mobile-toggle").hasClass("inactive")) {
$(".mobile-toggle").removeClass("inactive");
$(".mobile-toggle").addClass("active");
$(".mobile-image").show()
$(".desktop-toggle").removeClass("active");
$(".desktop-toggle").addClass("inactive");
$(".desktop-image").hide()
}
});
$(".desktop-toggle").click(function() {
if ($(".desktop-toggle").hasClass("inactive")) {
$(".desktop-toggle").removeClass("inactive");
$(".desktop-toggle").addClass("active");
$(".desktop-image").show()
$(".mobile-toggle").removeClass("active");
$(".mobile-toggle").addClass("inactive");
$(".mobile-image").hide()
}
});
You would use $(this) selector for this case, and jQuery has lot of functions for finding parent, sibling, children, next, prev or etc elements.
I've changed your fiddle and added new selectors.
JSFiddle
$(".mobile-toggle").click(function() {
if ($(this).hasClass("inactive")) {
$(this).removeClass("inactive");
$(this).addClass("active");
//find current toggle element parent, then find next element(wrapper of the images) and finally find children image.
$(this).parent('.platform-toggle').next('.platform-images').children('.mobile-image').show();
$(this).siblings('.desktop-toggle').removeClass("active");
$(this).siblings('.desktop-toggle').addClass("inactive");
$(this).parent('.platform-toggle').next('.platform-images').children('.desktop-image').hide();
}
});
You may use parent div of the specific control in this way:
$(".mobile-toggle").click(function() {
var parentObj=$(this).closest(".wrapper");
if ($(parentObj).find(".mobile-toggle").hasClass("inactive")) {
$(parentObj).find(".mobile-toggle").removeClass("inactive");
$(parentObj).find(".mobile-toggle").addClass("active");
$(parentObj).find(".mobile-image").show()
$(parentObj).find(".desktop-toggle").removeClass("active");
$(parentObj).find(".desktop-toggle").addClass("inactive");
$(parentObj).find(".desktop-image").hide()
}
});
$(".desktop-toggle").click(function() {
var parentObj=$(this).closest(".wrapper");
if ($(parentObj).find(".desktop-toggle").hasClass("inactive")) {
$(parentObj).find(".desktop-toggle").removeClass("inactive");
$(parentObj).find(".desktop-toggle").addClass("active");
$(parentObj).find(".desktop-image").show()
$(parentObj).find(".mobile-toggle").removeClass("active");
$(parentObj).find(".mobile-toggle").addClass("inactive");
$(parentObj).find(".mobile-image").hide()
}
});
This should be exactly the same as #Slim's answer, but with simplified code that doesn't re-select the same elements again and again. The $() selector in jQuery is fast, but there's no reason to keep selecting $(this) 7 times if we don't have to.
$(".mobile-toggle").click(function() {
var $this = $(this);
if ($this.hasClass("inactive")) {
$this.removeClass("inactive").addClass("active");
//find current toggle element parent, then find next element(wrapper of the images) and finally find children image.
var $platformImgs = $this.parent('.platform-toggle').next('.platform-images')
$platformImgs.children('.mobile-image').show();
$this.siblings('.desktop-toggle').removeClass("active").addClass("inactive");
$platformImgs.children('.desktop-image').hide();
}
});

jQuery slideDown not working on element with dynamically assigned id

EDIT: I cleaned up the code a bit and narrowed down the problem.
So I'm working on a Wordpress site, and I'm trying to incorporate drop-downs into my menu on mobile, which means I have to use jQuery to assign classes and id's to my already existing elements. I have this code that already works on premade HTML, but fails on dynamically created id's.
Here is the code:
...
var menuCount = 0;
var contentCount = 0;
//find the mobile menu items
var submenus = $('[title="submenu"]');
if (submenus.length && submenus.parent('.fusion-mobile-nav-item')) {
console.log(submenus);
submenus.addClass('dropdown-title').append('<i id="dropdown-angle" class="fa fa-angle-down" aria-hidden="true"></i>');
submenus.each(function() {
$(this).attr("href", "#m" + menuCount++);
})
var content = submenus.parent().find('ul.sub-menu');
content.addClass('dropdown-content');
content.each(function() {
$(this).attr("id", "m" + contentCount++);
})
}
$(document).on('click', '.dropdown-title', function(e) {
var currentAttrValue = $(this).attr('href');
if ($(e.target).is('.d-active') || $(e.target).parent('.dropdown-title').is('.d-active')) {
$(this).removeClass('d-active');
$(currentAttrValue).slideUp(300).removeClass('d-open');
} else {
$('.dropdown-title').removeClass('d-active');
$('.dropdown-content').slideUp(300).removeClass('d-open');
$(this).addClass('d-active');
console.log($(currentAttrValue));
//THIS LINE FAILS
$(currentAttrValue).slideDown(300).addClass('d-open');
}
e.preventDefault();
});
I've registered the elements with the class dropdown-title using $(document).on(...) but I can't figure out what I need to do to register the elements with the custom ID's. I've tried putting the event callback inside the .each functions, I've tried making custom events to trigger, but none of them will get the 2nd to last line of code to trigger. There's no errors in the console, and when I console log the selector I get this:
[ul#m0.sub-menu.dropdown-content, context: document, selector: "#m0"]
0
:
ul#m0.sub-menu.dropdown-content
context
:
document
length
:
1
selector
:
"#m0"
proto
:
Object[0]
So jQuery knows the element is there, I just can't figure out how to register it...or maybe it's something I'm not thinking of, I don't know.
If you are creating your elements dynamically, you should be assigning the .on 'click' after creating those elements. Just declare the 'on click' callback code you posted after adding the ids and classes instead of when the page loads, so it gets attached to the elements with .dropdown-title class.
Check this jsFiddle: https://jsfiddle.net/6zayouxc/
EDIT: Your edited JS code works... There also might be some problem with your HTML or CSS, are you hiding your submenus? Make sure you are not making them transparent.
You're trying to call a function for a attribute, instead of the element. You probably want $(this).slideDown(300).addClass('d-active'); (also then you don't need $(this).addClass('d-active'); before)
Inside submenus.each loop add your callback listener.
As you are adding the class dropdown-title dynamically, it was not available at dom loading time, that is why event listener was not attached with those elemnts.
var menuCount = 0;
var contentCount = 0;
//find the mobile menu items
var submenus = $('[title="submenu"]');
if (submenus.length && submenus.parent('.fusion-mobile-nav-item')) {
console.log(submenus);
submenus.addClass('dropdown-title').append('<i id="dropdown-angle" class="fa fa-angle-down" aria-hidden="true"></i>');
submenus.each(function() {
$(this).attr("href", "#m" + menuCount++);
// add callback here
$(this).click( function(e) {
var currentAttrValue = $(this).attr('href');
if ($(e.target).is('.d-active') || $(e.target).parent('.dropdown-title').is('.d-active')) {
$(this).removeClass('d-active');
$(currentAttrValue).slideUp(300).removeClass('d-open');
} else {
$('.dropdown-title').removeClass('d-active');
$('.dropdown-content').slideUp(300).removeClass('d-open');
$(this).addClass('d-active');
console.log($(currentAttrValue));
$(currentAttrValue).slideDown(300).addClass('d-active');
}
e.preventDefault();
});
})
var content = submenus.parent().find('ul.sub-menu');
content.addClass('dropdown-content');
content.each(function() {
$(this).attr("id", "m" + contentCount++);
})
}
Turns out my problem is that jQuery is adding to both the mobile menu and the desktop menu, where the desktop menu is being loaded first when I search for that ID that's the one that jQuery finds. So it turns out I was completely wrong about my suspicions.

JScrollPane Plugin - Reinitialize on Collapse

I'm trying to get JScrollPane to reinitialize on expand/collapse of my accordion found here. You can demo the accordion by clicking on one of the parents (Stone Tiles, Stone Sinks, Stone Wall Clading, etc).
Right now I set it as a click event using the following JQuery...
var pane = $('.menuwrap')
pane.jScrollPane();
var api = pane.data('jsp');
var i = 1;
$("ul#widget-collapscat-5-top > li.collapsing").click(function() {
$(this).delay(3000);
api.reinitialise();
});
It seems to work when you click the parent the second time, but not the first. I have no idea why but I went into trying to edit the JS for the accordion so that I can add this function when the collapse is complete (as opposed to trying to do this click workaround). The collapse JS can be viewed here.
I tried to add the JS for the reinitialize function here, but I think I'm not doing something properly.
May you point me in the right direction?
Thanks!
The api.reinitialise() is working properly. What is happening is that it updates the size when you click, and at this moment the element is not expanded yet. You may notice that if you expand, colapse and expand again the same section, nothing happens. But if you expand one and then click another one, the ScrollPane will adjust to the size of the first expanded element.
You can solve this with events: place $(this).trigger('colapseComplete') when the colapse ends. Then you can use:
//Listening to the colapseComplete event we triggered above
$("#widget-collapscat-5-top > li.collapsing").on('colapseComplete', function() {
api.reinitialise();
});
Maybe you can alter the addExpandCollapse function to call the reinitialise function at the end of each of its click actions this way :
function addExpandCollapse(id, expandSym, collapseSym, accordion) {
jQuery('#' + id + ' .expand').live('click', function() {
if (accordion==1) {
var theDiv = jQuery(this).parent().parent().find('span.collapse').parent().find('div');
jQuery(theDiv).hide('normal');
jQuery(this).parent().parent().find('span.collapse').removeClass('collapse').addClass('expand');
createCookie(theDiv.attr('id'), 0, 7);
}
jQuery('#' + id + ' .expand .sym').html(expandSym);
expandCat(this, expandSym, collapseSym);
api.reinitialise(); // HERE
return false;
});
jQuery('#' + id + ' .collapse').live('click', function() {
collapseCat(this, expandSym, collapseSym);
api.reinitialise(); // and HERE
return false;
});
}
and to be on a safer side, make sure you have the var api = pane.data('jsp'); line before the above piece of code anywhere in the file.

How to simplify code to add new elements to a page with jQuery?

I've written the following code to add a clickable "link button" to a section of my page.
var linkButtonHtml = "<a data-makeId='" + makeId + "' href='javascript:expandMake(" + makeId + "," + categoryId + ")'>+</a> " + makeName;
var divHtml = "<div style='display:none' class='models' data-makeId='" + makeId + "'></div>" + "<br/>";
html += linkButtonHtml + divHtml;
$('#linkDiv').html(html);
The code works fine, but it's ugly and difficult to read with all the string concatenation.
As you can see, I am building anchor elements and div elements with string concatenation. The target of my anchor element is a javascript function invocation with two arguments. Is there a good jQuery way to improve the readability of this code?
I'm not sure if this really improves readability is here is a 100% jQuery solutions:
$(html)
.append(
$('<a />')
.attr('data-makeId', makeId)
.attr('href', 'javascript:void(0);')
.click(function(event)
{
// Prevent clicking the link from leaving the page.
event.preventDefault();
expandMake(makeId, categoryId);
})
.text('+'))
.append(
document.createTextNode(makeName)
)
.append(
$('<div />')
.addClass('models')
.attr('data-makeId=', makeId)
.hide());
Where "html" in $(html) is the html variable you have in your sample.
jQuery offers an option for a second argument when creating elements.
var linkButton = $('<a>',{'data-makeId':makeId,
href:'#',
click:function(){expandMake( makeId, categoryId )},
text:'+'
});
var div = $('<div>',{ style:'display:none',
'class':'models',
'data-makeId': makeId
})
.after('<br>');
$('#linkDiv')
.empty()
.append(html)
.append(linkButton)
.append( makeName )
.append(div);
EDIT: Fixed an issue where makeName was not appended.
Only real way is either abstracting some of your tag generation or spread the script out a little to make it more readable : http://jsfiddle.net/3dYPX/1/
Your also using jQuery so you might want to consider changing the way you trigger the javascript. Try looking into the .live() event. (Ill just get an example up, not that its very important)
Using live event for unobtrusive javascript:
http://jsfiddle.net/3dYPX/2/
It is all being done inside of the onLoad event at the moment, just to use as an example.
Use a template library such as
jQuery Templates instead of inlining
HTML.
Instead of using "javascript:" URLs, attach event handlers to the generated DOM fragments.
Refrain from using inline styles.
Something like:
$('#linkDiv')
.empty()
.append($.tmpl(myTemplate, {
makeId: makeId,
makeName: makeName,
categoryId: categoryId
}))
.click(function () {
var makeId = $(this).attr("data-makeId");
if (makeId) {
expandMake(makeId, $(this).attr("data-categoryId"));
}
});
Where myTemplate has the content:
${makeName}
<div class="models" data-makeId="${makeId}"></div>
Instead of using an inline style to initially hide the models, hide them all with a general CSS rule, and then selectively show them with a class:
.models { display: none }
.models.shown { display: block }
Just add the "shown" class to show a certain block of models.
Here you go:
$('#linkDiv').empty().append([
$('+').data('makeId', makeId).click(function(e) {
e.preventDefault();
expandMake(makeId, categoryId);
})[0],
$('<span>').text(makeName)[0],
$('<div class="models">').data('makeId', makeId).hide()[0],
$('<br>')[0]
]);
Live demo: http://jsfiddle.net/cncbm/1/
Consider this:
$('#linkDiv').data({'makeId': makeId, 'categoryId': categoryId}).empty().append([
$('+').click(expandMake)[0],
$('<span>').text(makeName)[0],
$('<div class="models">').hide()[0]
]);
So, you define that data-stuff on the parent DIV (the common parent) and you re-factor the expandMake function so that it reads those data-values from the parent DIV instead of passing them as arguments.

Categories

Resources