Result
表題の通りで、任意の記事をユーザーにMastodonで投稿して貰う為の共有ボタンを設置する、というもの。
当然問題となるのがユーザーによるインスタンスの違いですが、この方法ではインスタンスをユーザーに入力して貰う形となっています。
JavaScript
// DOMからリンクを取得 const button = document.querySelector( '.mastodon-share' ); // ユーザーがリンクをクリックすると button.addEventListener( 'click' , (e) => { // ユーザーがすでにインスタンスに入っており且つ、その情報がlocalStorageにある場合 // インスタンスと現在のページのタイトルとURLを含むリンク href を書き出す if (localStorage.getItem( 'mastodon-instance' )) { button.href = ` https: //${localStorage.getItem('mastodon-instance')}/ share?text=${encodeURIComponent(document.title)} %0A${encodeURIComponent(location.href)} `; // それ以外の場合はユーザーにインスタンスの入力をして貰いlocalStorageに保存 } else { e.preventDefault(); let instance = window.prompt( 'Please tell me your Mastodon instance' ); localStorage.setItem( 'mastodon-instance' , instance); } }); |
これはクリックでインスタンスの指示を促す方法。おそらくもっともシンプルな方法です。
ただ、シェアボタンの挙動としてはちょっとアレなので他SNSと概ね似たようなボタンを今回は作る、という話です。
ボタン化には以下のようなコードが使われています。
Array.prototype.forEach || (Array.prototype.forEach = function (r) { var o, t; if ( null == this ) throw new TypeError( "this is null or not defined" ); var n = Object( this ), e = n.length >>> 0; if ( "function" != typeof r) throw new TypeError(r + " is not a function" ); for (arguments.length > 1 && (o = arguments[1]), t = 0; t < e; ) { var i; t in n && ((i = n[t]), r.call(o, i, t, n)), t++; } }); document.addEventListener( "DOMContentLoaded" , function () { document.querySelectorAll( ".mast-share" ).forEach( function (e, t) { (e.querySelector( ".mast-check-toggle" ).id = "mast-check-toggle-" + t), (e.querySelector( ".mast-check-label" ).htmlFor = "mast-check-toggle-" + t), e .querySelector( ".mast-share-button" ) .addEventListener( "click" , function (t) { var a = new RegExp( "^(?:(?:https?|ftp)://)?(?:\\S+(?::\\S*)?@|\\d{1,3}(?:\\.\\d{1,3}){3}|(?:(?:[a-z\\d\\u00a1-\\uffff]+-?)*[a-z\\d\\u00a1-\\uffff]+)(?:\\.(?:[a-z\\d\\u00a1-\\uffff]+-?)*[a-z\\d\\u00a1-\\uffff]+)*(?:\\.[a-z\\u00a1-\\uffff]{2,6}))(?::\\d+)?(?:[^\\s]*)?$" , "i" ), n = e.querySelector( 'input[name="mast-instance-input"]' ); if (a.test(n.value)) { var o = `http: //${n.value.replace( /(^\w+:|^)\/\ //, "" )}/share?text=${encodeURIComponent(document.title)} ${encodeURIComponent( location.href )}`; window.open( o, "new" , "toolbar=no,location=no,status=yes,resizable=yes,scrollbars=yes,height=600,width=400" ); } else n.classList.add( "invalid" ), setTimeout( function () { n.classList.remove( "invalid" ); }, 300); }), e.addEventListener( "mouseleave" , function (t) { e.querySelector( ".mast-check-toggle" ).checked = !1; }); }); }); |
css
.mast-check-toggle { height : 0 ; width : 0 ; padding : 0 ; margin : 0 ; visibility : hidden ; display : none ; } .mast-check-toggle:checked + .mast-instance { display : block !important ; } .mast-check-toggle:not(:checked) + .mast-instance { display : none ; } .mast-share.active { padding-bottom : 10px ; } .mast-share-lg .mast- top { cursor : pointer ; position : absolute ; width : 142px ; top : 0 ; left : 0 ; padding : 4px 8px ; z-index : 1 ; } .mast-share-md .mast- top , .mast-share-sm .mast- top { cursor : pointer ; position : absolute ; width : 18px ; height : 15px ; top : 0 ; left : 0 ; padding : 4px 8px ; z-index : 1 ; } .mast-share-lg, .mast-share-md, .mast-share-sm { color : #fff ; display : inline-block ; height : 23px ; background-color : #292d37 ; border-radius : 3px ; position : relative ; } .mast-share-lg { width : 158px ; } .mast-share-md { width : 92px ; } .mast-share-sm { width : 34px ; } .mast-share-md .mast-instance, .mast-share-sm .mast-instance { border-radius : 0 3px 3px ; z-index : 0 ; } .mast-instance { background-color : #292d37 ; padding : 8px ; position : absolute ; top : 20px ; left : 0 ; margin : 0 auto ; border-radius : 0 0 3px 3px ; } .mast-share span { vertical-align : top ; font-family : sans-serif ; font-weight : 700 ; font-size : 14px ; } .mast-share img { max-height : 19px ; margin-top : 1px ; } .mast-instance input[name= "mast-instance-input" ] { background-color : #000000 00; border : none ; color : #fff ; border-bottom : 2px solid #3087d5 ; font-size : 14px ; font-weight : 700 ; max-width : 130px ; } .mast-instance input[name= "mast-instance-input" ][class= "invalid" ] { border-bottom : 2px solid red ; animation-name : shake; animation-duration : 0.1 s; animation-timing-function : ease-in-out; animation-iteration-count : infinite ; } @-webkit-keyframes shake { 41% , 8% { -webkit- transform : translateX ( -10px ); } 25% , 58% { -webkit- transform : translateX ( 10px ); } 75% { -webkit- transform : translateX ( -5px ); } 92% { -webkit- transform : translateX ( 5px ); } 0% , 100% { -webkit- transform : translateX ( 0 ); } } .mast-share input::placeholder { color : #eee ; } .mast-share input:focus { outline : 0 ; } .mast-share button { background-color : #000000 00; color : #fff ; border : none ; font-size : 14px ; font-weight : 700 ; padding : 0 ; margin : 8px 0 0 ; cursor : pointer ; } .mast-share button:hover { color : #3087d5 ; } |
こちらはボタンやインスタンス入力フォームなどのスタイルとなっています。
ですので、ボタンのスタイルを変更したい場合はこのCSSを編集します。
html
< div class = "mast-share mast-share-sm" > < input type = "checkbox" class = "mast-check-toggle" > < div class = "mast-instance" >< span >インスタンスを入力: </ span >< input type = "textbox" name = "mast-instance-input" placeholder = "mstdn.jp" >< button class = "mast-share-button" >シェア</ button ></ div > < label class = "mast-top mast-check-label" > < img src = "data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2261.076954mm%22%20height%3D%2265.47831mm%22%20viewBox%3D%220%200%20216.4144%20232.00976%22%3E%3Cpath%20d%3D%22M211.80734%20139.0875c-3.18125%2016.36625-28.4925%2034.2775-57.5625%2037.74875-15.15875%201.80875-30.08375%203.47125-45.99875%202.74125-26.0275-1.1925-46.565-6.2125-46.565-6.2125%200%202.53375.15625%204.94625.46875%207.2025%203.38375%2025.68625%2025.47%2027.225%2046.39125%2027.9425%2021.11625.7225%2039.91875-5.20625%2039.91875-5.20625l.8675%2019.09s-14.77%207.93125-41.08125%209.39c-14.50875.7975-32.52375-.365-53.50625-5.91875C9.23234%20213.82%201.40609%20165.31125.20859%20116.09125c-.365-14.61375-.14-28.39375-.14-39.91875%200-50.33%2032.97625-65.0825%2032.97625-65.0825C49.67234%203.45375%2078.20359.2425%20107.86484%200h.72875c29.66125.2425%2058.21125%203.45375%2074.8375%2011.09%200%200%2032.975%2014.7525%2032.975%2065.0825%200%200%20.41375%2037.13375-4.59875%2062.915%22%20fill%3D%22%233088d4%22%2F%3E%3Cpath%20d%3D%22M177.50984%2080.077v60.94125h-24.14375v-59.15c0-12.46875-5.24625-18.7975-15.74-18.7975-11.6025%200-17.4175%207.5075-17.4175%2022.3525v32.37625H96.20734V85.42325c0-14.845-5.81625-22.3525-17.41875-22.3525-10.49375%200-15.74%206.32875-15.74%2018.7975v59.15H38.90484V80.077c0-12.455%203.17125-22.3525%209.54125-29.675%206.56875-7.3225%2015.17125-11.07625%2025.85-11.07625%2012.355%200%2021.71125%204.74875%2027.8975%2014.2475l6.01375%2010.08125%206.015-10.08125c6.185-9.49875%2015.54125-14.2475%2027.8975-14.2475%2010.6775%200%2019.28%203.75375%2025.85%2011.07625%206.36875%207.3225%209.54%2017.22%209.54%2029.675%22%20fill%3D%22%23fff%22%2F%3E%3C%2Fsvg%3E%0A" > </ label > </ div > < script >Array.prototype.forEach||(Array.prototype.forEach=function(r){var o,t;if(null==this)throw new TypeError("this is null or not defined");var n=Object(this),e=n.length>>>0;if("function"!=typeof r)throw new TypeError(r+" is not a function");for(arguments.length>1&&(o=arguments[1]),t=0;t< e ;){var i;t in n&&( i = n [t],r.call(o,i,t,n)),t++}});document.addEventListener("DOMContentLoaded",function(){document.querySelectorAll(".mast-share").forEach(function(e,t){e.querySelector(".mast-check-toggle") .id = "mast-check-toggle-" +t,e.querySelector(".mast-check-label") .htmlFor = "mast-check-toggle-" +t,e.querySelector(".mast-share-button").addEventListener("click",function(t){var a = new RegExp("^(?:(?:https?|ftp)://)?(?:\\S+(?::\\S*)?@|\\d{1,3}(?:\\.\\d{1,3}){3}|(?:(?:[a-z\\d\\u00a1-\\uffff]+-?)*[a-z\\d\\u00a1-\\uffff]+)(?:\\.(?:[a-z\\d\\u00a1-\\uffff]+-?)*[a-z\\d\\u00a1-\\uffff]+)*(?:\\.[a-z\\u00a1-\\uffff]{2,6}))(?::\\d+)?(?:[^\\s]*)?$","i"), n = e .querySelector('input[ name = "mast-instance-input" ]');if(a.test(n.value)){var o=`http://${n.value.replace(/(^\w+:|^)\/\//,"")}/share?text=${encodeURIComponent(document.title)} ${encodeURIComponent(location.href)}`;window.open(o,"new"," toolbar = no , location = no , status = yes , resizable = yes , scrollbars = yes , height = 600 , width = 400 ")}else n.classList.add("invalid"),setTimeout(function(){n.classList.remove("invalid")},300)}),e.addEventListener("mouseleave",function(t){e.querySelector(".mast-check-toggle").checked=!1})})});</script> < style >.mast-check-toggle{height:0;width:0;padding:0;margin:0;visibility:hidden;display:none}.mast-check-toggle:checked+.mast-instance{display:block!important}.mast-check-toggle:not(:checked)+.mast-instance{display:none}.mast-share.active{padding-bottom:10px}.mast-share-lg .mast-top{cursor:pointer;position:absolute;width:142px;top:0;left:0;padding:4px 8px;z-index:1}.mast-share-md .mast-top,.mast-share-sm .mast-top{cursor:pointer;position:absolute;width:18px;height:15px;top:0;left:0;padding:4px 8px;z-index:1}.mast-share-lg,.mast-share-md,.mast-share-sm{color:#fff;display:inline-block;height:23px;background-color:#292D37;border-radius:3px;position:relative}.mast-share-lg{width:158px}.mast-share-md{width:92px}.mast-share-sm{width:34px}.mast-share-md .mast-instance,.mast-share-sm .mast-instance{border-radius:0 3px 3px;z-index:0}.mast-instance{background-color:#292D37;padding:8px;position:absolute;top:20px;left:0;margin:0 auto;border-radius:0 0 3px 3px}.mast-share span{vertical-align:top;font-family:sans-serif;font-weight:700;font-size:14px}.mast-share img{max-height:19px;margin-top:1px}.mast-instance input[name="mast-instance-input"]{background-color:#00000000;border:none;color:#fff;border-bottom:2px solid #3087D5;font-size:14px;font-weight:700;max-width:130px}.mast-instance input[name="mast-instance-input"][class="invalid"]{border-bottom:2px solid red;animation-name:shake;animation-duration:.1s;animation-timing-function:ease-in-out;animation-iteration-count:infinite}@-webkit-keyframes shake{41%,8%{-webkit-transform:translateX(-10px)}25%,58%{-webkit-transform:translateX(10px)}75%{-webkit-transform:translateX(-5px)}92%{-webkit-transform:translateX(5px)}0%,100%{-webkit-transform:translateX(0)}}.mast-share input::placeholder{color:#eee;}.mast-share input:focus{outline:0}.mast-share button{background-color:#00000000;color:#fff;border:none;font-size:14px;font-weight:700;padding:0;margin:8px 0 0;cursor:pointer}.mast-share button:hover{color:#3087d5}</ style > |
HTMLの他に先ほどのJavaScript、CSSを含めたコードです。
このコードのコピーで、「Mastodonで記事をシェア」ボタンを設置する事が出来ます。
JavaScript及びCSSはCDNにもあるみたいなので以下のようにコードを短くする事も可能です。
< div class = "mast-share mast-share-sm" > < input type = "checkbox" class = "mast-check-toggle" > < div class = "mast-instance" >< span >インスタンスを入力: </ span >< input type = "textbox" name = "mast-instance-input" placeholder = "mstdn.jp" >< button class = "mast-share-button" >シェア</ button ></ div > < label class = "mast-top mast-check-label" > < img src = "logotype-short.svg" > </ label > </ div > < script src = "https://cdn.rawgit.com/grayleonard/mastodon-share/35d4e3ab/mast-share.min.js" ></ script > < link rel = "stylesheet" href = "https://cdn.rawgit.com/grayleonard/mastodon-share/35d4e3ab/mast-share.min.css" > |
via
以下で詳しく解説されています。
Adding a “share to mastodon” link to any web site – and here
またコードはGithubで公開されています。