1

I'm about to start working with the mousewheel event but the only thing I can find online uses addEventListener(). I am wanting to detect is with native HTML and CSS. In other words I'm looking for something like:

<span id='fooBar' onmousewheel='alert("fooBar")'></span>

I am creating spans dynamically and injecting them into the DOM and it would work a lot better if I didn't have to run javascript to do so. Every other event I can seem to get working natively but not mousewheel. Is this possible?

Xandor
  • 440
  • 1
  • 9
  • 22

5 Answers5

3

The reason you are only finding references to addEventListener() is because that is the standard and correct way to do it (it appears that onmousewheel isn't even supported in Firefox).

While this can be made to work (in browsers that support it) using the code you show in your question (except that your span is empty so you can't initiate the event with it since the element won't have any rendered size) and I have an example of that below, the use of inline HTML event handling attributes hearkens back to the time before we had standards and should not be used. Here's a link to another answer of mine that explains why.

<span id='fooBar' onmousewheel='alert("fooBar")'>Use the mousewheel while mouse is over this</span>
Community
  • 1
  • 1
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • Well, as far as the empty that was just to give an example of what formatting I was looking for. My actual elements do have content. – Xandor May 16 '17 at 06:06
1

Inline vs. Delegation

In the demo the A side uses the standard addEventListener and registers the wheel event which is the replacement for the deprecated mousewheel event. Using the addEventListener is not only the standard but it's the most efficient if event delegation is used.

The use of onwheel as an attribute event has limited support due to the fact that using any attribute event is non-standard and discouraged. Despite this, I have included side B which uses the deprecated non-standard onmousewheel event for an attribute inline event handler. Because it's an awkwardly coded <span>, I used insertAdjacentHTML on a string that used all three quotes (i.e.', ", `). The use of a string literal was used on the a 2nd level of nested quotes, it's very messy.

Refer to this post on how the Event Object properties are utilized in event delegation.

Details are commented in the demo

Demo

// Reference the buttons 
const btnA = document.getElementById('addA');
const btnB = document.getElementById('addB');

// Reference the parent nodes
const secA = document.querySelector('section:first-of-type');
const secB = document.querySelector('section:last-of-type');

// Register the click event on buttons
btnA.addEventListener('click', addNodeA, false);
btnB.addEventListener('click', addNodeB, false);

/* Register the wheel event on section A 
|| which is the parent node of the wheeled
|| nodes. Event delegation involves one 
|| event handler for multiple event targets.
|| This is far more efficient than multiple 
|| inline event handlers.
*/
secA.addEventListener('wheel', markNode, false);

let cnt = 0;

/* Add node A to section A
|| ex. <span id="A1" class="A">A1</span>
*/
function addNodeA(e) {
  cnt++;
  var nodeA = document.createElement('span');
  nodeA.id = 'A' + cnt;
  nodeA.textContent = nodeA.id;
  nodeA.className = 'A';
  secA.appendChild(nodeA);
  return false;
}

/* Add node B to section B
|| ex. <span id="B3" class="B" onmousewheel="this.style.outline = `5px dashed red`">B3</span>
*/
function addNodeB(e) {
  cnt++;
  /* This string is wrapped in single quotes,
  || double quotes for the attributes values,
  || and backticks for the property value of
  || an attribute value. Very messy, confusing,
  || and inefficient.
  */
  var nodeB = '<span id="B' + cnt + '" class="B" onmousewheel="this.style.outline = `5px dashed red`">B' + cnt + '</span>';

  // insertAdjacentHTML is innerHTML on steroids
  secB.insertAdjacentHTML('beforeend', nodeB);
  return false;
}

function markNode(e) {

  /* If the wheeled node (i.e. e.target) is not the 
  || registered node (i.e. e.currentTarget), then...
  */
  if (e.target !== e.currentTarget) {
    var node = e.target;
    if (node.className === 'A') {
      node.style.outline = '5px dashed blue';
    } else {
      return false;
    }
  }
}
html,
body {
  width: 100%;
  width: 100%
}

fieldset {
  height: 10%;
}

main {
  border: 3px solid lime;
  height: 90%;
  min-height: 250px;
  display: flex;
}

section {
  width: 50%;
  min-height: 250px;
  outline: 3px dashed gold;
  padding: 10px 25px;
}

span {
  height: 50px;
  width: 50px;
  padding: 2px;
  text-align: center;
  font-size: 12px;
  margin: 5px;
  display: inline-block;
}

.A {
  background: rgba(0, 100, 200, .3);
}

.B {
  background: rgba(200, 100, 0, .3);
}

#addB {
  margin-left: 35%
}
<fieldset>
  <legend>addNode</legend>
  <button id='addA'>nodeA</button>
  <button id='addB'>nodeB</button>
</fieldset>
<main>
  <section></section>
  <section></section>
</main>
Community
  • 1
  • 1
zer00ne
  • 41,936
  • 6
  • 41
  • 68
  • I guess this really raises another question then. The main reason I am doing inline event handling is because I of the way I am adding the elements into the DOM. When I create my element in question, it is actually a set of 4 nested elements. Your coding example for A takes 4 lines or so per element, and compressing them would make the coding rather ugly. I use something more like B so it's about 2 lines per and easily read HTML. Is this just considered a bad way of doing this? I was trying to keep my code short and legible (it's already hitting 10s of thousands or so in total) – Xandor May 16 '17 at 06:51
  • Event delegation cost is up front, there's only one `eventListener` that covers multiple elements. Inline on 1000+ elements is 1000+ event handlers. Event delegation on 1000+ elements is 1 event handler. – zer00ne May 16 '17 at 07:33
  • The code probably can be refactored for legibility or terseness, the pattern that I write for demos are a mix of both so the important parts are clear and the steps to get there are easily followed. The extra steps to add id, class, etc. are really not necessary in most cases. With 4 nested elements, making them like B is better, but event delegation from A can still be applied, *unless* none of these clusters share a common ancestor (separate documents perhaps?) – zer00ne May 16 '17 at 07:39
  • I apologize, I meant 10s of thousands of lines of coding. I do actually need the classes for most of the elements. I can do a mix of the two, it will just add quite a bit of coding for the event listeners if it should not be done inline for any events. – Xandor May 16 '17 at 08:06
  • You got to pick your battles, I know how that goes. Are your clusters the same tags and layout or does it vary? If you have the same tags and layout for your clusters you could use [` – zer00ne May 16 '17 at 08:46
  • I'm creating a desktop like GUI. So each window has several nested elements many of which have multiple event listeners each. I'll look into template and document fragment. They may be of use, I'm not too familiar with them. – Xandor May 16 '17 at 09:28
  • Your project sounds complex, especially if you are using only JS. ` – zer00ne May 16 '17 at 09:33
  • It is very complex, and I am using Javascript and jQuery solely for the front end. Its just about complete now, this is one of the final touches. At this point I think I will create and append the elements with a string and then use js to do the event handlers. In total it may add upwards of 30 lines or so, but it sounds like it will be beneficial resource wise anyways. – Xandor May 16 '17 at 17:00
  • If you go the string route, do not use `innerHTML`, use `insertAdjacentHTML()` instead. There's [problems with `innerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML#Security_considerations) and [`insertAdjacentHTML()`](https://hacks.mozilla.org/2011/11/insertadjacenthtml-enables-faster-html-snippet-injection/) is faster. Keep in mind, event delegation can be used with `document` or `window` as the parent node. Also, it would be dead simple if you used jQuery for event delegation: use the [`.on`](http://api.jquery.com/on/) method. – zer00ne May 16 '17 at 17:45
  • Yes, I use jQuery for a large portion of the project, including the injection using `.append()` and was already researching `.on()`. I mostly think I rather this because of jQuery selector abilities. Although I am glad you said this about `innerHTML()` there are a few places where I use this so I will need to update them. – Xandor May 17 '17 at 03:35
0

You can dynamically create elements with listeners pretty easily. You just need to understand the DOM and how to attach event listeners.

The example below creates 10 spans and attaches a listener to each of them. Just hover over a span and scroll the mouse-wheel. The ID of the span will be logged to the console.

// Create 10 spans with a scroll listener.
var count = 10;
for (var i = 0; i < count; i++) {
  var span = document.createElement('SPAN');
  span.id = 'foo-bar-' + i;
  span.innerHTML = 'Foo #' + i;
  addEventListener(span, 'mousewheel', performEvent);
  document.body.appendChild(span);
}

// Event function.
function performEvent(e) {
  console.log(e.target.id);
}

// IE 8+ via http://youmightnotneedjquery.com/
function addEventListener(el, eventName, handler) {
  if (el.addEventListener) {
    el.addEventListener(eventName, handler);
  } else {
    el.attachEvent('on' + eventName, function(){
      handler.call(el);
    });
  }
}
.as-console-wrapper { max-height: 5em !important; }

span[id^="foo-bar-"] {
  border: thin solid black;
  margin: 0.25em;
}
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
  • That `addEventListener()` proxy/wrapper will fail anytime you pass an event name to it that doesn't exist in the IE <=8 world, such as `DOMContentLoaded`. – Scott Marcus May 15 '17 at 17:54
  • 1
    @ScottMarcus I know, but no one should be using anything less than IE 8. It's a security risk. Also, IE 8 came out over 8 years ago. In the Internet world, that's equivalent to a millennium. – Mr. Polywhirl May 15 '17 at 17:55
  • That's exactly my point. Why are you even showing an IE 8 patch? `.addEventListener()` is supported by every mainstream browser going back for at least 5 years. Your code can fail and just promotes the continued use of outdated software. No one should be using IE 8 at all. – Scott Marcus May 15 '17 at 17:57
  • @ScottMarcus Because IE is still supported in many major web applications. It's the last IE version that is at the very least "bearable". – Mr. Polywhirl May 15 '17 at 17:59
  • We're going to have to agree to disagree here. The actual statistics don't back that statement up. Of course, there will always be exceptions, but corporate America (and the rest of the world) writ large, migrated those apps. many years ago. Most would tell you that IE 9 is the absolute earliest version of IE that should be tolerated. – Scott Marcus May 15 '17 at 18:00
0

Try the following:

::-webkit-scrollbar {
    width: 0px;  /* remove scrollbar space */
    background: transparent;  /* optional: just make scrollbar invisible */
}
<!DOCTYPE HTML>
<html>
  <body style="overflow: auto;
max-height: 100vh;" onscroll="alert('hi')">
    <div style="height:2000px;width:100%"></div>
  </body>
</html>
A.J. Uppal
  • 19,117
  • 6
  • 45
  • 76
0

The reason you find "addEventListener" examples only is because you need to handle this cross-browser:

var supportedWheelEvent = "onwheel" in HTMLDivElement.prototype ? "wheel" : document.onmousewheel !== undefined ? "mousewheel" : "DOMMouseScroll";

Also, it's best to do this on one element only! Use delegated event listener to handle this for all of the elements that you need.

HTML Example:

<div id="delegator">
   <div class="handleWheel">handle wheel event here</div>
   <div> no wheel event handled here </div>
   <div class="handleWheel">handle wheel event here</div>
   <div> no wheel event handled here </div>
</div>

JS:

var supportedWheelEvent = "onwheel" in HTMLDivElement.prototype ? "wheel" : document.onmousewheel !== undefined ? "mousewheel" : "DOMMouseScroll";

function handler(e){
    if(e.target.classList.contains('handleWheel')){
        // handle the event here
    }
}

var d = document.getElementById('delegator');
d.addEventListener(supportedWheelEvent, handler);

Working codepen sample: https://codepen.io/Mchaov/pen/zwaMmM

Martin Chaov
  • 824
  • 7
  • 17