0
142
2018-11-21

SocialLocker - даем доступ к контенту только через Social Sharing

Дать доступ для загрузки файла только только после того как пользователь "зашарил" текущую страницу - данный пост посвящается тому как реализовать данный замысел при помощи HTML, CSS и jQuery.
Понравилась страница? Поставь свою оценку!
PLUGIN_STAR_RATINGS.SCORE_TEXTPLUGIN_STAR_RATINGS.VOTES_TEXT

В ходе работы мне приходиться посещать довольно много блогов, и то, что я видел на многих из них, - это "панелька" для скрытия контента. Принцип работы данной панельки достаточно прост. Например, чтобы разблокировать кнопку загрузки файла, или прочесть определенную часть страницы необходимо нажать на одну из кнопок шаринга на данной панели.

Один из многочисленных видов панели Social Locker

Данная панель в GitHub’e называется Social Locker (в дальнейшем так и будем её именовать) и является отличным вариантом для раскрутки Вашего ресурса.

Я сделал больше исследований в чтобы найти подходящий блокировщик контента, но большинство из них - платные плагины или очень простые варианты на Git’e.

Поэтому я решил написать свою собственную версию, основу для которой я взял у данного товарища по цеху:

https://codeshack.io/create-your-own-content-locker-html-javascript/

Она не требует большого количества кода, и вы можете настроить ее так, как хотите, хотя вам понадобится опыт работы в HTML, CSS, и jQuery.

Принцип работы моего варианта Social Locker

Допустим у Вас на странице имеется файл который возможно понадобиться пользователю и он захочет его загрузить.

Блок к которому применен Social Locker выглядит вот так:

Внешний вид блока SocialLocker

Пользователь наводит курсор мышки на данный блок и перед ним благодаря анимации открывается вот такая панелька с кнопками для шаринга страницы:

Вид блока Social Locker после наведения на него курсора

После того как пользователь сделал клик по выбранной им социальной сети появится окно iframe от AddToAny в котором пользователь и совершит шаринг страницы.

После шаринга страницы появится попап окно c текстом. "Спасибо что поделился"

Выглядит данное окно вот так:

AddtoAny благодарственное окно после шаринга страницы

Внимательно изучив файл по пути

/SDStudio_SocialLocker_Plus_AddToAny/script.js

В строке 38, Вы увидите код раскомментировав который (и заменив в нем значения на свои) у Вас появится возможность встраивать свою рекламу от Google в данное попап окно.

После того как попап окно исчезнет Smart Locker будет отключен, и у пользователя появится возможность загрузить файл. Причем достаточно один раз разшарить страницу и смарт локер будет снят со всех "залокированных" ссылок.

Как подключить к себе на сайт

В начале Вам нужно скачать мой смарт локер по ссылке ниже:

Загрузить SDStudio Smart Locker:

И соответственно подключить файлы style.css и script.js из загруженной папки к своему сайту.

Мой скрипт предполагает что на Вашем сайте уже подключены FontAwesome и AddToAny.

При написании поста Вам необходимо поместить ссылку для применения Social Locker в блок:

<div class = "sociallocker" >
    <a href = "/file.zip" > 
        Загрузить файл
    </a> 
</div>

Содержимое файла style.css:

.sociallocker {
    background-color: #EEEEEE;
    text-align: center;
    position: relative;
    max-width: 500px;
    height: 120px;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    border-radius: 10px;
}

.sociallocker-overlay {
    background-color: rgba(0, 0, 0, 0.6);
    font-size: 20px;
    padding-right: 15px;
    padding-left: 15px;
    font-weight: bold;
    color: #ffffff;
    transition: all 0.2s ease;
}

.sociallocker-overlay i {
    margin-right: 10px;
    margin-left: 25px;
}

.sociallocker:hover .sociallocker-overlay {
    top: -100%;
    transition: all 0.2s linear;
}

.sociallocker:hover .sociallocker-content {
    top: 100%;
    transition: all 0.2s linear;
}

.sociallocker-content a {
    display: inline-block;
    text-decoration: none;
    padding: 10px 20px;
    background-color: #777777;
    color: #f9f9f9;
    border-radius: 4px;
    font-weight: bold;
}

.sociallocker-overlay,
.sociallocker-content,
.sociallocker-links {
    position: absolute;
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    top: 0;
    left: 0;
}

.sociallocker-content {
    background-color: #ccc;
    transition: all 0.2s ease;
}

.social-1 {
    text-decoration: none;
    color: #ffffff;
    display: inline-block;
    width: 60px;
    height: 60px;
    overflow: hidden;
    margin-right: 5px;
}

.social-1 i {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
}

.social-1:hover i {
    background-color: rgba(0, 0, 0, 0.1);
    transform: scale(1.2);
    transition: all 0.2s;
}

.fb {
    background-color: #4561A8;
}

.tw {
    background-color: #17ADEA;
}

.gp {
    background-color: #BF3B28;
}

.in {
    background-color: #1679B1;
}

.pi {
    background-color: #D9303C;
}

.su {
    background-color: #E84930;
}

div# a2apage_thanks_kit {
    min - height: 50 px;
}

@media screen and (max-width: 560px) {
    .sociallocker-overlay {
        font-size: 15px;
    }
}

/* Высота кнопок AddToAny */
.a2a_kit a {
    min-height: 45px !important;
}

div.sociallocker-content a {
    color: white !important;
    border: white 2px solid;
    background-color: #777777;
}

div.sociallocker-content a:hover {
    color: #002467 !important;
    border: #002467 2px solid;
    background-color: #ffffff;
}

Содержимое файла script.js:

// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// ИПОЛЬЗОВАНИЕ :
// 
// Помещаем контент для скрытия в контейнер:
// <div class = "sociallocker" >
//     <a href = "/file.zip" > 
//         Загрузить файл
//     </a> 
// </div>
// Данный скрипт автоматически обернет каждый блок ссылки в свою обвертку. 
// И не даст загрузить файл пока пользователь не зашарит Вашу страницу
// 
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 

// =======================================================
// =======================================================
// AddToAny START
// =======================================================
// =======================================================

// AddToAny Подключаем скрипт после загрузки страницы
var s = document.createElement("script");
s.type = "text/javascript";
s.src = "//static.addtoany.com/menu/page.js";
// Use any selector
$("header").append(s);

var a2a_config = a2a_config || {};

// $('div#a2apage_overlay')

a2a_config.thanks = {
    postShare: true,
    ad: false,
    // Указываем свой GoogleAdsence если хотим что бы отображался блок рекламы в окне
    // ad: '<ins class=\"adsbygoogle\"\
    //         style=\"display:inline-block;width:300px;height:250px\"\
    //         data-ad-client=\"ca-pub-xxxxxxxxxxxxxxxx\"\
    //         data-ad-slot=\"1234567890\"></ins>\
    //     <\script>\
    //     (adsbygoogle = window.adsbygoogle || []).push({});\
    //     <\/script>'
};

//AddToAny - Меняем локализацию на свой язык
//https://www.addtoany.com/buttons/customize/translation_localization
// var a2a_config = a2a_config || {};
a2a_config.locale = "ru-RU";

// =======================================================
// =======================================================
// AddToAny END
// =======================================================
// =======================================================

jQuery(document).ready(function ($) {

    // Оборачиваем каждую кнопку SocialLock
    // START
    // --------------------------------------------------------------

    // Оборачиваем весь контент в нутри div#sociallocker в блок для скрытия в оверлее
    $('div.sociallocker').wrapInner("<div class='sociallocker-content'></div>");

    // ВСТАВКА КОДА С КНОПКАМИ И ОВЕРЛЕЕМ
    // ====================================
    // Вставляем блок с кнопками от AddToAny перед обернутым содержимым
    $("<div class=\"sociallocker-links\"> <!-- AddToAny BEGIN --> <div class=\"a2a_kit a2a_kit_size_32 a2a_default_style\"> <a class=\"a2a_dd\" href=\"https://www.addtoany.com/share\"> </a> <a class=\"a2a_button_facebook\"></a> <a class=\"a2a_button_twitter\"></a> <a class=\"a2a_button_google_plus\"></a> <a class=\"a2a_button_pinterest\"></a> <a class=\"a2a_button_linkedin\"></a> <a class=\"a2a_button_trello\"></a> </div> </div>").insertBefore(".sociallocker-content");
    // Вставляем блок с оверлеем
    $("<div class=\"sociallocker-overlay\"><i class=\"fa fa-lock\"></i>Загрузить файл добавив страницу в свою социальную сеть<i class=\"fa fa-share-alt\"></i></div > ").insertAfter('.sociallocker-content');

    // --------------------------------------------------------------
    // Оборачиваем каждую кнопку SocialLock
    // END

    // =======================================================================================================
    // Едреный скрипт благодаря которому появляется возможность отследить изменения style свойств объекта
    // START
    // -------------------------------------------------------------------------------------------------------
    $(window).bind("load", function () {

        // https://github.com/RickStrahl/jquery-watch

        /*
        jquery-watcher 
        Version 1.21 - 1/19/2016
        © 2014-2016 Rick Strahl, West Wind Technologies 
        www.west-wind.com
        Licensed under MIT License
        http://en.wikipedia.org/wiki/MIT_License
        */
        (function ($, undefined) {
            $.fn.watch = function (options) {
                /// <summary>
                /// Allows you to monitor changes in a specific
                /// CSS property of an element by polling the value.
                /// You can also monitor attributes (using attr_ prefix)
                /// or property changes (using prop_ prefix).
                /// when the value changes a function is called.
                /// The callback is fired in the context
                /// of the selected element (ie. this)
                ///
                /// Uses the MutationObserver API of the DOM and
                /// falls back to setInterval to poll for changes
                /// for non-compliant browsers (pre IE 11)
                /// </summary>            
                /// <param name="options" type="Object">
                /// Option to set - see comments in code below.
                /// </param>        
                /// <returns type="jQuery" /> 
                var opt = $.extend({
                    // CSS styles or Attributes to monitor as comma delimited list
                    // For attributes use a attr_ prefix
                    // Example: "top,left,opacity,attr_class"
                    properties: null,

                    // interval for 'manual polling' (IE 10 and older)            
                    interval: 100,

                    // a unique id for this watcher instance
                    id: "_watcher_" + new Date().getTime(),

                    // flag to determine whether child elements are watched            
                    watchChildren: false,

                    // Callback function if not passed in callback parameter   
                    callback: null
                }, options);

                return this.each(function () {
                    var el = this;
                    var el$ = $(this);
                    var fnc = function (mRec, mObs) {
                        __watcher.call(el, opt.id, mRec, mObs);
                    };

                    var data = {
                        id: opt.id,
                        props: opt.properties.split(','),
                        vals: [opt.properties.split(',').length],
                        func: opt.callback, // user function
                        fnc: fnc, // __watcher internal
                        origProps: opt.properties,
                        interval: opt.interval,
                        intervalId: null
                    };
                    // store initial props and values
                    $.each(data.props, function (i) {
                        var propName = data.props[i];
                        if (data.props[i].startsWith('attr_'))
                            data.vals[i] = el$.attr(propName.replace('attr_', ''));
                        else if (propName.startsWith('prop_'))
                            data.vals[i] = el$.prop(propName.replace('props_', ''));
                        else
                            data.vals[i] = el$.css(propName);
                    });

                    el$.data(opt.id, data);

                    hookChange(el$, opt.id, data);
                });

                function hookChange(element$, id, data) {
                    element$.each(function () {
                        var el$ = $(this);

                        if (window.MutationObserver) {
                            var observer = el$.data('__watcherObserver' + opt.id);
                            if (observer == null) {
                                observer = new MutationObserver(data.fnc);
                                el$.data('__watcherObserver' + opt.id, observer);
                            }
                            observer.observe(this, {
                                attributes: true,
                                subtree: opt.watchChildren,
                                childList: opt.watchChildren,
                                characterData: true
                            });
                        } else
                            data.intervalId = setInterval(data.fnc, opt.interval);
                    });
                }

                function __watcher(id, mRec, mObs) {
                    var el$ = $(this);
                    var w = el$.data(id);
                    if (!w) return;
                    var el = this;

                    if (!w.func)
                        return;

                    var changed = false;
                    var i = 0;
                    for (i; i < w.props.length; i++) {
                        var key = w.props[i];

                        var newVal = "";
                        if (key.startsWith('attr_'))
                            newVal = el$.attr(key.replace('attr_', ''));
                        else if (key.startsWith('prop_'))
                            newVal = el$.prop(key.replace('prop_', ''));
                        else
                            newVal = el$.css(key);

                        if (newVal == undefined)
                            continue;

                        if (w.vals[i] != newVal) {
                            w.vals[i] = newVal;
                            changed = true;
                            break;
                        }
                    }
                    if (changed) {
                        // unbind to avoid recursive events
                        el$.unwatch(id);

                        // call the user handler
                        w.func.call(el, w, i, mRec, mObs);

                        // rebind the events
                        hookChange(el$, id, w);
                    }
                }
            }
            $.fn.unwatch = function (id) {
                this.each(function () {
                    var el = $(this);
                    var data = el.data(id);
                    try {
                        if (window.MutationObserver) {
                            var observer = el.data("__watcherObserver" + id);
                            if (observer) {
                                observer.disconnect();
                                el.removeData("__watcherObserver" + id);
                            }
                        } else
                            clearInterval(data.intervalId);
                    }
                    // ignore if element was already unbound
                    catch (e) {}
                });
                return this;
            }
            String.prototype.startsWith = function (sub) {
                if (sub === null || sub === undefined) return false;
                return sub == this.substr(0, sub.length);
            }
        })(jQuery, undefined);

        // -0-------------------------
        // some element to monitor
        var el = $("#a2apage_overlay");

        // hook up the watcher
        el.watch({
            // specify CSS styles or attribute names to monitor
            properties: "display,top,left,opacity,attr_class,prop_innerHTML",

            // callback function when a change is detected
            callback: function (data, i) {

                // console.log('style changed!');

                // Событие при изменении свойств стилией бекграунда
                // START
                // -------------------------------------------------------------
                // if ($('div#a2apage_modal').attr('aria-label') == "Спасибо, что поделился") {
                if ($("div#a2apage_modal").css("display") == "block") {
                    // console.log('Внимание!!!! Изменение атрибутов начало происходить!!!!');
                    setTimeout(function () {
                        //alert('Появился');
                        // 
                        $("div#a2apage_overlay").css("display", "none");
                        $("div#a2apage_modal").css("display", "none");

                        $(".sociallocker-links").each(function () {
                            $(this).css('display', 'none');
                        });
                        $(".sociallocker-overlay").each(function () {
                            $(this).css('display', 'none');
                        });
                        $(".sociallocker-content").each(function () {
                            $(this).css('top', '0');
                        });
                    }, 2500);
                };
                // -------------------------------------------------------------
                // END

                var propChanged = data.props[i];
                var newValue = data.vals[i];

                var el = this;
                var el$ = $(this);

                // do what you need based on changes
                // or do your own checks 
            }
        });
    });
    // -------------------------------------------------------------------------------------------------------
    // Едреный скрипт благодаря которому появляется возможность отследить изменения style свойств объекта
    // END
});

Если же у Вас несколько ссылок придется каждую из них помещать в блок:

<div class = "sociallocker" > ссылка </a>

На данный момент я не стал заморачиваться с возможностью вставки нескольких ссылок в один блок (но если будет слишком много желающих в комментариях, уверен я на это выкрою время) так как в очень редких случаях использую несколько ссылок в одном блоке.

Минусы моего Smart Locker

При клике на кнопку локера на мобильных устройствах:

image alt text

Панель с социальными кнопками не успевает отображаться и сразу происходит переход на iframe социальной сети + срабатывает отображение попап окна и автоматическое открытие ссылок. В ближайшем будущем я постараюсь данный баг исправить. А пока предлагаю просто не подключать социал локер для мобильных устройств. Файлы с мобильного скачивают редко.

Для того что бы подключить социал локер только для ПК воспользуйтесь решением ниже (подключаем скрипт локера только для устройств ширина экрана которых больше 768px):

<script>
jQuery(document).ready(function ($) {
if ( $(window).width() >= 768) {
var s = document.createElement("script");
s.type = "text/javascript";
s.src = "/SDStudio_SocialLocker_Plus_AddToAny/script.js";
// Use any selector
$("header").append(s);
};
});
</script>

Полезности

jquery-watch

Для реализации задуманного мне пришлось весьма потрудиться для правильного запуска скрипта разблокировки контента после того как пользователь "зашарил страницу". Все дело в том что необходимо отслеживать отобразился ли попап после шаринга. Для этого мне пришел на помощь данный скрипт, а точнее библиотека:

https://github.com/RickStrahl/jquery-watch

Пример кода для тестирования плагина. В данном фрагменте кода мы следим за изменением стилей и свойств блока с классами '.modal-wrapper.styled':

jQuery(document).ready(function($){

    // watch the content
    $(".modal-wrapper.styled").watch({
        properties: "top,left,opacity,display,attr_class",
        watchChildren: true,
        callback: function (data, i) {
            console.log("text changed");
        }
    });

});

Без данного плагина мне бы не удалось отследить атрибут style - display у элемента страницы $("#a2apage_overlay"). От себя отмечу что это единственное решение которое работает как нужно и практически во всех браузерах.

AddToAny

Если У Вас появится желание сделать свой вариант возможно Вам понадобится документация. В данном случае основной акцент будет на скрипт для социальной шары от AddToAny.

По ссылке ниже Вы можете узнать подробнее о функциях и полезных советах при работе с кнопками социальных сетей от сервиса.

https://www.addtoany.com/buttons/customize/