Tobias Glaus
Tobias Glaus

Reputation: 3628

Function gets executed for every previous element aswell

I am currently creating a codepen, where you can change the color,background-color,text,... of each element on the page when opening the contextmenu.

I store the element, which I right-clicked in a variable:

var x = event.clientX, y = event.clientY,
    efp = document.elementFromPoint(x, y);

When I return the value of that variable efp in the console, it returns me the correct element.


Now the problem:
Changing the color of, for example a text element, works fine. But when I try to do that another time, it not only changes the element, but the element I previously changed aswell.

Something like this:
Change color of p -> changes p
Change background of section -> changes section AND p
Change color of h1 -> changes h1 AND section AND p


And here's my full code:

$(document).on("contextmenu", function(e){
  $('.custom-menu li').show(0);
  e.preventDefault();
  var el = e.target.nodeName;
  var x = event.clientX, y = event.clientY,
      efp = document.elementFromPoint(x, y);
  console.log(efp);
  switch(el){
    case "P":
      ctxP();
      break;
    case "CONTENT":
      ctxCon();
      break;
    case "HEADER":
      ctxHead();
      break;
    case "FOOTER":
      ctxFoot();
      break;
  }
  $(".custom-menu li").on("click", function(){
    switch($(this).attr("data-action")) {
      case "color": 
        colorPick();
        break;
      case "bgcolor": 
        bgcolorPick();
        break;
      case "text": 
        textChange();
        break;
    }
    $(".custom-menu").fadeOut(100);
  });
  $(".custom-menu").finish().fadeIn(100).css({
    top: e.pageY + "px",
    left: e.pageX + "px"
  });
  function ctxP(){
    $('.custom-menu li[data-action=bgcolor]').hide();
  }
  function ctxCon(){
    $('.custom-menu li[data-action=color]').hide();
    $('.custom-menu li[data-action=text]').hide();
  }
  function ctxHead(){
    $('.custom-menu li[data-action=color]').hide();
    $('.custom-menu li[data-action=text]').hide();
  }
  function ctxFoot(){
    $('.custom-menu li[data-action=color]').hide();
    $('.custom-menu li[data-action=text]').hide();
  }
  function colorPick(){
    $(".overlay").fadeIn();
    $(".colorpicker").fadeIn().css('display', 'flex');
    $(".color").click(function(){
      var color = $(this).css('background-color');
      $(efp).css('color', color);
      $(".colorpicker, .overlay").fadeOut();
    });
  }
  function bgcolorPick(){
    $(".overlay").fadeIn();
    $(".colorpicker").fadeIn().css('display', 'flex');
    $(".color").click(function(){
      var color = $(this).css('background-color');
      $(efp).css('background-color', color);
      $(".colorpicker, .overlay").fadeOut();
    });
  }
  function textChange(){
    $(".overlay, .textChange").fadeIn();
    $('.textChange input').keypress(function(e) {
      var text = $(this).val();
      if(e.keyCode == 13){
        $(efp).text(text);
        $(".overlay, .textChange").fadeOut();
        $(this).val("");
      }
    });
  }
});

$(document).click(function(e){    
  if (!$(e.target).parents(".custom-menu").length > 0) {
    $(".custom-menu").fadeOut(100);
  }
});
*{
  margin:0;
  padding:0;
  box-sizing:border-box;
}
header,content,footer{
  display:block;
  width:100%;
  height:200px;
  padding:50px 20%;
}
header{
  background:#efefef;
}
content{
  background-color:#cccccc;
}
footer{
  background-color:#afafaf
}

.custom-menu {
  display: none;
  z-index: 1000;
  position: absolute;
  overflow: hidden;
  border: 1px solid #CCC;
  white-space: nowrap;
  background: #FFF;
  color: #333;
}
.custom-menu li {
  padding: 8px 12px;
  cursor: pointer;
  list-style-type: none;
  transition: all .3s ease;
  user-select: none;
}
.custom-menu li:hover {
  background-color: #DEF;
}
.colorpicker{
  position:fixed;
  z-index:20;
  display:flex;
  display:none;
  flex-wrap:wrap;
  top:10%;
  left:10%;
  height:80%;
  width:80%;
  background-color:white;
  -webkit-box-shadow: 0px 0px 15px 0px rgba(0,0,0,0.5);
  -moz-box-shadow: 0px 0px 15px 0px rgba(0,0,0,0.5);
  box-shadow: 0px 0px 15px 0px rgba(0,0,0,0.5);
}
.color{
  display:block;
  flex-grow:1;
  transition:.5s ease-in-out;
  cursor:pointer;
}
.textChange{
  display:none;
  position:fixed;
  z-index:20;
  top:calc(50% - 25px);
  width:20%;
  height:50px;
  left:40%;
}
.textChange input{
  height:100%;
  width:100%;
  padding:10px;
  font-size:1.1em;
}
.overlay{
  display:none;
  position:fixed;
  z-index:10;
  width:100%;
  background-color:black;
  height:100%;
  opacity:.5;
  top:0;
}
.disabled{
  pointer-events:none;
  color:#a3a3a3;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div class="wrapper">
  <header><p>Hello</p></header>
  <content><p>Peter</p></content>
  <footer><p>Hallo</p></footer>
</div>


<div class="overlay"></div>
<div class="textChange">
  <input type="textarea" placeholder="Type new text in here">
</div>

<div class="colorpicker">
  <div class="color" style="background-color:#1abc9c"></div>
  <div class="color" style="background-color:#16a085"></div>
  <div class="color" style="background-color:#2ecc71"></div>
  <div class="color" style="background-color:#27ae60"></div>
  <div class="color" style="background-color:#3498db"></div>
  <div class="color" style="background-color:#2980b9"></div>
  <div class="color" style="background-color:#9b59b6"></div>
  <div class="color" style="background-color:#8e44ad"></div>
  <div class="color" style="background-color:#e74c3c"></div>
  <div class="color" style="background-color:#c0392b"></div>
  <div class="color" style="background-color:#e67e22"></div>
  <div class="color" style="background-color:#d35400"></div>
  <div class="color" style="background-color:#f1c40f"></div>
  <div class="color" style="background-color:#f39c12"></div>
  <div class="color" style="background-color:#ecf0f1"></div>
  <div class="color" style="background-color:#bdc3c7"></div>
  <div class="color" style="background-color:#95a5a6"></div>
  <div class="color" style="background-color:#7f8c8d"></div>
  <div class="color" style="background-color:#34495e"></div>
  <div class="color" style="background-color:#2c3e50"></div>
</div>

<ul class='custom-menu'>
  <li data-action="color">change color</li>
  <li data-action="bgcolor">change background-color</li>
  <li data-action="text">change text</li>
</ul>

Upvotes: 1

Views: 62

Answers (2)

Serge K.
Serge K.

Reputation: 5323

You're creating a closure and attaching a new click listener on every right click within the on('contextmenu' event listener, hence this behavior. Simply move all of your declarations out of the event listener like this :

function ctxP() {
  $('.custom-menu li[data-action=bgcolor]').hide();
}

function ctxCon() {
  $('.custom-menu li[data-action=color]').hide();
  $('.custom-menu li[data-action=text]').hide();
}

function ctxHead() {
  $('.custom-menu li[data-action=color]').hide();
  $('.custom-menu li[data-action=text]').hide();
}

function ctxFoot() {
  $('.custom-menu li[data-action=color]').hide();
  $('.custom-menu li[data-action=text]').hide();
}

function colorPick() {
  $(".overlay").fadeIn();
  $(".colorpicker").fadeIn().css('display', 'flex');
  $(".color").click(function() {
    var color = $(this).css('background-color');
    $(efp).css('color', color);
    $(".colorpicker, .overlay").fadeOut();
  });
}

function bgcolorPick() {
  $(".overlay").fadeIn();
  $(".colorpicker").fadeIn().css('display', 'flex');
  $(".color").click(function() {
    var color = $(this).css('background-color');
    $(efp).css('background-color', color);
    $(".colorpicker, .overlay").fadeOut();
  });
}

function textChange() {
  $(".overlay, .textChange").fadeIn();
  $('.textChange input').keypress(function(e) {
    var text = $(this).val();
    if (e.keyCode == 13) {
      $(emo).text(text);
      $(".overlay, .textChange").fadeOut();
      $(this).val("");
    }
  });
}

$(".custom-menu li").on("click", function() {
  switch ($(this).attr("data-action")) {
    case "color":
      colorPick();
      break;
    case "bgcolor":
      bgcolorPick();
      break;
    case "text":
      textChange();
      break;
  }

  $(".custom-menu").fadeOut(100);
});

var efp;

$(document).on("contextmenu", function(e) {
  e.preventDefault();

  $('.custom-menu li').show(0);

  var el = e.target.nodeName,
    x = e.clientX,
    y = e.clientY;
  
  efp = document.elementFromPoint(x, y);

  $(".custom-menu").finish().fadeIn(100).css({
    top: e.pageY + "px",
    left: e.pageX + "px"
  });

  switch (el) {
    case "P":
      ctxP();
      break;
    case "CONTENT":
      ctxCon();
      break;
    case "HEADER":
      ctxHead();
      break;
    case "FOOTER":
      ctxFoot();
      break;
  }
});

$(document).click(function(e) {
  if (!$(e.target).parents(".custom-menu").length > 0) {
    $(".custom-menu").fadeOut(100);
  }
});
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

header,
content,
footer {
  display: block;
  width: 100%;
  height: 200px;
  padding: 50px 20%;
}

header {
  background: #efefef;
}

content {
  background-color: #cccccc;
}

footer {
  background-color: #afafaf
}

.custom-menu {
  display: none;
  z-index: 1000;
  position: absolute;
  overflow: hidden;
  border: 1px solid #CCC;
  white-space: nowrap;
  background: #FFF;
  color: #333;
}

.custom-menu li {
  padding: 8px 12px;
  cursor: pointer;
  list-style-type: none;
  transition: all .3s ease;
  user-select: none;
}

.custom-menu li:hover {
  background-color: #DEF;
}

.colorpicker {
  position: fixed;
  z-index: 20;
  display: flex;
  display: none;
  flex-wrap: wrap;
  top: 10%;
  left: 10%;
  height: 80%;
  width: 80%;
  background-color: white;
  -webkit-box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, 0.5);
  -moz-box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, 0.5);
  box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, 0.5);
}

.color {
  display: block;
  flex-grow: 1;
  transition: .5s ease-in-out;
  cursor: pointer;
}

.textChange {
  display: none;
  position: fixed;
  z-index: 20;
  top: calc(50% - 25px);
  width: 20%;
  height: 50px;
  left: 40%;
}

.textChange input {
  height: 100%;
  width: 100%;
  padding: 10px;
  font-size: 1.1em;
}

.overlay {
  display: none;
  position: fixed;
  z-index: 10;
  width: 100%;
  background-color: black;
  height: 100%;
  opacity: .5;
  top: 0;
}

.disabled {
  pointer-events: none;
  color: #a3a3a3;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div class="wrapper">
  <header>
    <p>Hello</p>
  </header>
  <content>
    <p>Peter</p>
  </content>
  <footer>
    <p>Hallo</p>
  </footer>
</div>


<div class="overlay"></div>
<div class="textChange">
  <input type="textarea" placeholder="Type new text in here">
</div>

<div class="colorpicker">
  <div class="color" style="background-color:#1abc9c"></div>
  <div class="color" style="background-color:#16a085"></div>
  <div class="color" style="background-color:#2ecc71"></div>
  <div class="color" style="background-color:#27ae60"></div>
  <div class="color" style="background-color:#3498db"></div>
  <div class="color" style="background-color:#2980b9"></div>
  <div class="color" style="background-color:#9b59b6"></div>
  <div class="color" style="background-color:#8e44ad"></div>
  <div class="color" style="background-color:#e74c3c"></div>
  <div class="color" style="background-color:#c0392b"></div>
  <div class="color" style="background-color:#e67e22"></div>
  <div class="color" style="background-color:#d35400"></div>
  <div class="color" style="background-color:#f1c40f"></div>
  <div class="color" style="background-color:#f39c12"></div>
  <div class="color" style="background-color:#ecf0f1"></div>
  <div class="color" style="background-color:#bdc3c7"></div>
  <div class="color" style="background-color:#95a5a6"></div>
  <div class="color" style="background-color:#7f8c8d"></div>
  <div class="color" style="background-color:#34495e"></div>
  <div class="color" style="background-color:#2c3e50"></div>
</div>

<ul class='custom-menu'>
  <li data-action="color">change color</li>
  <li data-action="bgcolor">change background-color</li>
  <li data-action="text">change text</li>
</ul>

Upvotes: 4

Jeremy Thille
Jeremy Thille

Reputation: 26360

It is because of this line :

$(".color").click( function(){

Every time the bgcolorPick() function is called, you attach one more click handler. So every time you click, all your click handlers execute on all the elements previously clicked, changing their colour. Detach the click handler before attaching it again to the latest clicked element :

$(".color").off("click").on("click", function(){

Updated codepen

Upvotes: 5

Related Questions