0

I'm building a website in French, where some (but not all) articles are translated in English, Spanish and/or German. Each one of these articles should have a little flag representing the available translation. The idea is then that the user clicks on, say, the German flag, and the content is replaced with the German version of the text, with jquery. The flags should also disappear and be replaced with something like an X (aka a 'close' button). Once this X is clicked, the French version should again be displayed.

My jQuery skills being close to nil, I got this far:

$('.flags button').click(function() {
  $("p:lang(fr)").hide();
  $("p:lang(gb)").show();
  $('span.flags').html('<button type="button" class="close"></button>');
});
$('button.close').click(function() {
  $("p:lang(fr)").show();
  $("p:lang(gb)").hide();
});

The X does not work, though I might figure it out sooner or later. Still, is there a better way to proceed?

Thanks a lot!

  • You'll want to use [event delegation](http://api.jquery.com/on/#direct-and-delegated-events) to get your `close` button to work. – Dave Apr 05 '16 at 15:06

2 Answers2

0

You should use jQuery .on() as your button.close is dynamically created as a result of other function. So after change your code will become,


    $(document.body).on('click','button.close',(function() {
      $("p:lang(fr)").show();
      $("p:lang(gb)").hide();
    });

$('button.close').click(function() { //your code })

This will register click handler only if your HTML element is present at the time of parsing HTML document.

For more information check this blog post

Also check, http://api.jquery.com/on/

Ketan
  • 242
  • 1
  • 11
0

Bind the event handler to the wrapper of the dynamic content instead of the content that does not yet exist (and thus cannot be bound to)

$('.flags button').click(function() {
  $("p:lang(fr)").hide();
  $("p:lang(gb)").show();
  $('span.flags').html('<button type="button" class="close"></button>');
});
$('span.flags').on('click','button.close',function() {
  $("p:lang(fr)").show();
  $("p:lang(gb)").hide();
});

EDIT: A better way (perhaps)

To answer your question, is there a better way? Well perhaps if more flexible/modular is a better way then we might do that.

Here is the example in action: https://jsfiddle.net/MarkSchultheiss/y11qpd1L/

Bottom line is you want a language selector. We can debate what languages you have/want but really that depends on your data; so let's let the data determine the "flags" and you can decide if you wish to keep the "default" - I have simply put that in the data as well (in the markup).

Here is a sample markup: It is less than trivial because, well people do that often and I wanted to show a "group" of "buttons/flags" for each article and make it easy to see that. Notice the data-languagedefault="fr" on the .article element. Now the buttons/flags whatever you call them outside the "Default" one (your X button) are not shown, they are generated from the content's data. You can enhance that to use images etc. but I used text for simplicity in the example.

<div id="articles">
  <div class="article" data-languagedefault="fr">
    <div class="flags">
      <ul class="myflags">
        <li>
          <button class="default" type="button">Default</button>
        </li>
      </ul>
    </div>
    <div class="container">
      <span class="content" lang="fr" data-language="fr">I am french</span>
      <span class="content" lang="gb" data-language="gb">I am gb</span>
      <span class="content" lang="tr" data-language="tr">I am tr</span>
      <span class="content" lang="en-us" data-language="en-us">I am en-us</span>
      <span class="content" lang="en-gb" data-language="en-gb">I am en-gb</span>
    </div>
  </div>
</div>

Now how do we utilize that? We create some buttons from the .content spans data attributes, then use those to drive what is showing etc.

I borrowed the jQuery extension filterByData from an answer on this page: https://stackoverflow.com/a/22209579/125981

$(document).ready(function() {
  (function($) {
    $.fn.filterByData = function(prop, val) {
      var $self = this;
      if (typeof val === 'undefined') {
        return $self.filter(function() {
          return typeof $(this).data(prop) !== 'undefined';
        });
      }
      return $self.filter(function() {
        return $(this).data(prop) == val;
      });
    };
  })(window.jQuery);
  // setup all flags from data
  $('#articles').find('.article').find('.content').each(function(index) {
    var lang = $(this).data('language');
    $('.flags').find('.myflags')
      .append('<li><button class="flag" data-language="' + lang + '" type="button">' + lang + '</button></li>');
  });
  // show active button
  $('li').on('activate', function() {
    $(this).addClass('active').siblings().removeClass('active');
  });
  //set currently active language indicator and language
  function setLang(me) {
    var lang = $(me).data('language');
    $(".content:lang(" + lang + ")").show().siblings('.content').hide();
    $(me).parent('li').trigger('activate');
    var defaultLang = $(me).parents('.article').eq(0).data('languagedefault');
    var isDefault = defaultLang == lang;
    $('#articles').find('.article').find('.myflags').find('button.default').toggle(!isDefault);
  }
  $('#articles').find('.article').find('.myflags').on('click', 'button.flag', function() {
    setLang(this);
  });
  $('#articles').find('.article').find('.myflags').on('click', 'button.default', function() {
    var defaultLang = $(this).parents('.article').eq(0).data('languagedefault');
    var flag = $('.flag').filterByData('language', defaultLang);
    setLang(flag);
  });
});

EDIT: Note, if you want the "default" to be selected, simply trigger the click on that "button/flag"

$('#articles').find('.article').find('.myflags').find('button.default').trigger('click');

OR, even on some other language (as an option)

$('.flag').filterByData('language', 'gb').trigger('click');

Community
  • 1
  • 1
Mark Schultheiss
  • 32,614
  • 12
  • 69
  • 100