Width of scrollbar on hover not expanding - javascript

I am trying to expand the scrollbar width on hover.
But I am getting this console error --> scrollbar.addEventListener is not a function
JAVASCRIPT
setTimeout(function()
{
const scrollbar = document.querySelector('.articles-container::-webkit-scrollbar');
scrollbar.addEventListener('mouseover', scon, false);
scrollbar.addEventListener('mouseout', scout, false);
function scon()
{
scrollbar.style.width = '30px';
}
function scout()
{
scrollbar.style.width = '20px';
}
}, 1300);
Why am I getting this error --> scrollbar.addEventListener is not a function
Please help me

This is because document.querySelectorAll() actually returns a NodeList (an array-like structure) which does not have this method (besides, to which element in the NodeList would you add it to?).
In order to fix your issue, you need to either use document.querySelector() (notice it does not terminate with All) or extract the element you want to attach the listener to document.querySelectorAll()[<number>]

Related

Adding click listener to an array of JQuery objects

Here is my javascript:
function scrollTo(position) {
$('body').animate({
scrollTop: position
}, 500);
}
var titles = $('.title');
for(var i = 0; i < titles.length; i++) {
titles[i].click(function() {
console.log('click');
scrollTo(0);
});
}
It's supposed to select all three titles and apply a click listener that will scroll the page back to the top. Unfortunately I am not getting the message in the console nor is it scrolling.
Everywhere I have looked online has given for loops exactly like this to apply a click listener to multiple JQuery objects but for some reason it won't work for me. I'm afraid I may have made a stupid mistake but I can't find it.
The reason your code did not work was because of this line
titles[i].click(function() {
When you access a jQuery object with a index, you are technically accessing the DOM element and not the jQuery object anymore. And the DOM object does not have a click method. If you would have opened your console, you would have seen an error. If you still want to use a for loop, you will have to convert that to a jQuery object first.
$(titles[i]).click(function() {
If you would have done that, it would work as expected.
Also if you still do not want to double wrap it, you could have also used .eq to get the jQuery object from the array.
titles.eq(i).click(function() {
You need not iterate over the jQuery objects to assign event handlers. You could just do it in one line.
$('.title').click(function() {
console.log('click');
scrollTo(0);
});
Also check the version of jQuery you are using, if it is the latest then you could use on
$('.title').on('click', function() {
Also you need to make sure that the elements with class title are present on the page when you associate the events.
you can use this, should give you what your looking for.
$('.title').click(function () {
$('body,html').animate({
scrollTop: 0
}, 600);
return false;
});
also as Sushanth said, if you have a new version of jQuery you can use .on(click)

Call a function as if an event called it

I have the following function:
bringToFront : function () {
"use strict";
Desktop.appZ += 1;
this.style.zIndex = Desktop.appZ;
}
This function get's called when certain elements are clicked:
appWindow.addEventListener("mousedown", Desktop.bringToFront, false);
appWindowParent.appendChild(appWindow);
However, if I add some elements to the DOM and click them, thus increasing their z-index, and then add another element, this element will appear behind the first elements, instead of in front of them. So when I add "appWindow" to "appWindowParent", I also want to call "bringToFront" on "appWindow". I need to do this without chaining the "bringToFront" function (i.e. without adding arguments).
Thanks!
By the way, I know I could just increase the z-index manually when I create the element, but I intend to do more things in the "bringToFront" function and I don't want to duplicate that code.
You can use apply() to set the value of this inside the function
appWindowParent.appendChild(appWindow);
Desktop.bringToFront.apply(appWindow);
Detect by following code if a new element is inserted into dom
$(document).on('DOMNodeInserted', function(e) {
if (e.target.id == 'someID') {
}
});

JavaScript click event listener on class

I'm currently trying to write some JavaScript to get the attribute of the class that has been clicked. I know that to do this the correct way, I should use an event listener.
My code is as follows:
var classname = document.getElementsByClassName("classname");
var myFunction = function() {
var attribute = this.getAttribute("data-myattribute");
alert(attribute);
};
classname.addEventListener('click', myFunction(), false);
I was expecting to get an alert box every time I clicked on one of the classes to tell me the attribute but unfortunately this does not work. Can anyone help please?
(Note - I can quite easily do this in jQuery but I would NOT like to use it)
This should work. getElementsByClassName returns an Array-like object (see below) of the elements matching the criteria.
var elements = document.getElementsByClassName("classname");
var myFunction = function() {
var attribute = this.getAttribute("data-myattribute");
alert(attribute);
};
for (var i = 0; i < elements.length; i++) {
elements[i].addEventListener('click', myFunction, false);
}
jQuery does the looping part for you, which you need to do in plain JavaScript.
If you have ES6 support you can replace your last line with:
Array.from(elements).forEach(function(element) {
element.addEventListener('click', myFunction);
});
Note: Older browsers (like IE6, IE7, IE8) donĀ“t support getElementsByClassName and so they return undefined.
Details on getElementsByClassName
getElementsByClassName doesn't return an array, but a HTMLCollection in most, or a NodeList in some browsers (Mozilla ref). Both of these types are Array-Like, (meaning that they have a length property and the objects can be accessed via their index), but are not strictly an Array or inherited from an Array (meaning other methods that can be performed on an Array cannot be performed on these types).
Thanks to user #Nemo for pointing this out and having me dig in to fully understand.
With modern JavaScript it can be done like this:
const divs = document.querySelectorAll('.a');
divs.forEach(el => el.addEventListener('click', event => {
console.log(event.target.getAttribute("data-el"));
}));
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Example</title>
<style>
.a {
background-color:red;
height: 33px;
display: flex;
align-items: center;
margin-bottom: 10px;
cursor: pointer;
}
.b {
background-color:#00AA00;
height: 50px;
display: flex;
align-items: center;
margin-bottom: 10px;
}
</style>
</head>
<body>
<div class="a" data-el="1">1</div>
<div class="b" data-el="no-click-handler">2</div>
<div class="a" data-el="3">11</div>
</body>
</html>
Gets all elements by class name
Loops over all elements with using forEach
Attach an event listener on each element
Uses event.target to retrieve more information for specific element
* This was edited to allow for children of the target class to trigger the events. See bottom of the answer for details. *
An alternative answer to add an event listener to a class where items are frequently being added and removed. This is inspired by jQuery's on function where you can pass in a selector for a child element that the event is listening on.
var base = document.querySelector('#base'); // the container for the variable content
var selector = '.card'; // any css selector for children
base.addEventListener('click', function(event) {
// find the closest parent of the event target that
// matches the selector
var closest = event.target.closest(selector);
if (closest && base.contains(closest)) {
// handle class event
}
});
Fiddle: https://jsfiddle.net/u6oje7af/94/
This will listen for clicks on children of the base element and if the target of a click has a parent matching the selector, the class event will be handled. You can add and remove elements as you like without having to add more click listeners to the individual elements. This will catch them all even for elements added after this listener was added, just like the jQuery functionality (which I imagine is somewhat similar under the hood).
This depends on the events propagating, so if you stopPropagation on the event somewhere else, this may not work. Also, the closest function has some compatibility issues with IE apparently (what doesn't?).
This could be made into a function if you need to do this type of action listening repeatedly, like
function addChildEventListener(base, eventName, selector, handler) {
base.addEventListener(eventName, function(event) {
var closest = event.target.closest(selector);
if (closest && base.contains(closest)) {
// passes the event to the handler and sets `this`
// in the handler as the closest parent matching the
// selector from the target element of the event
handler.call(closest, event);
}
});
}
=========================================
EDIT: This post originally used the matches function for DOM elements on the event target, but this restricted the targets of events to the direct class only. It has been updated to use the closest function instead, allowing for events on children of the desired class to trigger the events as well. The original matches code can be found at the original fiddle:
https://jsfiddle.net/u6oje7af/23/
You can use the code below:
document.body.addEventListener('click', function (evt) {
if (evt.target.className === 'databox') {
alert(this)
}
}, false);
Yow can use querySelectorAll to select all the classes and loop through them to assign the eventListener. The if condition checks if it contains the class name.
const arrClass = document.querySelectorAll(".className");
for (let i of arrClass) {
i.addEventListener("click", (e) => {
if (e.target.classList.contains("className")) {
console.log("Perfrom Action")
}
})
}
Also consider that if you click a button, the target of the event listener is not necessaily the button itself, but whatever content inside the button you clicked on. You can reference the element to which you assigned the listener using the currentTarget property. Here is a pretty solution in modern ES using a single statement:
document.querySelectorAll(".myClassName").forEach(i => i.addEventListener(
"click",
e => {
alert(e.currentTarget.dataset.myDataContent);
}));
All the above-mentioned answers are correct and I just want to demonstrate another short way
document.querySelectorAll('.classname').forEach( button => {
button.onclick = function () {
// rest of code
}
});
Here's a different approach for many DOM elements with the same class name by selecting path key in the eventListener object.
Add an event listener to the immediate parent class wrapping all the child elements with the same class and get the path by selecting the first key in the event object.
E.g say you want to edit table cells of a table
//Select tbody element & add event listener
let tbody = document.querySelector('tbody');
tbody.addEventListener("click", function(e,v) {
// Get the clicked cell
let cell = e.path[0];
// Get the current cell value
let cellValue = cell.innerHTML;
//Rest of code goes here
}

jQuery .is(":hover") In IE8

I am having an issue checking the state of a div in IE8. I want to check if the mouse is currently hovering over some divs or not. Currently in IE8, I get the following error: Syntax error, unrecognized expression: hover. Below is the jQuery that is causing the error:
// This function will close the slideout of widgets
function CloseWidgetPanel()
{
if (!$("#widgets").is(":hover") && !$(".widgetPanel").is(":hover"))
{
if ($("#widgets").is(":animated"))
{
$("#widgets").stop(true, true);
}
$("#widgets").hide("slide", { direction: "right" }, 300);
}
else
{
// We are currently hovering over a panel, so check back in 2 seconds.
setTimeout(CloseWidgetPanel, 2000);
}
}
Alternative way:
$(".widgetPanel, #widgets").hover(function() {
$(this).toggleClass('hover')
});
Then:
if (!$("#widgets").is(":hover") && !$(".widgetPanel").is(":hover"))
change to
if (!$("#widgets").hasClass('hover') && !$(".widgetPanel").hasClass('hover'))
jQuery does not implement the :hover selector and IE8 doesn't support queryselectorall, therefore it fails. You'll have to find another way to detect that the element is currently being hovered over such as a mouseenter and leave event that sets a global (or parent scope) variable or applies a state class/attribute to the element.

Set time delay between two frames on mouseover

I have to display two images for single mouseover. So when I mouseover to the image, first, the image is displayed then with a time delay of 5000, the image is needed to display for that same hover. Now on mouseout display the original image.
I am not so familiar with JavaScript and jQuery.
Can someone please give me some idea about how to do this.
What i did is,
$('.image1').mouseover(function() {
setInterval($(this).removeClass(.image1).addClass('image-over1'),5000);
$(this).removeClass(.image1).addClass('image-over2');
});
$('.image1').mouseout(function() {
$(this).removeClass('image-over1');
$(this).removeClass('image-over2').addClass(item);
});
$('.image1').click(function(){
document.location='index.php?page='index.php';
})
The .hover() function lets you specify both mouseover/mouseout at the same time, and you need to make a function for the setInterval:
$('.image1').hover(function(evt) {
// mouse over function.
// DOM Element that got the mouseover.
var target = evt.target;
if (target.timer) {
clearTimeout(target.timer);
target.timer = null;
}
target.timer = setInterval(function() {
// $(this) will not work here, since 'this' has changed.
// depending on your css you shouldn't need to remove the '.image1'
// class, just make sure .image-over1 and .image-over2 are
// stronger selectors, or occur after .image1
$('.image1').addClass('image-over2');
// at this point your element will be (just guessing <img>, could be
// anything really:
// <img class="image1 image-over1 image-over2" .../>
// it's absolutely fine for the image to have all those classes as
// long as your css is correct.
}, 5000);
$('.image1').addClass('image-over1');
}, function(evt) {
// mouse out function.
// DOM Element that got the mouseout.
var target = evt.target;
if (target.timer) {
clearTimeout(target.timer);
target.timer = null;
}
$('.image1').removeClass('image-over1');
$('.image1').removeClass('image-over2');
});
$('.image1').click(function(){ document.location='index.php?page='index.php'; })
First of all, I think there's a problem in your approach; if you remove the "image1" class from the element on a mouseover, then that element won't be matched by the $(".image1") selector for the mouseout. Is there a reason you need to remove it? If you do (i.e. if there is something defined on the class in the CSS that you need to disable), is there some other selector you could match on?
As to the time delay, if you're using a jQuery version greater than 1.4, you can use the .delay() function:
$('.image1').mouseover(function() {
$(this).addClass('image-over1').delay(5000).addClass('image-over2');
});
Perhaps you to create an animated GIF of these images??? Then use a code similar to here: http://www.netmechanic.com/news/vol3/design_no10.htm
Even if the images are generated on fly, it is possible to programtically generate animated gif in PHP - see http://php.net/manual/en/function.imagegif.php

Categories

Resources